@fangorn-network/westmarch 0.0.14-dev → 0.0.16-dev

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 (137) hide show
  1. package/package.json +1 -1
  2. package/site/agent.js +7 -1
  3. package/site/dist/assets/{AddFundsSelectionScreen-RAoCn7ej-D_EsIeJu.js → AddFundsSelectionScreen-RAoCn7ej-B6uvYyqa.js} +1 -1
  4. package/site/dist/assets/{Address-H8kD1d9O-C85sJ79B.js → Address-H8kD1d9O-DcHv7A8C.js} +1 -1
  5. package/site/dist/assets/{AffirmativeConsentScreen-BG0MJxku-DdEPFOww.js → AffirmativeConsentScreen-BG0MJxku-v2wz-N-s.js} +1 -1
  6. package/site/dist/assets/{ApiController-CYdiyu9B.js → ApiController-BbhXeqTV.js} +3 -3
  7. package/site/dist/assets/{AuthenticateWithWalletScreen-CU6hIcFT-8rbYiYYE.js → AuthenticateWithWalletScreen-CU6hIcFT-B0eT1zE6.js} +1 -1
  8. package/site/dist/assets/{AwaitingEvmToSolBridgingScreen-D0e_sZMS-2pkNfCno.js → AwaitingEvmToSolBridgingScreen-D0e_sZMS-DL5PH-v_.js} +1 -1
  9. package/site/dist/assets/{AwaitingExternalEthereumTransferScreen-ur5HL4R7-7AQt25_k.js → AwaitingExternalEthereumTransferScreen-ur5HL4R7-n1BsRPqc.js} +1 -1
  10. package/site/dist/assets/{AwaitingPasswordlessCodeScreen-DRHz76mM-BZY30TGs.js → AwaitingPasswordlessCodeScreen-DRHz76mM-BVl2BJDY.js} +1 -1
  11. package/site/dist/assets/{AwaitingSolToEvmBridgingScreen-CdLRrGD2-BILOOLi_.js → AwaitingSolToEvmBridgingScreen-CdLRrGD2-BD-OZUQb.js} +1 -1
  12. package/site/dist/assets/{BridgeNetworkSelectionView-CFGjB2nO-CvsbQcFv.js → BridgeNetworkSelectionView-CFGjB2nO-CaYrzRje.js} +1 -1
  13. package/site/dist/assets/{CaptchaScreen-DbpvkIGx-D8eDcAkc.js → CaptchaScreen-DbpvkIGx-BQJ7inx1.js} +1 -1
  14. package/site/dist/assets/{CoinbaseOnrampStatusScreen-BbyCJDXL-DLs9zX-Q.js → CoinbaseOnrampStatusScreen-BbyCJDXL-DDaMC7cl.js} +1 -1
  15. package/site/dist/assets/{ConnectEmailForm-B-VRE0MN-N9J6T_YL.js → ConnectEmailForm-B-VRE0MN-DVSf4tFJ.js} +1 -1
  16. package/site/dist/assets/{ConnectOnlyLandingScreen-CZYx9Bmp-LeuDMN1E.js → ConnectOnlyLandingScreen-CZYx9Bmp-DP19hJq5.js} +1 -1
  17. package/site/dist/assets/{ConnectOnlyStatusScreen-DCRzjxHx-DaRa59tZ.js → ConnectOnlyStatusScreen-DCRzjxHx-DHvijlTx.js} +1 -1
  18. package/site/dist/assets/{ConnectOrCreateScreen-CUUYUO07-crHzh-TL.js → ConnectOrCreateScreen-CUUYUO07-ewmXYPTQ.js} +1 -1
  19. package/site/dist/assets/{ConnectPhoneForm-Dek5xn4C-BEA4FmhI.js → ConnectPhoneForm-Dek5xn4C-DUYdmVtr.js} +1 -1
  20. package/site/dist/assets/{ConnectWalletView-DqI-PTvX-CMQIad63.js → ConnectWalletView-DqI-PTvX-RobGErVW.js} +1 -1
  21. package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-Bbiq3hY6.js +1 -0
  22. package/site/dist/assets/{ConnectionStatusScreen-fUfROm9m-BeWLchcf.js → ConnectionStatusScreen-fUfROm9m-DPkOGNWl.js} +1 -1
  23. package/site/dist/assets/{ConnectorUtil-Cq4lWnfe.js → ConnectorUtil-BO1L6VvV.js} +1 -1
  24. package/site/dist/assets/{CrossAppAuthScreen-CLw7ymZq-Csju14vF.js → CrossAppAuthScreen-CLw7ymZq-DbGy4QyA.js} +1 -1
  25. package/site/dist/assets/{CustomLandingScreenView-Cm_MBSyX-CBOrbNLo.js → CustomLandingScreenView-Cm_MBSyX-B3WqJZBh.js} +1 -1
  26. package/site/dist/assets/{DelegatedActionsConsentScreen-DYF2U4y5-BL5hzNUI.js → DelegatedActionsConsentScreen-DYF2U4y5-CmUDoS0C.js} +1 -1
  27. package/site/dist/assets/{DepositAddressAddressStepView-4vdkb55L-B3vY888G.js → DepositAddressAddressStepView-4vdkb55L-ClAb_j1B.js} +1 -1
  28. package/site/dist/assets/{DepositAddressScreen-DBLRs7aI-DmdQaFI2.js → DepositAddressScreen-DBLRs7aI-CN1P_KY9.js} +1 -1
  29. package/site/dist/assets/{DepositAddressV3Screen-CDqIEMQw-BoupRyfV.js → DepositAddressV3Screen-CDqIEMQw-BtDZmuEN.js} +1 -1
  30. package/site/dist/assets/{EmbeddedWalletConnectingScreen-D4T7DUzs-D_AVgO4s.js → EmbeddedWalletConnectingScreen-D4T7DUzs-D3pExuaW.js} +1 -1
  31. package/site/dist/assets/{EmbeddedWalletKeyExportScreen-CqV4NYQ3-C9sAExzE.js → EmbeddedWalletKeyExportScreen-CqV4NYQ3-DI6bu-oi.js} +1 -1
  32. package/site/dist/assets/{EmbeddedWalletOnAccountCreateScreen-BoKb3Q2S-DxaWAAKa.js → EmbeddedWalletOnAccountCreateScreen-BoKb3Q2S-BQ9Ygr0P.js} +1 -1
  33. package/site/dist/assets/{EnrollTotp-Dibew4dp-SA2pvNEc.js → EnrollTotp-Dibew4dp-7gVtJ1Jt.js} +1 -1
  34. package/site/dist/assets/{ErrorScreen-DipdTWzE-c8g8En21.js → ErrorScreen-DipdTWzE-BT8TtRTx.js} +1 -1
  35. package/site/dist/assets/ErrorScreen-DipdTWzE-DAam41Xl.js +1 -0
  36. package/site/dist/assets/{FarcasterConnectStatusScreen-51tjUxqq-s_s5M2CZ.js → FarcasterConnectStatusScreen-51tjUxqq-BPCBi37Q.js} +1 -1
  37. package/site/dist/assets/{FarcasterSignerStatusScreen-6x-Uh95G-BHBR4k8H.js → FarcasterSignerStatusScreen-6x-Uh95G-XCmWpnUd.js} +1 -1
  38. package/site/dist/assets/{FiatOnrampScreen-BAhYeiTI-BH_sU6EV.js → FiatOnrampScreen-BAhYeiTI-DJRcUh2N.js} +1 -1
  39. package/site/dist/assets/{FundSolWalletWithExternalSolanaWallet-WtLwYaOl-B8TRV36j.js → FundSolWalletWithExternalSolanaWallet-WtLwYaOl-BfNVSTb1.js} +1 -1
  40. package/site/dist/assets/{FundWithBankDepositScreen-DajkHxBD-9rPtyA3d.js → FundWithBankDepositScreen-DajkHxBD-BfvMcHb6.js} +1 -1
  41. package/site/dist/assets/{FundingEditAmountScreen-CpvpiO4M-GaXigGI9.js → FundingEditAmountScreen-CpvpiO4M-VBfbahnA.js} +1 -1
  42. package/site/dist/assets/{FundingMethodSelectionScreen-Db7vhRlE-UsmtTqv4.js → FundingMethodSelectionScreen-Db7vhRlE-CMuBYf64.js} +1 -1
  43. package/site/dist/assets/{LandingScreen-CGWUoSUG-DK1EY-6v.js → LandingScreen-CGWUoSUG-Q7hgXKvM.js} +1 -1
  44. package/site/dist/assets/{LinkConflictScreen-C3ZEPsgl-ClWoqvEL.js → LinkConflictScreen-C3ZEPsgl-DK1z_v7y.js} +1 -1
  45. package/site/dist/assets/{LinkEmailScreen-dKo9frwc-C3v7glH0.js → LinkEmailScreen-dKo9frwc-BYYpJ5-6.js} +1 -1
  46. package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-uhub9C26.js +1 -0
  47. package/site/dist/assets/{LinkPasskeyScreen-zbrl_AI_-D72Gb0LW.js → LinkPasskeyScreen-zbrl_AI_-uid22VTN.js} +1 -1
  48. package/site/dist/assets/{LinkPhoneScreen-oIUpfj0w-C0DuD14j.js → LinkPhoneScreen-oIUpfj0w-CVQibNi9.js} +1 -1
  49. package/site/dist/assets/{ManualTransferScreen-CHqcuqbs-tXiZDHmN.js → ManualTransferScreen-CHqcuqbs-DMygAXMU.js} +1 -1
  50. package/site/dist/assets/{MfaAuthEnrollmentFlowScreen-Dj7gPTRC-CaW7xeCC.js → MfaAuthEnrollmentFlowScreen-Dj7gPTRC-_1GFUSIg.js} +1 -1
  51. package/site/dist/assets/{MfaAuthVerifyFlowScreen-DbFpyWMy-CeRqvx8k.js → MfaAuthVerifyFlowScreen-DbFpyWMy-DLnBJ5b8.js} +1 -1
  52. package/site/dist/assets/{MfaEnrollmentFlowScreen-DhLnGgnr-BkN1k4lh.js → MfaEnrollmentFlowScreen-DhLnGgnr-BUJY1lzs.js} +1 -1
  53. package/site/dist/assets/{MfaVerifyFlowScreen-C_XPjMRP-CXSqLVbE.js → MfaVerifyFlowScreen-C_XPjMRP-25l5fVun.js} +1 -1
  54. package/site/dist/assets/{OAuthStatusScreen-ufbOa-da-CeiGcSia.js → OAuthStatusScreen-ufbOa-da-DL30vu07.js} +1 -1
  55. package/site/dist/assets/{PasskeySelectSignupOrLogin-xh2ScC_l-CIf5I7wU.js → PasskeySelectSignupOrLogin-xh2ScC_l-DtxhRQyw.js} +1 -1
  56. package/site/dist/assets/{PasskeyStatusScreen-Bla8MaOk-CLoL7QC-.js → PasskeyStatusScreen-Bla8MaOk-CNfu8ujT.js} +1 -1
  57. package/site/dist/assets/{PasswordRecoveryScreen-awGXZkQk-B9F67IYV.js → PasswordRecoveryScreen-awGXZkQk-BR6z6xE8.js} +1 -1
  58. package/site/dist/assets/{QrCode-DbnZwlkH-h6Bhoe5U.js → QrCode-DbnZwlkH-BymHrKY7.js} +1 -1
  59. package/site/dist/assets/{RecoveryOAuthStatusScreen-CsgtTI2x-D-A5P3PX.js → RecoveryOAuthStatusScreen-CsgtTI2x-Dro1btv5.js} +1 -1
  60. package/site/dist/assets/{RecoveryPasswordCreateScreen-Be-qJ-LU-C8lTSeSr.js → RecoveryPasswordCreateScreen-Be-qJ-LU-C1q1hiaN.js} +1 -1
  61. package/site/dist/assets/{RecoverySelectionScreen-D1iGtpmi-P5P3plkH.js → RecoverySelectionScreen-D1iGtpmi-C21D20gD.js} +1 -1
  62. package/site/dist/assets/{SIWXUtil-TUCxa7aA.js → SIWXUtil-CHZVugix.js} +1 -1
  63. package/site/dist/assets/{SelectSourceAsset-CWFog6gm-D7yTw87h.js → SelectSourceAsset-CWFog6gm-RKjBsB6m.js} +1 -1
  64. package/site/dist/assets/{SetAutomaticRecoveryScreen-BO_OEqAZ-f0iDIGmw.js → SetAutomaticRecoveryScreen-BO_OEqAZ-BcWHJoEG.js} +1 -1
  65. package/site/dist/assets/{SignRequestScreen-BHKv32KR-DDpMYyJ1.js → SignRequestScreen-BHKv32KR-iQCaBkcz.js} +1 -1
  66. package/site/dist/assets/{SolanaWalletConnector-C7Lqk-Wz-DPwhuzcI.js → SolanaWalletConnector-C7Lqk-Wz-BUdJJ0ep.js} +1 -1
  67. package/site/dist/assets/{StandardSignAndSendTransactionScreen-CfhIXVtd-BJ-onCjv.js → StandardSignAndSendTransactionScreen-CfhIXVtd-nART-xUU.js} +1 -1
  68. package/site/dist/assets/{TelegramAuthScreen-UL9zLfO8-NWlMgNRX.js → TelegramAuthScreen-UL9zLfO8-Dw9geyK3.js} +1 -1
  69. package/site/dist/assets/{TransactionDetails-e9nesl30-C4MpIHR-.js → TransactionDetails-e9nesl30-DeEL1JQS.js} +1 -1
  70. package/site/dist/assets/{TransactionErrorView-BDBNqA_f-p1T9WVMx.js → TransactionErrorView-BDBNqA_f-Dffuo7I4.js} +1 -1
  71. package/site/dist/assets/{TransferFromWalletScreen-Qvn1Yuno-88IQcRnY.js → TransferFromWalletScreen-Qvn1Yuno-CWLWUiHy.js} +1 -1
  72. package/site/dist/assets/{TransferOrBridgeLoadingScreen-Dl1I_iIz-1fDAIxAf.js → TransferOrBridgeLoadingScreen-Dl1I_iIz-Bo-tm6wr.js} +1 -1
  73. package/site/dist/assets/{UpdateEmailScreen-DVOhPV0H-Jj2DQW8D.js → UpdateEmailScreen-DVOhPV0H-gFbSIUhh.js} +1 -1
  74. package/site/dist/assets/{UpdatePhoneScreen-CXRswTNZ-DwYIUBqi.js → UpdatePhoneScreen-CXRswTNZ-Coy4E1NQ.js} +1 -1
  75. package/site/dist/assets/{WalletInfoCard-sq_2wgFg-DpNSAGte.js → WalletInfoCard-sq_2wgFg-ISBsQt8p.js} +1 -1
  76. package/site/dist/assets/{WalletLink-DK5LB5Ha-DHaL350n.js → WalletLink-DK5LB5Ha-CXBqCHf3.js} +1 -1
  77. package/site/dist/assets/{WalletOverflowButton-bfCnp-9T-CUyIJFzt.js → WalletOverflowButton-bfCnp-9T-DP0-PoZL.js} +1 -1
  78. package/site/dist/assets/{_esm-B26FZg5Y.js → _esm-CJe2F7LL.js} +1 -1
  79. package/site/dist/assets/{_esm-BdeM6pxJ.js → _esm-zhBBffjV.js} +1 -1
  80. package/site/dist/assets/{accounts-DiZ-0VmN.js → accounts-Bq8t1wAD.js} +1 -1
  81. package/site/dist/assets/{basic-oIwwSyu4.js → basic-MlZ7f4s9.js} +1 -1
  82. package/site/dist/assets/ccip-BI9LWBBg.js +1 -0
  83. package/site/dist/assets/{ccip-BZvRTL6g.js → ccip-C-59SNEF.js} +2 -2
  84. package/site/dist/assets/{ccip-65sIfSPR.js → ccip-y08wK9du.js} +1 -1
  85. package/site/dist/assets/{client-CZG0ndth.js → client-D0IV9lV9.js} +1 -1
  86. package/site/dist/assets/{coinbase-DCPC2L_R-D3aIgpyd.js → coinbase-DCPC2L_R-Rs9nVCfm.js} +1 -1
  87. package/site/dist/assets/{core-snws5xwv.js → core-zQ1tZtVZ.js} +3 -3
  88. package/site/dist/assets/{dist-DnGANz51.js → dist-BxQqwnue.js} +1 -1
  89. package/site/dist/assets/{esm-ByALO6mA.js → esm-DXsETz3H.js} +1 -1
  90. package/site/dist/assets/{esm-B5cbIQaz.js → esm-vs5jNx_Y.js} +2 -2
  91. package/site/dist/assets/{ethers-zTjbodk5-wHhL_BQ1.js → ethers-zTjbodk5-B7BBFFKf.js} +1 -1
  92. package/site/dist/assets/{features-DQ0UYy6K.js → features-CYXRa3_m.js} +1 -1
  93. package/site/dist/assets/{getErc20Balance-jXNpFf0N-Bb9WM31M.js → getErc20Balance-jXNpFf0N-V2btxzrJ.js} +1 -1
  94. package/site/dist/assets/{getErc20TokenInfo-BNTrzLMP-KPrdn04K.js → getErc20TokenInfo-BNTrzLMP-CtCwQuGA.js} +1 -1
  95. package/site/dist/assets/{id-UqEwL2BI-BkKm533U.js → id-UqEwL2BI-CdKXapd-.js} +1 -1
  96. package/site/dist/assets/index-DPuRQJG2.js +8 -0
  97. package/site/dist/assets/{index-DvMddJcd-BatvgC-S.js → index-DvMddJcd-D-4mRLKK.js} +1 -1
  98. package/site/dist/assets/{index-YiCw0Y_x-CX5i3Z87.js → index-YiCw0Y_x-BX1zsiqG.js} +4 -4
  99. package/site/dist/assets/{localBatchGatewayRequest-3kLrX_c7.js → localBatchGatewayRequest-5YxrERBj.js} +2 -2
  100. package/site/dist/assets/parseEther-CGP3gnf2.js +1 -0
  101. package/site/dist/assets/{pkce-DXF0yKjP-ChQhE0st.js → pkce-DXF0yKjP-Bqz4wAsq.js} +1 -1
  102. package/site/dist/assets/preload-helper-BaNbYf_w.js +1 -0
  103. package/site/dist/assets/roles-32M1l9jI.js +1 -0
  104. package/site/dist/assets/roles-DKPW79Ca.js +1 -0
  105. package/site/dist/assets/shouldProceedtoEmbeddedWalletCreationFlow-B3vyavrh-ByFOTrQK.js +1 -0
  106. package/site/dist/assets/{simulateTransaction-CfuOrae4-64lWYJAL.js → simulateTransaction-CfuOrae4-CMFGDgT_.js} +1 -1
  107. package/site/dist/assets/{storage-Ufl800Ne-D0bZBw-H.js → storage-Ufl800Ne-EalZTH9q.js} +1 -1
  108. package/site/dist/assets/{styles-Cm9H63WO-Br4QtzUL.js → styles-Cm9H63WO-D90SsXOs.js} +1 -1
  109. package/site/dist/assets/{to-ui-error-DWe-qwS3-Cx2bk1kZ.js → to-ui-error-DWe-qwS3-DgCO-Y70.js} +1 -1
  110. package/site/dist/assets/{toViemAccount-B5iDGq1b-Ba8L7zed.js → toViemAccount-B5iDGq1b-DZ1MBqUW.js} +2 -2
  111. package/site/dist/assets/{transformers.web-CVP3pJOS.js → transformers.web-D4RvOP4-.js} +1 -1
  112. package/site/dist/assets/{use-headless-crypto-deposit-Yn-QRLB3-LUIVFBSd.js → use-headless-crypto-deposit-Yn-QRLB3-Dn6_8w-Y.js} +1 -1
  113. package/site/dist/assets/{use-unlink-wallet-BKK4_5Ur-CC2pq27z.js → use-unlink-wallet-BKK4_5Ur-DgsQ75lh.js} +1 -1
  114. package/site/dist/assets/{useActiveWallet-B-yZYGc6-D7Ik-LxJ.js → useActiveWallet-B-yZYGc6-B6JWREbk.js} +1 -1
  115. package/site/dist/assets/{useWalletBalance-CZnP5f_f-Dw2GIJ4g.js → useWalletBalance-CZnP5f_f-L3V0jDjL.js} +1 -1
  116. package/site/dist/assets/{useWallets-X1ndvJ_U-DRjz7uYD.js → useWallets-X1ndvJ_U-CLwUcevw.js} +1 -1
  117. package/site/dist/assets/{w3m-modal-BeVrBOIf.js → w3m-modal-BMubXAoG.js} +1 -1
  118. package/site/dist/assets/{wallet-DMLc-ukR.js → wallet-Bmjp2WfA.js} +2 -2
  119. package/site/dist/assets/{walletconnect-v2-3frQFwbg-CrtLXofr.js → walletconnect-v2-3frQFwbg-Dv6JY_ko.js} +3 -3
  120. package/site/dist/assets/{wui-ux-by-reown-D34ILNLS.js → wui-ux-by-reown-fVu0QkI4.js} +1 -1
  121. package/site/dist/index.html +4 -4
  122. package/src/agent/tools.js +69 -2
  123. package/src/agent/view-tools.js +7 -1
  124. package/src/core/roles.js +7 -2
  125. package/src/publish/ship.js +11 -4
  126. package/src/publish/view.js +101 -22
  127. package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-wu3a3kw4.js +0 -1
  128. package/site/dist/assets/ErrorScreen-DipdTWzE-DcxVjMO4.js +0 -1
  129. package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-DYIl5Kr2.js +0 -1
  130. package/site/dist/assets/ccip-DA_zG341.js +0 -1
  131. package/site/dist/assets/index-B5YmgwLH.js +0 -8
  132. package/site/dist/assets/ort-wasm-simd-threaded.asyncify-CxOG5pUO.wasm +0 -0
  133. package/site/dist/assets/parseEther-VjhVo3dd.js +0 -1
  134. package/site/dist/assets/preload-helper-uBIymjUX.js +0 -1
  135. package/site/dist/assets/roles-C9V_uDr_.js +0 -1
  136. package/site/dist/assets/roles-YDRjv_PY.js +0 -1
  137. package/site/dist/assets/shouldProceedtoEmbeddedWalletCreationFlow-B3vyavrh-BK_FBkaE.js +0 -1
@@ -1,5 +1,5 @@
1
1
  const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./PhArrowCircleDown-D97G-BOA.js","./property-DuZ9DLP4.js","./PhArrowClockwise-DVgyFuez.js","./PhArrowDown-87b1BizM.js","./PhArrowLeft-Da0MfnbK.js","./PhArrowRight-AdrIL4DW.js","./PhArrowSquareOut-Bk2CMG3I.js","./PhArrowsDownUp-DM09Jo_3.js","./PhArrowsLeftRight-MuKBLUoF.js","./PhArrowUp-4sgyKPGx.js","./PhArrowUpRight-BNghD2zq.js","./PhArrowsClockwise-BXRn1GEl.js","./PhBank-fHbgCqqZ.js","./PhBrowser-2-9m-AkM.js","./PhCaretDown-BDNabFuL.js","./PhCaretLeft-CI460R-A.js","./PhCaretRight-D7ZS3Ls8.js","./PhCaretUp-C-iIfyvO.js","./PhCheck-DxQXn57r.js","./PhCircleHalf-MJZeRBGF.js","./PhClock-x3MS3Mwi.js","./PhCompass-hYn3HhGN.js","./PhCopy-CWNYcABI.js","./PhCreditCard-Fy4pzwtt.js","./PhCurrencyDollar-BgiaenCi.js","./PhDesktop-CxrzCSv5.js","./PhDeviceMobile-CsyA5Goi.js","./PhDotsThree-D-k1BiYg.js","./PhVault-6JTskUcC.js","./PhEnvelope-D8kzeNox.js","./PhFunnelSimple-CfQs5DAW.js","./PhGlobe-DvmSc9_A.js","./PhIdentificationCard-CfkZm-5K.js","./PhImage-Cf0J2ZfJ.js","./PhInfo-Bwi51-2X.js","./PhLightbulb-C4FZV7Es.js","./PhMagnifyingGlass-C9u8jSCJ.js","./PhPaperPlaneRight-zxQVvPLX.js","./PhPlus-C4UoW-2n.js","./PhPower-CFyWDmgD.js","./PhPuzzlePiece-BjoNVfST.js","./PhQrCode-CP3tFauY.js","./PhQuestion-wOWJBd3G.js","./PhQuestionMark-CV0k9C0Y.js","./PhSealCheck-vIKzREuN.js","./PhSignOut-CxELV-i0.js","./PhSpinner-CGDmbzUd.js","./PhTrash-C9PD0SAt.js","./PhUser-0sWNXQeZ.js","./PhWarning-CoKs3gz6.js","./PhWarningCircle-BXHw1fCt.js","./PhX-DX0SFIqC.js"])))=>i.map(i=>d[i]);
2
- import{t as e}from"./preload-helper-uBIymjUX.js";import{_ as t,c as n,d as r,g as i,h as a,i as o,l as s,n as c,p as l,s as u,u as ee,v as d}from"./ConstantsUtil-DmLsRHAl.js";var te={attribute:!0,type:String,converter:t,reflect:!1,hasChanged:i},ne=(e=te,t,n)=>{let{kind:r,metadata:i}=n,a=globalThis.litPropertyMetadata.get(i);if(a===void 0&&globalThis.litPropertyMetadata.set(i,a=new Map),r===`setter`&&((e=Object.create(e)).wrapped=!0),a.set(n.name,e),r===`accessor`){let{name:r}=n;return{set(n){let i=t.get.call(this);t.set.call(this,n),this.requestUpdate(r,i,e,!0,n)},init(t){return t!==void 0&&this.C(r,void 0,e,t),t}}}if(r===`setter`){let{name:r}=n;return function(n){let i=this[r];t.call(this,n),this.requestUpdate(r,i,e,!0,n)}}throw Error(`Unsupported decorator location: `+r)};function f(e){return(t,n)=>typeof n==`object`?ne(e,t,n):((e,t,n)=>{let r=t.hasOwnProperty(n);return t.constructor.createProperty(n,e),r?Object.getOwnPropertyDescriptor(t,n):void 0})(e,t,n)}function re(e){return f({...e,state:!0,attribute:!1})}var p=`.`,m={getSpacingStyles(e,t){if(Array.isArray(e))return e[t]?`var(--apkt-spacing-${e[t]})`:void 0;if(typeof e==`string`)return`var(--apkt-spacing-${e})`},getFormattedDate(e){return new Intl.DateTimeFormat(`en-US`,{month:`short`,day:`numeric`}).format(e)},formatCurrency(e=0,t={}){let n=Number(e);return isNaN(n)?`$0.00`:new Intl.NumberFormat(`en-US`,{style:`currency`,currency:`USD`,minimumFractionDigits:2,maximumFractionDigits:2,...t}).format(n)},getHostName(e){try{return new URL(e).hostname}catch{return``}},getTruncateString({string:e,charsStart:t,charsEnd:n,truncate:r}){return e.length<=t+n?e:r===`end`?`${e.substring(0,t)}...`:r===`start`?`...${e.substring(e.length-n)}`:`${e.substring(0,Math.floor(t))}...${e.substring(e.length-Math.floor(n))}`},generateAvatarColors(e){let t=e.toLowerCase().replace(/^0x/iu,``).replace(/[^a-f0-9]/gu,``).substring(0,6).padEnd(6,`0`),n=this.hexToRgb(t),r=getComputedStyle(document.documentElement).getPropertyValue(`--w3m-border-radius-master`),i=100-3*Number(r?.replace(`px`,``)),a=`${i}% ${i}% at 65% 40%`,o=[];for(let e=0;e<5;e+=1){let t=this.tintColor(n,.15*e);o.push(`rgb(${t[0]}, ${t[1]}, ${t[2]})`)}return`
2
+ import{t as e}from"./preload-helper-BaNbYf_w.js";import{_ as t,c as n,d as r,g as i,h as a,i as o,l as s,n as c,p as l,s as u,u as ee,v as d}from"./ConstantsUtil-DmLsRHAl.js";var te={attribute:!0,type:String,converter:t,reflect:!1,hasChanged:i},ne=(e=te,t,n)=>{let{kind:r,metadata:i}=n,a=globalThis.litPropertyMetadata.get(i);if(a===void 0&&globalThis.litPropertyMetadata.set(i,a=new Map),r===`setter`&&((e=Object.create(e)).wrapped=!0),a.set(n.name,e),r===`accessor`){let{name:r}=n;return{set(n){let i=t.get.call(this);t.set.call(this,n),this.requestUpdate(r,i,e,!0,n)},init(t){return t!==void 0&&this.C(r,void 0,e,t),t}}}if(r===`setter`){let{name:r}=n;return function(n){let i=this[r];t.call(this,n),this.requestUpdate(r,i,e,!0,n)}}throw Error(`Unsupported decorator location: `+r)};function f(e){return(t,n)=>typeof n==`object`?ne(e,t,n):((e,t,n)=>{let r=t.hasOwnProperty(n);return t.constructor.createProperty(n,e),r?Object.getOwnPropertyDescriptor(t,n):void 0})(e,t,n)}function re(e){return f({...e,state:!0,attribute:!1})}var p=`.`,m={getSpacingStyles(e,t){if(Array.isArray(e))return e[t]?`var(--apkt-spacing-${e[t]})`:void 0;if(typeof e==`string`)return`var(--apkt-spacing-${e})`},getFormattedDate(e){return new Intl.DateTimeFormat(`en-US`,{month:`short`,day:`numeric`}).format(e)},formatCurrency(e=0,t={}){let n=Number(e);return isNaN(n)?`$0.00`:new Intl.NumberFormat(`en-US`,{style:`currency`,currency:`USD`,minimumFractionDigits:2,maximumFractionDigits:2,...t}).format(n)},getHostName(e){try{return new URL(e).hostname}catch{return``}},getTruncateString({string:e,charsStart:t,charsEnd:n,truncate:r}){return e.length<=t+n?e:r===`end`?`${e.substring(0,t)}...`:r===`start`?`...${e.substring(e.length-n)}`:`${e.substring(0,Math.floor(t))}...${e.substring(e.length-Math.floor(n))}`},generateAvatarColors(e){let t=e.toLowerCase().replace(/^0x/iu,``).replace(/[^a-f0-9]/gu,``).substring(0,6).padEnd(6,`0`),n=this.hexToRgb(t),r=getComputedStyle(document.documentElement).getPropertyValue(`--w3m-border-radius-master`),i=100-3*Number(r?.replace(`px`,``)),a=`${i}% ${i}% at 65% 40%`,o=[];for(let e=0;e<5;e+=1){let t=this.tintColor(n,.15*e);o.push(`rgb(${t[0]}, ${t[1]}, ${t[2]})`)}return`
3
3
  --local-color-1: ${o[0]};
4
4
  --local-color-2: ${o[1]};
5
5
  --local-color-3: ${o[2]};
@@ -1,14 +1,14 @@
1
1
  <!doctype html>
2
2
  <html lang="en">
3
- <script type="module" crossorigin src="./assets/index-B5YmgwLH.js"></script>
3
+ <script type="module" crossorigin src="./assets/index-DPuRQJG2.js"></script>
4
4
  <link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-BcKkbAw3.js">
5
- <link rel="modulepreload" crossorigin href="./assets/preload-helper-uBIymjUX.js">
6
- <link rel="modulepreload" crossorigin href="./assets/roles-C9V_uDr_.js">
5
+ <link rel="modulepreload" crossorigin href="./assets/preload-helper-BaNbYf_w.js">
6
+ <link rel="modulepreload" crossorigin href="./assets/roles-DKPW79Ca.js">
7
7
 
8
8
  <meta charset="utf-8">
9
9
  <meta name="viewport" content="width=device-width, initial-scale=1">
10
10
  <title>Fangorn app</title>
11
- <style>:root{--bg:#fbfbf8;--fg:#1a1c19;--muted:#6a6f68;--line:#dfe1db;--accent:#2f5d45;--mark:#e6eedb;--hot:#b4432f;--edge:#b7bab2;--font:system-ui, sans-serif;--font-display:var(--font);--radius:3px;--gutter:16px;--measure:760px;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--bg:#121412;--fg:#e4e6e0;--muted:#959a92;--line:#2a2e29;--accent:#8fbf9f;--mark:#2d3b2a;--hot:#d9674f;--edge:#4b5049;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}}*{box-sizing:border-box}body{background:var(--bg);color:var(--fg);font:16px/1.5 var(--font);margin:0}a{color:var(--accent)}button,input{font:inherit;color:inherit}button{cursor:pointer;border:1px solid var(--line);border-radius:var(--radius);background:0 0;padding:4px 10px}button:disabled{opacity:.45;cursor:default}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}ul{margin:0;padding:0;list-style:none}h2{text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin:28px 0 8px;font-size:.75rem;font-weight:600}h2.page,.record h2{font:600 1.3rem/1.3 var(--font-display);text-transform:none;letter-spacing:0;color:var(--fg);margin:4px 0 12px}h3{margin:0;font-size:1rem}small,time,.hint,.empty,.about,#status,.resultsbar,.detail{color:var(--muted)}small,time,.hint{font-size:.8rem}.paid{border:1px solid var(--line);border-radius:var(--radius);gap:6px;margin:12px 0;padding:10px 14px;display:grid}.paid h3{margin:0;font-size:1rem}.paid ul{margin:0;padding-left:18px}.detail{margin:0;font-size:.9rem}mark{background:var(--mark);color:inherit}.bar{z-index:2;background:var(--bg);border-bottom:1px solid var(--line);position:sticky;top:0}.bar>div,main,footer{max-width:var(--measure);padding-inline:var(--gutter);margin:0 auto}.bar>div{flex-wrap:wrap;align-items:center;gap:12px;padding-block:10px;display:flex}#home{color:var(--fg);font:600 1.1rem var(--font-display);white-space:nowrap;text-decoration:none}#q{flex:240px}#q input{border:1px solid var(--line);border-radius:var(--radius);background:0 0;width:100%;padding:8px 10px}nav{gap:14px;font-size:.9rem;display:flex}nav a{color:var(--muted);text-decoration:none}nav a.on,nav a:hover{color:var(--fg)}main{padding-block:16px 48px}.list,article{border-top:1px solid var(--line)}.row{border-bottom:1px solid var(--line);align-items:flex-start;gap:10px;padding:10px 0;display:flex}.main{overflow-wrap:anywhere;flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.title{color:var(--fg);font-weight:500;text-decoration:none}.title:hover{color:var(--accent)}.resultsbar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px;margin-bottom:8px;font-size:.9rem;display:flex}.sort,.chips,.acts,.votes{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.chips{margin:4px 0 16px}.picker{color:var(--muted);flex-direction:column;gap:4px;margin:4px 0 16px;font-size:.85rem;display:inline-flex}.picker select{font:inherit;color:var(--fg);background:var(--bg);border:1px solid var(--edge);border-radius:var(--radius);min-width:min(320px,100%);padding:6px 10px;font-size:1rem}details.about summary{cursor:pointer;color:var(--fg);font-size:1.05rem}details.about p{color:var(--muted);margin:6px 0 0}.chip{padding:2px 10px;font-size:.85rem}.chip.on,.vote[aria-pressed=true]{border-color:var(--accent);background:var(--mark)}.vote{padding:2px 7px;line-height:1.3}.star{color:var(--muted);border:0;padding:4px 6px;font-size:1.25rem;line-height:1}.star[aria-pressed=true]{color:var(--accent);background:0 0}.more,.linkish{color:var(--accent);border:0;padding:8px 0 0;font-size:.9rem}.act{color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);padding:4px 10px;font-size:.88rem;text-decoration:none}article{padding:12px 0 4px}article header{flex-direction:column;margin-bottom:4px;display:flex}article .list{border-top:0}article .row:last-child{border-bottom:0}.record{border-top:0}.record dl{grid-template-columns:max-content 1fr;gap:2px 14px;margin:12px 0;font-size:.88rem;display:grid}.record dt{color:var(--muted);text-transform:capitalize}.record dd{margin:0}.acts{margin:12px 0 4px}.doc iframe{border:1px solid var(--line);background:#fff;width:100%;height:80vh;margin-top:12px}.taste{margin-bottom:20px}.pills{flex-wrap:wrap;gap:6px;margin:6px 0;display:flex}.pills li{border:1px solid var(--line);border-radius:var(--radius);align-items:center;gap:2px;padding:0 0 0 8px;font-size:.85rem;display:inline-flex}.pills li.no{color:var(--muted);text-decoration:line-through}.pills button{color:var(--muted);border:0;padding:0 8px}.knobs{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin:12px 0;display:grid}.knob{gap:2px;font-size:.85rem;display:grid}.knob span{justify-content:space-between;font-weight:600;display:flex}.knob input{width:100%;accent-color:var(--accent)}body.wide{--measure:1200px}.mapbar input{border:1px solid var(--line);border-radius:var(--radius);background:0 0;width:100%;margin-bottom:8px;padding:8px 10px}.mapwrap{grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:start;gap:16px;display:grid}@media (width<=760px){.mapwrap{grid-template-columns:1fr}}svg.map{width:100%;height:auto;display:block;position:sticky;top:70px}svg.map path{stroke:var(--edge);stroke-width:.8px;cursor:pointer}svg.map path:hover,svg.map path:focus{stroke:var(--accent);stroke-width:2px;outline:none}svg.map path.near{stroke:var(--accent);stroke-width:2px;stroke-dasharray:4 3}svg.map path.on{stroke:var(--fg);stroke-width:2.6px}.h0{fill:var(--bg);background:var(--bg)}.h1{fill:color-mix(in srgb, var(--hot) 18%, var(--bg));background:color-mix(in srgb, var(--hot) 18%, var(--bg))}.h2{fill:color-mix(in srgb, var(--hot) 36%, var(--bg));background:color-mix(in srgb, var(--hot) 36%, var(--bg))}.h3{fill:color-mix(in srgb, var(--hot) 56%, var(--bg));background:color-mix(in srgb, var(--hot) 56%, var(--bg))}.h4{fill:color-mix(in srgb, var(--hot) 78%, var(--bg));background:color-mix(in srgb, var(--hot) 78%, var(--bg))}.h5{fill:var(--hot);background:var(--hot)}.sw{border:1px solid var(--edge);vertical-align:-1px;width:14px;height:12px;display:inline-block}.panel h3{margin:4px 0}.panel .chips{margin:6px 0}.panel .list{margin-top:8px}footer{color:var(--muted);padding-block:0 32px;font-size:.8rem}</style>
11
+ <style>:root{--bg:#fbfbf8;--fg:#1a1c19;--muted:#6a6f68;--line:#dfe1db;--accent:#2f5d45;--mark:#e6eedb;--hot:#b4432f;--edge:#b7bab2;--font:system-ui, sans-serif;--font-display:var(--font);--radius:3px;--gutter:16px;--measure:760px;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--bg:#121412;--fg:#e4e6e0;--muted:#959a92;--line:#2a2e29;--accent:#8fbf9f;--mark:#2d3b2a;--hot:#d9674f;--edge:#4b5049;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}}*{box-sizing:border-box}body{background:var(--bg);color:var(--fg);font:16px/1.5 var(--font);margin:0}a{color:var(--accent)}button,input{font:inherit;color:inherit}button{cursor:pointer;border:1px solid var(--line);border-radius:var(--radius);background:0 0;padding:4px 10px}button:disabled{opacity:.45;cursor:default}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}ul{margin:0;padding:0;list-style:none}h2{text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin:28px 0 8px;font-size:.75rem;font-weight:600}h2.page,.record h2{font:600 1.3rem/1.3 var(--font-display);text-transform:none;letter-spacing:0;color:var(--fg);margin:4px 0 12px}h3{margin:0;font-size:1rem}small,time,.hint,.empty,.about,#status,.resultsbar,.detail{color:var(--muted)}small,time,.hint{font-size:.8rem}.paid{border:1px solid var(--line);border-radius:var(--radius);gap:6px;margin:12px 0;padding:10px 14px;display:grid}.paid h3{margin:0;font-size:1rem}.paid ul{margin:0;padding-left:18px}.detail{margin:0;font-size:.9rem}mark{background:var(--mark);color:inherit}.bar{z-index:2;background:var(--bg);border-bottom:1px solid var(--line);position:sticky;top:0}.bar>div,main,footer{max-width:var(--measure);padding-inline:var(--gutter);margin:0 auto}.bar>div{flex-wrap:wrap;align-items:center;gap:12px;padding-block:10px;display:flex}#home{color:var(--fg);font:600 1.1rem var(--font-display);white-space:nowrap;text-decoration:none}#q{flex:240px}#q input{border:1px solid var(--line);border-radius:var(--radius);background:0 0;width:100%;padding:8px 10px}nav{gap:14px;font-size:.9rem;display:flex}nav a{color:var(--muted);text-decoration:none}nav a.on,nav a:hover{color:var(--fg)}main{padding-block:16px 48px}.list,article{border-top:1px solid var(--line)}.row{border-bottom:1px solid var(--line);align-items:flex-start;gap:10px;padding:10px 0;display:flex}.main{overflow-wrap:anywhere;flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.title{color:var(--fg);font-weight:500;text-decoration:none}.title:hover{color:var(--accent)}.resultsbar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px;margin-bottom:8px;font-size:.9rem;display:flex}.sort,.chips,.acts,.votes{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.chips{margin:4px 0 16px}.picker{color:var(--muted);flex-direction:column;gap:4px;margin:4px 0 16px;font-size:.85rem;display:inline-flex}.picker select{font:inherit;color:var(--fg);background:var(--bg);border:1px solid var(--edge);border-radius:var(--radius);min-width:min(320px,100%);padding:6px 10px;font-size:1rem}details.about summary{cursor:pointer;color:var(--fg);font-size:1.05rem}details.about p{color:var(--muted);margin:6px 0 0}.chip{padding:2px 10px;font-size:.85rem}.chip.on,.vote[aria-pressed=true]{border-color:var(--accent);background:var(--mark)}.vote{padding:2px 7px;line-height:1.3}.star{color:var(--muted);border:0;padding:4px 6px;font-size:1.25rem;line-height:1}.star[aria-pressed=true]{color:var(--accent);background:0 0}.more,.linkish{color:var(--accent);border:0;padding:8px 0 0;font-size:.9rem}.act{color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);padding:4px 10px;font-size:.88rem;text-decoration:none}article{padding:12px 0 4px}article header{flex-direction:column;margin-bottom:4px;display:flex}article .list{border-top:0}article .row:last-child{border-bottom:0}.record{border-top:0}.record dl{grid-template-columns:max-content 1fr;gap:2px 14px;margin:12px 0;font-size:.88rem;display:grid}.record dt{color:var(--muted);text-transform:capitalize}.record dd{margin:0}.acts{margin:12px 0 4px}.signal{padding:14px 0 6px}.signal h3{font:600 1.15rem/1.3 var(--font-display);margin:4px 0 6px}.signal .meta{color:var(--muted);text-transform:uppercase;letter-spacing:.04em;flex-wrap:wrap;align-items:center;gap:4px 10px;font-size:.78rem;display:flex}.stage{border:1px solid var(--accent);color:var(--accent);text-transform:none;letter-spacing:0;border-radius:999px;padding:0 8px}.steps{margin:8px 0 0;padding:0;list-style:none}.steps li{border-top:1px solid var(--line);grid-template-columns:7.5em 1fr;gap:10px;padding:5px 0;font-size:.9rem;display:grid}.steps time{color:var(--muted);font-variant-numeric:tabular-nums}.doc iframe{border:1px solid var(--line);background:#fff;width:100%;height:80vh;margin-top:12px}.taste{margin-bottom:20px}.pills{flex-wrap:wrap;gap:6px;margin:6px 0;display:flex}.pills li{border:1px solid var(--line);border-radius:var(--radius);align-items:center;gap:2px;padding:0 0 0 8px;font-size:.85rem;display:inline-flex}.pills li.no{color:var(--muted);text-decoration:line-through}.pills button{color:var(--muted);border:0;padding:0 8px}.knobs{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin:12px 0;display:grid}.knob{gap:2px;font-size:.85rem;display:grid}.knob span{justify-content:space-between;font-weight:600;display:flex}.knob input{width:100%;accent-color:var(--accent)}body.wide{--measure:1200px}.mapbar input{border:1px solid var(--line);border-radius:var(--radius);background:0 0;width:100%;margin-bottom:8px;padding:8px 10px}.mapwrap{grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:start;gap:16px;display:grid}@media (width<=760px){.mapwrap{grid-template-columns:1fr}}svg.map{width:100%;height:auto;display:block;position:sticky;top:70px}svg.map path{stroke:var(--edge);stroke-width:.8px;cursor:pointer}svg.map path:hover,svg.map path:focus{stroke:var(--accent);stroke-width:2px;outline:none}svg.map path.near{stroke:var(--accent);stroke-width:2px;stroke-dasharray:4 3}svg.map path.on{stroke:var(--fg);stroke-width:2.6px}.h0{fill:var(--bg);background:var(--bg)}.h1{fill:color-mix(in srgb, var(--hot) 18%, var(--bg));background:color-mix(in srgb, var(--hot) 18%, var(--bg))}.h2{fill:color-mix(in srgb, var(--hot) 36%, var(--bg));background:color-mix(in srgb, var(--hot) 36%, var(--bg))}.h3{fill:color-mix(in srgb, var(--hot) 56%, var(--bg));background:color-mix(in srgb, var(--hot) 56%, var(--bg))}.h4{fill:color-mix(in srgb, var(--hot) 78%, var(--bg));background:color-mix(in srgb, var(--hot) 78%, var(--bg))}.h5{fill:var(--hot);background:var(--hot)}.sw{border:1px solid var(--edge);vertical-align:-1px;width:14px;height:12px;display:inline-block}.panel h3{margin:4px 0}.panel .chips{margin:6px 0}.panel .list{margin-top:8px}footer{color:var(--muted);padding-block:0 32px;font-size:.8rem}</style>
12
12
  <link rel="stylesheet" href="./theme.css">
13
13
  <header class="bar"><div>
14
14
  <a id="home" href="#/"><span id="name">Fangorn app</span></a>
@@ -12,7 +12,7 @@
12
12
  // Pure functions, so `node tools.js` checks them with no browser and no network.
13
13
 
14
14
  import { bestPassage, cosine, lexScore, norm, zFloor } from "../core/rank.js";
15
- import { briefOf, collections, linkOf, subtitleOf, titleOf, typeOf, values } from "../core/roles.js";
15
+ import { briefOf, collections, linkOf, pick, subtitleOf, titleOf, typeOf, values } from "../core/roles.js";
16
16
 
17
17
  /** Fields that are plumbing rather than content — never worth faceting or
18
18
  * showing as "what this corpus holds". */
@@ -214,6 +214,50 @@ export function neighbors(rows, id, roles, { limit = 10, fields } = {}) {
214
214
  };
215
215
  }
216
216
 
217
+ /**
218
+ * One thing followed across rows: a matter a committee took up and then the council, as a
219
+ * thread whose steps are in order. Rows group by the declared `thread` role (view.js wrote
220
+ * each row's target onto it); the thread's head is the target row itself, when this corpus
221
+ * holds it. The thread that moved last comes first.
222
+ *
223
+ * A step is one occasion: rows sharing a date and a subtitle (an agenda and its minutes)
224
+ * count once, keeping the one that says the most. `where` narrows the steps, `id` asks for
225
+ * one thread, and `min` (default 2) is how many steps make a thread: a matter taken up once
226
+ * so far is a record, not news.
227
+ *
228
+ * ponytail: the head is found by value (any field of a row that is not itself a step), since
229
+ * a fused view's identity role names one type's field. Index it if corpora get huge.
230
+ */
231
+ export function threads(rows, roles, { id, where, min = 2, limit = 20, offset = 0, fields = [] } = {}) {
232
+ const f = roles.thread;
233
+ if (!f) return { total: 0, offset, threads: [], error: "this corpus declares no thread relation" };
234
+ const dateOf = (r) => String(pick(r, roles.temporal) ?? "").slice(0, 10);
235
+ const groups = new Map();
236
+ for (const r of rows) {
237
+ const k = r[f] == null ? "" : String(r[f]);
238
+ if (!k || (id != null && k !== String(id)) || !matches(r, where)) continue;
239
+ const occ = `${dateOf(r)}|${subtitleOf(r, roles) ?? ""}`;
240
+ const g = groups.get(k) ?? groups.set(k, new Map()).get(k);
241
+ const had = g.get(occ);
242
+ if (!had || String(r.text ?? "").length > String(had.text ?? "").length) g.set(occ, r);
243
+ }
244
+ const heads = new Map();
245
+ for (const r of rows) {
246
+ if (r[f] != null) continue;
247
+ for (const v of Object.values(r)) if (typeof v === "string" && groups.has(v) && !heads.has(v)) heads.set(v, r);
248
+ }
249
+ const all = [...groups].map(([k, g]) => {
250
+ const steps = [...g.values()].sort((a, b) => dateOf(a).localeCompare(dateOf(b)));
251
+ const head = heads.get(k), last = steps.at(-1);
252
+ return {
253
+ id: k, title: titleOf(head ?? last, roles), url: linkOf(head ?? last, roles), first: dateOf(steps[0]), date: dateOf(last),
254
+ head: head ? brief(head, roles, fields) : null,
255
+ steps: steps.map((r) => ({ ...brief(r, roles, fields), date: dateOf(r) })),
256
+ };
257
+ }).filter((t) => t.steps.length >= min || id != null).sort((a, b) => b.date.localeCompare(a.date) || a.id.localeCompare(b.id));
258
+ return { total: all.length, offset, threads: all.slice(offset, offset + limit) };
259
+ }
260
+
217
261
  /** One row, whole. Deliberately the only verb that returns everything — the
218
262
  * others preview, so an agent chooses when to spend its context. */
219
263
  export function getRow(rows, id, roles) {
@@ -377,5 +421,28 @@ if (typeof process !== "undefined" && import.meta.url === `file://${process.argv
377
421
  }
378
422
  }
379
423
 
380
- console.log("tools.js self-check ok — coverage not schema, semantic/lexical modes + z-floor, list-ish facets + where, neighbours, whole-row fetch, previews truncate");
424
+ // threads: one matter across meetings, an agenda and its minutes as one step, headed by
425
+ // the matter's own row, the latest-moving first.
426
+ {
427
+ const rl = { title: ["heading", "title"], subtitle: ["meeting"], temporal: ["date"], tags: [], measures: [], thread: "part_of", externalUrl: {} };
428
+ const pool = [
429
+ { id: "1", heading: "Rezone Oak St", meeting: "Plan Commission", date: "2026-01-05", part_of: "p:7", text: "on the agenda" },
430
+ { id: "2", heading: "Rezone Oak St", meeting: "Plan Commission", date: "2026-01-05", part_of: "p:7", text: "recommended for approval 5-0" },
431
+ { id: "3", heading: "Rezone Oak St", meeting: "Village Board", date: "2026-01-20", part_of: "p:7", text: "adopted" },
432
+ { id: "4", heading: "Pool bid", meeting: "Village Board", date: "2026-03-01", part_of: "p:9" },
433
+ { id: "5", heading: "Pool bid", meeting: "Finance", date: "2026-02-20", part_of: "p:9", place: "A" },
434
+ { id: "6", heading: "Once", meeting: "Village Board", date: "2026-04-01", part_of: "p:11" },
435
+ { id: "m7", title: "Rezoning of Oak Street", matter_id: "p:7" },
436
+ ];
437
+ const t = threads(pool, rl);
438
+ if (t.threads.map((x) => x.id).join() !== "p:9,p:7") throw new Error(`latest-moving first, one step is not a thread: ${t.threads.map((x) => x.id)}`);
439
+ const oak = t.threads[1];
440
+ if (oak.steps.map((s) => s.id).join() !== "2,3" || oak.title !== "Rezoning of Oak Street" || oak.head?.id !== "m7")
441
+ throw new Error(`an agenda and its minutes are one step, the head titles the thread: ${JSON.stringify(oak)}`);
442
+ if (threads(pool, rl, { id: "p:11" }).threads[0]?.steps.length !== 1) throw new Error("asked for by id, any thread comes back");
443
+ if (threads(pool, rl, { where: { place: "A" }, min: 1 }).total !== 1) throw new Error("where narrows the steps");
444
+ if (!threads(pool, { ...rl, thread: null }).error) throw new Error("no thread role must say so");
445
+ }
446
+
447
+ console.log("tools.js self-check ok — coverage not schema, semantic/lexical modes + z-floor, list-ish facets + where, neighbours, whole-row fetch, previews truncate, threads");
381
448
  }
@@ -18,7 +18,7 @@
18
18
 
19
19
  import { configure, loadShard, trimView } from "../core/shard.js";
20
20
  import { rolesFrom, textOf } from "../core/roles.js";
21
- import { browse, facet, getRow, neighbors, search } from "./tools.js";
21
+ import { browse, facet, getRow, neighbors, search, threads } from "./tools.js";
22
22
  import { lexScore, rankDomains } from "../core/rank.js";
23
23
  import { existsSync } from "node:fs";
24
24
  import { EMBED_MODEL, embedQueryDirect, warmDirect } from "../core/embed.js";
@@ -150,6 +150,10 @@ export function viewTools(app, { fetchCatalog = fetch } = {}) {
150
150
  const [v] = await pick([view]);
151
151
  return { view: v.name, ...browse(await load(v), rolesOf(v.view), { limit, offset, sort, where: w }) };
152
152
  },
153
+ threads: async ({ view, id, limit = 20, offset = 0, where: w }) => {
154
+ const [v] = await pick([view]);
155
+ return { view: v.name, ...threads(await load(v), rolesOf(v.view), { id, limit, offset, where: w }) };
156
+ },
153
157
  };
154
158
 
155
159
  const n = app.name;
@@ -166,6 +170,8 @@ export function viewTools(app, { fetchCatalog = fetch } = {}) {
166
170
  inputSchema: { type: "object", properties: { field: str("Field name, e.g. from get"), views: VIEWS, limit: num("Max values (default 20)"), where: WHERE }, required: ["field"] } },
167
171
  { name: "browse", description: `List one view of ${n} without a query, with filters, sorting and paging.`,
168
172
  inputSchema: { type: "object", properties: { view: str("View name from describe"), limit: num("default 20"), offset: num("default 0"), sort: str("Field to sort by"), where: WHERE }, required: ["view"] } },
173
+ { name: "threads", description: `One thing followed across records of a ${n} view (a matter from committee to council): steps in date order, the latest-moving first. Views that declare no thread say so.`,
174
+ inputSchema: { type: "object", properties: { view: str("View name from describe"), id: str("One thread, by a step's thread field"), limit: num("default 20"), offset: num("default 0"), where: WHERE }, required: ["view"] } },
169
175
  ];
170
176
  // Records the app sells over x402 (its card's `paid`). Paid from this server's wallet,
171
177
  // never above FANGORN_MCP_MAX_PRICE, and checked against the sha256 the app published
package/src/core/roles.js CHANGED
@@ -71,7 +71,7 @@ export function rolesFrom(manifests = [], sample = []) {
71
71
  let launch = null, refers = null;
72
72
  const entityTypes = [];
73
73
  const gates = [];
74
- let identity = null;
74
+ let identity = null, thread = null;
75
75
 
76
76
  const push = (into, v) => { for (const x of arr(v)) if (x && !into.includes(x)) into.push(x); };
77
77
 
@@ -80,6 +80,10 @@ export function rolesFrom(manifests = [], sample = []) {
80
80
  push(title, rm.title); push(subtitle, rm.subtitle); push(tags, rm.tags);
81
81
  push(text, rm.text); push(measures, rm.measures); push(spatial, rm.spatial); push(media, rm.media); push(temporal, rm.temporal);
82
82
  identity ??= rm.identity ?? null;
83
+ // The relation that follows one thing across rows: an agenda item's `part_of` names
84
+ // its matter, so the items taken up at a committee and then the council are one thread.
85
+ // view.js writes the target's identity onto the row; a reader groups by it.
86
+ thread ??= rm.thread ?? null;
83
87
  // Where a row in THIS corpus points, in someone else's.
84
88
  //
85
89
  // The 21,131 subtitle rows in the archive bundle each carry the `path`
@@ -137,7 +141,7 @@ export function rolesFrom(manifests = [], sample = []) {
137
141
  guess(text, GUESS.text);
138
142
 
139
143
  return {
140
- title, subtitle, tags, text, measures, spatial, media, temporal, facets, identity,
144
+ title, subtitle, tags, text, measures, spatial, media, temporal, facets, identity, thread,
141
145
  labels, types, fieldLabels, externalUrl, actions, launch, refers, entityTypes,
142
146
  // What this view sells, if anything. A list because a fused view can pull
143
147
  // domains from several publishers and they price independently.
@@ -393,6 +397,7 @@ if (typeof process !== "undefined" && import.meta.url === `file://${process.argv
393
397
  if (!r.declared) throw new Error("a manifest with a role_map is declared, not sniffed");
394
398
  if (titleOf(row, r) !== "Cira's Red, White & Brew Bar") throw new Error("declared title lost");
395
399
  if (subtitleOf(row, r) !== "Bar & Grill") throw new Error("declared subtitle lost");
400
+ if (r.thread !== null || rolesFrom([{ role_map: { thread: "part_of" } }, places]).thread !== "part_of") throw new Error("thread is declared, never guessed");
396
401
  if (textOf(row, r) !== "Bar & Grill in Saint Germain, WI.") throw new Error("empty declared text fields must be skipped, not joined as blanks");
397
402
  // A declared reference into another publisher's corpus. All three parts or
398
403
  // none: a half-written join would silently point at nothing, and the caller
@@ -363,7 +363,7 @@ export async function ship(configPath, { crawl = true, deploy = true, dry = fals
363
363
  * `.ship/vectors.ndjson` keeps it cheap, so two recipes over the same data embed only the
364
364
  * text that differs.
365
365
  *
366
- * ponytail: reads every `volume_<n>_*.json` but edges in a source's --output-dir; the
366
+ * ponytail: reads every `volume_<n>_*.json` in a source's --output-dir; the
367
367
  * harness publishes only the stems its source declares, so a stale file left by an older
368
368
  * shape of a source is built here and not on chain. Clear the stage dir if that bites.
369
369
  */
@@ -386,15 +386,22 @@ export async function buildLocal(configPath, out, { crawl = false, only = null,
386
386
  const dir = resolve(cwd, expandEnv(s.command[at + 1]));
387
387
  const volume = s.command.includes("--volume") ? s.command[s.command.indexOf("--volume") + 1] : "1";
388
388
  const ns = (chain[`${owner}/${s.namespace}`] ??= { vertices: [], edges: [] });
389
+ const cidOf = new Map();
389
390
  for (const f of existsSync(dir) ? readdirSync(dir).sort() : []) {
390
391
  const m = new RegExp(`^volume_${volume}_(.+)\\.json$`).exec(f);
391
392
  if (!m || m[1] === "edges") continue;
392
393
  // The harness publishes {id: name, tag: entity, payload: fields}; the chain
393
394
  // addresses it by the payload's hash, and so does this.
394
- for (const n of JSON.parse(readFileSync(join(dir, f), "utf8"))) ns.vertices.push({
395
- cid: createHash("sha256").update(JSON.stringify(stable(n.fields))).digest("hex"),
396
- schemaId: n.fields?.entityType ?? m[1][0].toUpperCase() + m[1].slice(1), payload: n.fields });
395
+ for (const n of JSON.parse(readFileSync(join(dir, f), "utf8"))) {
396
+ const cid = createHash("sha256").update(JSON.stringify(stable(n.fields))).digest("hex");
397
+ cidOf.set(n.name, cid);
398
+ ns.vertices.push({ cid, schemaId: n.fields?.entityType ?? m[1][0].toUpperCase() + m[1].slice(1), payload: n.fields });
399
+ }
397
400
  }
401
+ // Edges name vertices by the harness's local ids; the chain returns them by CID.
402
+ const edges = join(dir, `volume_${volume}_edges.json`);
403
+ for (const e of existsSync(edges) ? JSON.parse(readFileSync(edges, "utf8")) : [])
404
+ if (cidOf.has(e.from) && cidOf.has(e.to)) ns.edges.push({ relation: e.rel, sourceCid: cidOf.get(e.from), targetCid: cidOf.get(e.to) });
398
405
  }
399
406
  // An empty stage is a crawl that did not happen (or a cache that was not restored), not
400
407
  // an app with no records; a view of it would grade as a failure for the wrong reason.
@@ -26,8 +26,9 @@
26
26
  // its `embedder` and this refuses to append to a view another encoder built.
27
27
  //
28
28
  // ponytail: flat records. quickbeam folds a record's graph neighbours into its text
29
- // and can fuse publishers by shared identity; that is the hosted tier's job. Edges
30
- // are not shipped here either (quickbeam's edges.json) until a reader needs them.
29
+ // and can fuse publishers by shared identity; that is the hosted tier's job. Edges are
30
+ // not shipped as edges (quickbeam's edges.json): a relation the schema declares is written
31
+ // onto its source row instead (`linkRelations`), which is all a serverless reader needs.
31
32
 
32
33
  import { gunzipSync, gzipSync } from "node:zlib";
33
34
  import { createHash } from "node:crypto";
@@ -38,7 +39,7 @@ import { coverage as fitCoverage } from "../market/reactions.js";
38
39
  export const SCHEMA_NAMESPACE = "fangorn.schema";
39
40
  export const TYPE_TAG = "fangorn.type.v1";
40
41
  export const EMBEDDER = { runtime: "transformers.js", model: EMBED_MODEL, dtype: EMBED_DTYPE };
41
- const SINGULAR = ["identity", "title", "subtitle", "temporal", "spatial", "media"];
42
+ const SINGULAR = ["identity", "title", "subtitle", "temporal", "spatial", "media", "thread"];
42
43
  const MULTI = ["tags", "measures", "relations", "text"];
43
44
  const ZERO = "0x0000000000000000000000000000000000000000";
44
45
  // Refit coverage once a domain has grown this many times over what it was fit on.
@@ -120,6 +121,23 @@ export function domainSpec(schema, tags) {
120
121
  return { description: decls.map((d) => d.description).filter(Boolean).join(" "), role_map, presentation };
121
122
  }
122
123
 
124
+ /**
125
+ * Each declared relation, onto the row it starts from: an agenda item with a `part_of` edge
126
+ * to a matter carries `part_of: <the matter's identity>`, so a reader groups rows by it
127
+ * without walking the graph. Undeclared edges are left alone (the schema is the contract).
128
+ * `records` maps CID → fields and is changed in place.
129
+ * ponytail: one target per relation and row (the last edge wins); a list when a relation
130
+ * needs many.
131
+ */
132
+ export function linkRelations(records, edges, schema) {
133
+ for (const e of edges ?? []) {
134
+ const from = records.get(e.sourceCid), to = records.get(e.targetCid);
135
+ if (!from || !to || !schema.relations.some((r) => r.rel === e.relation && r.from === from.entityType && r.to === to.entityType)) continue;
136
+ const id = schema.types[to.entityType]?.role_map?.identity;
137
+ from[e.relation] = String((id && to[id]) ?? e.targetCid);
138
+ }
139
+ }
140
+
123
141
  // ── a client that only reads ──────────────────────────────────────────────────
124
142
 
125
143
  /** An SDK client for reading commits: a throwaway key (nothing signs) and no upload
@@ -201,7 +219,7 @@ export function liveFields(out) {
201
219
  * Returns per-domain counts of what changed.
202
220
  */
203
221
  export async function publishView({ fangorn, app, namespaces, out, fromBlock = 0n, rebake = false,
204
- embed = embedDocumentDirect, log = console.log, shardRows = 20000 }) {
222
+ embed = embedDocumentDirect, log = console.log, shardRows = 20000, concurrency = 4 }) {
205
223
  if (!namespaces?.length) throw new Error("publishView: name the data namespace(s) to publish");
206
224
  fangorn.setAppId(app);
207
225
  const appId = fangorn.getAppId();
@@ -216,24 +234,54 @@ export async function publishView({ fangorn, app, namespaces, out, fromBlock = 0
216
234
  log(`schema: ${declared.length ? declared.join(", ") : "no declared types"}`);
217
235
 
218
236
  const report = {};
219
- for (const ns of namespaces.filter((n) => n !== SCHEMA_NAMESPACE)) {
220
- const timelines = await fangorn.appNamespaces({ namespace: ns, fromBlock: BigInt(fromBlock) });
221
- log(`${ns}: ${timelines.length} publisher(s) since block ${fromBlock}`);
222
- for (const { owner: publisher } of timelines) {
223
- const domain = domainFor(appId, publisher, ns);
224
- report[domain] = await publishDomain({
225
- fangorn, schema, publisher, ns, domain, dir: `${out}/cdn/domains/${domain}`, rebake, embed, log, shardRows,
226
- });
227
- }
237
+ // Several namespaces at once: reading one back through an IPFS gateway is block by block and
238
+ // takes minutes, while its embedding takes seconds. Each domain writes only its own folder.
239
+ const queue = namespaces.filter((n) => n !== SCHEMA_NAMESPACE);
240
+ // Who published into each namespace, from ONE scan of the app's commit logs. The chain filters
241
+ // them by app only, so a scan per namespace re-reads every log of the app each time: 376 s a
242
+ // namespace through a public RPC, against 3 s to read the namespace itself.
243
+ const reg = fangorn.getDataRegistry?.();
244
+ let committed = null;
245
+ if (reg?.getStateCommittedLogs && reg.namespaceKey) {
246
+ const logs = await retry(() => reg.getStateCommittedLogs({}, BigInt(fromBlock)));
247
+ committed = { keys: new Set(logs.map((l) => l.namespaceKey)), publishers: [...new Set(logs.map((l) => l.publisher))] };
248
+ log(`commit logs since block ${fromBlock}: ${logs.length} from ${committed.publishers.length} publisher(s)`);
228
249
  }
250
+ const publishersOf = (ns) => (committed
251
+ ? committed.publishers.filter((p) => committed.keys.has(reg.namespaceKey(p, ns))).map((owner) => ({ owner }))
252
+ : retry(() => fangorn.appNamespaces({ namespace: ns, fromBlock: BigInt(fromBlock) })));
253
+ const worker = async () => {
254
+ for (let ns; (ns = queue.shift()) !== undefined;) {
255
+ const timelines = await publishersOf(ns);
256
+ log(`${ns}: ${timelines.length} publisher(s) since block ${fromBlock}`);
257
+ for (const { owner: publisher } of timelines) {
258
+ const domain = domainFor(appId, publisher, ns);
259
+ report[domain] = await publishDomain({
260
+ fangorn, schema, publisher, ns, domain, dir: `${out}/cdn/domains/${domain}`, rebake, embed, log, shardRows,
261
+ });
262
+ }
263
+ }
264
+ };
265
+ await Promise.all(Array.from({ length: concurrency }, worker));
229
266
  writeCatalog(out);
230
267
  return report;
231
268
  }
232
269
 
270
+ /** A public RPC answers a burst with 429s: back off and try again, a few times. */
271
+ async function retry(f, tries = 6) {
272
+ for (let i = 0; ; i++) {
273
+ try { return await f(); } catch (e) {
274
+ if (i >= tries - 1 || !/429|Too Many Requests|rate limit|timeout|ECONNRESET|fetch failed/i.test(String(e?.message ?? e))) throw e;
275
+ await new Promise((ok) => setTimeout(ok, 2000 * 2 ** i));
276
+ }
277
+ }
278
+ }
279
+
233
280
  async function publishDomain({ fangorn, schema, publisher, ns, domain, dir, rebake, embed, log, shardRows }) {
234
- const { contents } = await fangorn.readNamespace(publisher, ns);
281
+ const { contents } = await retry(() => fangorn.readNamespace(publisher, ns));
235
282
  const records = new Map((contents.vertices ?? []).filter((v) => v.payload && typeof v.payload === "object")
236
283
  .map((v) => [v.cid, { ...v.payload, entityType: v.schemaId }]));
284
+ linkRelations(records, contents.edges, schema);
237
285
 
238
286
  if (rebake) rmSync(dir, { recursive: true, force: true }); // old shards would stay reachable
239
287
  const had = loadDomain(dir);
@@ -248,6 +296,11 @@ async function publishDomain({ fangorn, schema, publisher, ns, domain, dir, reba
248
296
  const live = (id) => had.rows.has(id) && !dead.has(id);
249
297
  const fresh = [...records.keys()].filter((id) => !live(id));
250
298
  const gone = [...had.rows.keys()].filter((id) => live(id) && !records.has(id));
299
+ // A row already served whose links changed (its matter came later, or moved): new fields,
300
+ // the same vector, so no embedding, only a rewrite.
301
+ const relinked = [...had.rows.keys()].filter((id) => live(id) && records.has(id)
302
+ && JSON.stringify(had.rows.get(id).fields) !== JSON.stringify(records.get(id)));
303
+ for (const id of relinked) had.rows.get(id).fields = records.get(id);
251
304
 
252
305
  // Roles per type: declared exactly, else guessed (and said so).
253
306
  const byType = new Map();
@@ -271,7 +324,7 @@ async function publishDomain({ fangorn, schema, publisher, ns, domain, dir, reba
271
324
  if (Math.floor((n + ids.length) / 1000) > Math.floor(n / 1000)) log(`${domain}: embedded ${n + ids.length}/${fresh.length}`);
272
325
  n += ids.length;
273
326
  }
274
- if (!fresh.length && !gone.length && had.manifest && !rebake) {
327
+ if (!fresh.length && !gone.length && !relinked.length && had.manifest && !rebake) {
275
328
  applySpec(dir, had.manifest, schema, had.rows, dead); // a schema change alone still lands
276
329
  return { added: 0, removed: 0 };
277
330
  }
@@ -297,11 +350,11 @@ async function publishDomain({ fangorn, schema, publisher, ns, domain, dir, reba
297
350
  }
298
351
  for (const id of gone) dead.add(id);
299
352
  // A tombstone hides a row from readers, but its shard still serves the bytes. A record
300
- // retracted for what it said (a name, an address) has to be gone, so any removal
301
- // rewrites the live rows into one shard and the old files are deleted. No re-embedding:
302
- // the vectors are already in the rows.
353
+ // retracted for what it said (a name, an address) has to be gone, so any removal (or a
354
+ // relink, whose rows' fields changed) rewrites the live rows into one shard and the old
355
+ // files are deleted. No re-embedding: the vectors are already in the rows.
303
356
  // ponytail: rewrites the whole domain on every removal; compact past a threshold if domains get big.
304
- if (dead.size) {
357
+ if (dead.size || relinked.length) {
305
358
  for (const s of manifest.shards) rmSync(`${dir}/shards/${s.file}`, { force: true });
306
359
  manifest.shards = [];
307
360
  for (const id of dead) had.rows.delete(id);
@@ -311,8 +364,8 @@ async function publishDomain({ fangorn, schema, publisher, ns, domain, dir, reba
311
364
  manifest.tombstones = [...dead].sort();
312
365
  manifest.created_at = Math.floor(Date.now() / 1000);
313
366
  applySpec(dir, manifest, schema, had.rows, dead, rolesOf);
314
- log(`${domain}: +${fresh.length} embedded, -${gone.length} removed, ${manifest.count} live`);
315
- return { added: fresh.length, removed: gone.length };
367
+ log(`${domain}: +${fresh.length} embedded, -${gone.length} removed, ${relinked.length} relinked, ${manifest.count} live`);
368
+ return { added: fresh.length, removed: gone.length, ...(relinked.length ? { relinked: relinked.length } : {}) };
316
369
  }
317
370
 
318
371
  /** Counts, types, roles, description, presentation and coverage from what the domain now holds. */
@@ -457,5 +510,31 @@ if (typeof process !== "undefined" && import.meta.url === `file://${process.argv
457
510
  { added: 2, removed: 0 }, "--rebake starts the domain over");
458
511
  eq(readdirSync(`${out}/cdn/domains/${d}/shards`).length, 1, "and leaves no orphaned shards");
459
512
  rmSync(out, { recursive: true, force: true });
460
- console.log("view.js self-check ok — domain names and embedded text match quickbeam's, the schema is applied, a second run embeds nothing, additions ship as delta shards, removals compact the domain, the real reader sees exactly the live records by the publisher's id, and another encoder's view is refused");
513
+
514
+ // Relations: a declared edge lands on its source row as the target's identity; an
515
+ // undeclared one does not; a link that arrives later rewrites the row without embedding it.
516
+ {
517
+ const T = { tag: "civic.item.v1" }, M = { tag: "civic.matter.v1" };
518
+ const rel = { [`${OWNER}/${SCHEMA_NAMESPACE}`]: { vertices: [
519
+ { cid: "ti", schemaId: TYPE_TAG, payload: { ...T, role_map: { identity: "item_id", title: "heading", thread: "part_of" } } },
520
+ { cid: "tm", schemaId: TYPE_TAG, payload: { ...M, role_map: { identity: "matter_id", title: "title" } } }],
521
+ edges: [{ relation: "part_of", sourceCid: "ti", targetCid: "tm" }] },
522
+ [`${PUB}/civic`]: { vertices: [
523
+ { cid: "i1", schemaId: T.tag, payload: { item_id: "a#1", heading: "Rezone, committee" } },
524
+ { cid: "i2", schemaId: T.tag, payload: { item_id: "b#4", heading: "Rezone, council" } },
525
+ { cid: "m1", schemaId: M.tag, payload: { matter_id: "plover:7", title: "Rezone" } }],
526
+ edges: [{ relation: "part_of", sourceCid: "i1", targetCid: "m1" }, { relation: "cites", sourceCid: "i2", targetCid: "m1" }] } };
527
+ const o = mkdtempSync(`${tmpdir()}/view-`), dr = domainFor(APP, PUB, "civic");
528
+ const go = () => publishView({ fangorn: localFangorn(rel, { appId: APP, owner: OWNER }), app: APP, namespaces: ["civic"], out: o, embed, log: () => {} });
529
+ const parts = () => Object.fromEntries([...loadDomain(`${o}/cdn/domains/${dr}`).rows.values()].map((r) => [r.fields.heading ?? r.fields.title, r.fields.part_of ?? r.fields.cites ?? null]));
530
+ await go();
531
+ eq(parts(), { "Rezone, committee": "plover:7", "Rezone, council": null, Rezone: null }, "a declared relation carries the target's identity; an undeclared one is not written");
532
+ eq(readJson(`${o}/cdn/domains/${dr}/manifest`).role_map.thread, "part_of", "the thread role reaches the manifest");
533
+ rel[`${PUB}/civic`].edges.push({ relation: "part_of", sourceCid: "i2", targetCid: "m1" });
534
+ const before = embedded.length;
535
+ eq((await go())[dr], { added: 0, removed: 0, relinked: 1 }, "a link that comes later relinks the row");
536
+ eq([parts()["Rezone, council"], embedded.length - before], ["plover:7", 0], "without embedding it again");
537
+ rmSync(o, { recursive: true, force: true });
538
+ }
539
+ console.log("view.js self-check ok — domain names and embedded text match quickbeam's, the schema is applied, a second run embeds nothing, additions ship as delta shards, removals compact the domain, the real reader sees exactly the live records by the publisher's id, and another encoder's view is refused, and declared relations land on their source rows");
461
540
  }
@@ -1 +0,0 @@
1
- import{n as e,r as t,t as n}from"./ConnectionStatusScreen-fUfROm9m-BeWLchcf.js";export{n as ConnectionStatusScreen,n as default,e as ConnectionStatusView,t as getErrorDetails};
@@ -1 +0,0 @@
1
- import{n as e,t}from"./ErrorScreen-DipdTWzE-c8g8En21.js";export{t as ErrorScreen,t as default,e as ErrorScreenView};
@@ -1 +0,0 @@
1
- import{i as e,n as t,r as n,t as r}from"./LinkPasskeyScreen-zbrl_AI_-D72Gb0LW.js";export{n as DoubleIconWrapper,t as LinkButton,r as LinkPasskeyScreen,r as default,e as LinkPasskeyView};
@@ -1 +0,0 @@
1
- import{i as e,n as t}from"./ccip-BZvRTL6g.js";export{t as offchainLookup,e as offchainLookupSignature};