@attocash/cli 0.1.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 (400) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +707 -0
  3. package/dist/application/app.d.ts +138 -0
  4. package/dist/application/app.js +654 -0
  5. package/dist/application/operations.d.ts +9 -0
  6. package/dist/application/operations.js +96 -0
  7. package/dist/cli/cli.d.ts +1 -0
  8. package/dist/cli/cli.js +460 -0
  9. package/dist/cli/help.d.ts +4 -0
  10. package/dist/cli/help.js +49 -0
  11. package/dist/cli/main.d.ts +2 -0
  12. package/dist/cli/main.js +13 -0
  13. package/dist/cli/onboarding.d.ts +14 -0
  14. package/dist/cli/onboarding.js +144 -0
  15. package/dist/cli/output.d.ts +2 -0
  16. package/dist/cli/output.js +231 -0
  17. package/dist/cli/terminal.d.ts +6 -0
  18. package/dist/cli/terminal.js +92 -0
  19. package/dist/cli/update-check.d.ts +1 -0
  20. package/dist/cli/update-check.js +12 -0
  21. package/dist/cli/updates.d.ts +2 -0
  22. package/dist/cli/updates.js +98 -0
  23. package/dist/core.d.ts +14 -0
  24. package/dist/core.js +14 -0
  25. package/dist/doctor/credential-probe.d.ts +7 -0
  26. package/dist/doctor/credential-probe.js +49 -0
  27. package/dist/doctor/doctor.d.ts +4 -0
  28. package/dist/doctor/doctor.js +213 -0
  29. package/dist/doctor/keyring.d.ts +7 -0
  30. package/dist/doctor/keyring.js +150 -0
  31. package/dist/doctor/profile.d.ts +19 -0
  32. package/dist/doctor/profile.js +73 -0
  33. package/dist/doctor/types.d.ts +40 -0
  34. package/dist/doctor/types.js +1 -0
  35. package/dist/domain/amount.d.ts +6 -0
  36. package/dist/domain/amount.js +27 -0
  37. package/dist/domain/errors.d.ts +10 -0
  38. package/dist/domain/errors.js +16 -0
  39. package/dist/labels/directory.d.ts +87 -0
  40. package/dist/labels/directory.js +140 -0
  41. package/dist/labels/personal.d.ts +17 -0
  42. package/dist/labels/personal.js +56 -0
  43. package/dist/labels/presentation.d.ts +154 -0
  44. package/dist/labels/presentation.js +90 -0
  45. package/dist/network/reader.d.ts +31 -0
  46. package/dist/network/reader.js +370 -0
  47. package/dist/network/retry.d.ts +7 -0
  48. package/dist/network/retry.js +53 -0
  49. package/dist/network/work.d.ts +3 -0
  50. package/dist/network/work.js +66 -0
  51. package/dist/pricing/market.d.ts +29 -0
  52. package/dist/pricing/market.js +150 -0
  53. package/dist/pricing/terms.d.ts +6 -0
  54. package/dist/pricing/terms.js +12 -0
  55. package/dist/spending/destination.d.ts +16 -0
  56. package/dist/spending/destination.js +42 -0
  57. package/dist/spending/journal.d.ts +28 -0
  58. package/dist/spending/journal.js +69 -0
  59. package/dist/spending/ledger.d.ts +118 -0
  60. package/dist/spending/ledger.js +444 -0
  61. package/dist/spending/payments.d.ts +67 -0
  62. package/dist/spending/payments.js +424 -0
  63. package/dist/spending/pool.d.ts +13 -0
  64. package/dist/spending/pool.js +38 -0
  65. package/dist/spending/reconcile.d.ts +18 -0
  66. package/dist/spending/reconcile.js +64 -0
  67. package/dist/storage/profiles.d.ts +13 -0
  68. package/dist/storage/profiles.js +86 -0
  69. package/dist/storage/secrets.d.ts +16 -0
  70. package/dist/storage/secrets.js +104 -0
  71. package/dist/storage/state.d.ts +32 -0
  72. package/dist/storage/state.js +255 -0
  73. package/dist/wallet/auto-receive.d.ts +57 -0
  74. package/dist/wallet/auto-receive.js +140 -0
  75. package/dist/wallet/defaults.d.ts +3 -0
  76. package/dist/wallet/defaults.js +26 -0
  77. package/dist/wallet/signing.d.ts +17 -0
  78. package/dist/wallet/signing.js +128 -0
  79. package/dist/wallet/types.d.ts +65 -0
  80. package/dist/wallet/types.js +1 -0
  81. package/dist/wallet/work.d.ts +29 -0
  82. package/dist/wallet/work.js +154 -0
  83. package/dist/watches/manager.d.ts +75 -0
  84. package/dist/watches/manager.js +197 -0
  85. package/node_modules/@attocash/commons-core/Kotlin-DateTime-library-kotlinx-datetime.mjs +6678 -0
  86. package/node_modules/@attocash/commons-core/Kotlin-DateTime-library-kotlinx-datetime.mjs.map +1 -0
  87. package/node_modules/@attocash/commons-core/LICENSE +28 -0
  88. package/node_modules/@attocash/commons-core/README.md +118 -0
  89. package/node_modules/@attocash/commons-core/commons-commons-core.d.mts +1036 -0
  90. package/node_modules/@attocash/commons-core/commons-commons-core.mjs +7701 -0
  91. package/node_modules/@attocash/commons-core/commons-commons-core.mjs.map +1 -0
  92. package/node_modules/@attocash/commons-core/kotlin-kotlin-stdlib.mjs +17681 -0
  93. package/node_modules/@attocash/commons-core/kotlin-kotlin-stdlib.mjs.map +1 -0
  94. package/node_modules/@attocash/commons-core/kotlin_org_jetbrains_kotlin_kotlin_dom_api_compat.mjs +4 -0
  95. package/node_modules/@attocash/commons-core/kotlin_org_jetbrains_kotlin_kotlin_dom_api_compat.mjs.map +1 -0
  96. package/node_modules/@attocash/commons-core/kotlinx-io-kotlinx-io-bytestring.mjs +209 -0
  97. package/node_modules/@attocash/commons-core/kotlinx-io-kotlinx-io-bytestring.mjs.map +1 -0
  98. package/node_modules/@attocash/commons-core/kotlinx-io-kotlinx-io-core.mjs +2284 -0
  99. package/node_modules/@attocash/commons-core/kotlinx-io-kotlinx-io-core.mjs.map +1 -0
  100. package/node_modules/@attocash/commons-core/kotlinx-serialization-kotlinx-serialization-core.mjs +6042 -0
  101. package/node_modules/@attocash/commons-core/kotlinx-serialization-kotlinx-serialization-core.mjs.map +1 -0
  102. package/node_modules/@attocash/commons-core/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
  103. package/node_modules/@attocash/commons-core/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
  104. package/node_modules/@attocash/commons-core/package.json +27 -0
  105. package/node_modules/@attocash/commons-node/LICENSE +28 -0
  106. package/node_modules/@attocash/commons-node/README.md +92 -0
  107. package/node_modules/@attocash/commons-node/commons-commons-node.d.mts +255 -0
  108. package/node_modules/@attocash/commons-node/commons-commons-node.mjs +2977 -0
  109. package/node_modules/@attocash/commons-node/commons-commons-node.mjs.map +1 -0
  110. package/node_modules/@attocash/commons-node/kotlin-logging.mjs +432 -0
  111. package/node_modules/@attocash/commons-node/kotlin-logging.mjs.map +1 -0
  112. package/node_modules/@attocash/commons-node/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
  113. package/node_modules/@attocash/commons-node/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
  114. package/node_modules/@attocash/commons-node/package.json +29 -0
  115. package/node_modules/@attocash/commons-node-remote/LICENSE +28 -0
  116. package/node_modules/@attocash/commons-node-remote/README.md +61 -0
  117. package/node_modules/@attocash/commons-node-remote/commons-commons-node-remote.d.mts +122 -0
  118. package/node_modules/@attocash/commons-node-remote/commons-commons-node-remote.mjs +1810 -0
  119. package/node_modules/@attocash/commons-node-remote/commons-commons-node-remote.mjs.map +1 -0
  120. package/node_modules/@attocash/commons-node-remote/kotlin-logging.mjs +432 -0
  121. package/node_modules/@attocash/commons-node-remote/kotlin-logging.mjs.map +1 -0
  122. package/node_modules/@attocash/commons-node-remote/kotlinx-browser.mjs +8 -0
  123. package/node_modules/@attocash/commons-node-remote/kotlinx-browser.mjs.map +1 -0
  124. package/node_modules/@attocash/commons-node-remote/kotlinx-serialization-kotlinx-serialization-json-io.mjs +4 -0
  125. package/node_modules/@attocash/commons-node-remote/kotlinx-serialization-kotlinx-serialization-json-io.mjs.map +1 -0
  126. package/node_modules/@attocash/commons-node-remote/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
  127. package/node_modules/@attocash/commons-node-remote/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
  128. package/node_modules/@attocash/commons-node-remote/package.json +31 -0
  129. package/node_modules/@attocash/commons-transport/LICENSE +28 -0
  130. package/node_modules/@attocash/commons-transport/README.md +5 -0
  131. package/node_modules/@attocash/commons-transport/commons-commons-transport.d.mts +64 -0
  132. package/node_modules/@attocash/commons-transport/commons-commons-transport.mjs +249 -0
  133. package/node_modules/@attocash/commons-transport/commons-commons-transport.mjs.map +1 -0
  134. package/node_modules/@attocash/commons-transport/kotlinx-atomicfu.mjs +263 -0
  135. package/node_modules/@attocash/commons-transport/kotlinx-atomicfu.mjs.map +1 -0
  136. package/node_modules/@attocash/commons-transport/kotlinx-browser.mjs +8 -0
  137. package/node_modules/@attocash/commons-transport/kotlinx-browser.mjs.map +1 -0
  138. package/node_modules/@attocash/commons-transport/kotlinx-coroutines-core.mjs +11604 -0
  139. package/node_modules/@attocash/commons-transport/kotlinx-coroutines-core.mjs.map +1 -0
  140. package/node_modules/@attocash/commons-transport/kotlinx-serialization-kotlinx-serialization-json-io.mjs +4 -0
  141. package/node_modules/@attocash/commons-transport/kotlinx-serialization-kotlinx-serialization-json-io.mjs.map +1 -0
  142. package/node_modules/@attocash/commons-transport/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
  143. package/node_modules/@attocash/commons-transport/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
  144. package/node_modules/@attocash/commons-transport/ktor-ktor-client-content-negotiation.mjs +499 -0
  145. package/node_modules/@attocash/commons-transport/ktor-ktor-client-content-negotiation.mjs.map +1 -0
  146. package/node_modules/@attocash/commons-transport/ktor-ktor-client-core.mjs +6456 -0
  147. package/node_modules/@attocash/commons-transport/ktor-ktor-client-core.mjs.map +1 -0
  148. package/node_modules/@attocash/commons-transport/ktor-ktor-events.mjs +83 -0
  149. package/node_modules/@attocash/commons-transport/ktor-ktor-events.mjs.map +1 -0
  150. package/node_modules/@attocash/commons-transport/ktor-ktor-http-cio.mjs +1714 -0
  151. package/node_modules/@attocash/commons-transport/ktor-ktor-http-cio.mjs.map +1 -0
  152. package/node_modules/@attocash/commons-transport/ktor-ktor-http.mjs +3197 -0
  153. package/node_modules/@attocash/commons-transport/ktor-ktor-http.mjs.map +1 -0
  154. package/node_modules/@attocash/commons-transport/ktor-ktor-io.mjs +2290 -0
  155. package/node_modules/@attocash/commons-transport/ktor-ktor-io.mjs.map +1 -0
  156. package/node_modules/@attocash/commons-transport/ktor-ktor-serialization-kotlinx-json.mjs +46 -0
  157. package/node_modules/@attocash/commons-transport/ktor-ktor-serialization-kotlinx-json.mjs.map +1 -0
  158. package/node_modules/@attocash/commons-transport/ktor-ktor-serialization-kotlinx.mjs +482 -0
  159. package/node_modules/@attocash/commons-transport/ktor-ktor-serialization-kotlinx.mjs.map +1 -0
  160. package/node_modules/@attocash/commons-transport/ktor-ktor-serialization.mjs +165 -0
  161. package/node_modules/@attocash/commons-transport/ktor-ktor-serialization.mjs.map +1 -0
  162. package/node_modules/@attocash/commons-transport/ktor-ktor-sse.mjs +4 -0
  163. package/node_modules/@attocash/commons-transport/ktor-ktor-sse.mjs.map +1 -0
  164. package/node_modules/@attocash/commons-transport/ktor-ktor-utils.mjs +3222 -0
  165. package/node_modules/@attocash/commons-transport/ktor-ktor-utils.mjs.map +1 -0
  166. package/node_modules/@attocash/commons-transport/ktor-ktor-websocket-serialization.mjs +4 -0
  167. package/node_modules/@attocash/commons-transport/ktor-ktor-websocket-serialization.mjs.map +1 -0
  168. package/node_modules/@attocash/commons-transport/ktor-ktor-websockets.mjs +1485 -0
  169. package/node_modules/@attocash/commons-transport/ktor-ktor-websockets.mjs.map +1 -0
  170. package/node_modules/@attocash/commons-transport/package.json +29 -0
  171. package/node_modules/@attocash/commons-worker/LICENSE +28 -0
  172. package/node_modules/@attocash/commons-worker/README.md +48 -0
  173. package/node_modules/@attocash/commons-worker/commons-commons-worker.d.mts +73 -0
  174. package/node_modules/@attocash/commons-worker/commons-commons-worker.mjs +702 -0
  175. package/node_modules/@attocash/commons-worker/commons-commons-worker.mjs.map +1 -0
  176. package/node_modules/@attocash/commons-worker/kotlin-logging.mjs +432 -0
  177. package/node_modules/@attocash/commons-worker/kotlin-logging.mjs.map +1 -0
  178. package/node_modules/@attocash/commons-worker/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
  179. package/node_modules/@attocash/commons-worker/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
  180. package/node_modules/@attocash/commons-worker/package.json +29 -0
  181. package/node_modules/@attocash/commons-worker-remote/LICENSE +28 -0
  182. package/node_modules/@attocash/commons-worker-remote/README.md +60 -0
  183. package/node_modules/@attocash/commons-worker-remote/commons-commons-worker-remote.d.mts +93 -0
  184. package/node_modules/@attocash/commons-worker-remote/commons-commons-worker-remote.mjs +289 -0
  185. package/node_modules/@attocash/commons-worker-remote/commons-commons-worker-remote.mjs.map +1 -0
  186. package/node_modules/@attocash/commons-worker-remote/kotlin-logging.mjs +432 -0
  187. package/node_modules/@attocash/commons-worker-remote/kotlin-logging.mjs.map +1 -0
  188. package/node_modules/@attocash/commons-worker-remote/kotlinx-browser.mjs +8 -0
  189. package/node_modules/@attocash/commons-worker-remote/kotlinx-browser.mjs.map +1 -0
  190. package/node_modules/@attocash/commons-worker-remote/kotlinx-serialization-kotlinx-serialization-json-io.mjs +4 -0
  191. package/node_modules/@attocash/commons-worker-remote/kotlinx-serialization-kotlinx-serialization-json-io.mjs.map +1 -0
  192. package/node_modules/@attocash/commons-worker-remote/kotlinx-serialization-kotlinx-serialization-json.mjs +5294 -0
  193. package/node_modules/@attocash/commons-worker-remote/kotlinx-serialization-kotlinx-serialization-json.mjs.map +1 -0
  194. package/node_modules/@attocash/commons-worker-remote/package.json +31 -0
  195. package/node_modules/@js-joda/core/CHANGELOG.md +363 -0
  196. package/node_modules/@js-joda/core/CheatSheet.md +947 -0
  197. package/node_modules/@js-joda/core/LICENSE +34 -0
  198. package/node_modules/@js-joda/core/README.md +155 -0
  199. package/node_modules/@js-joda/core/bower.json +21 -0
  200. package/node_modules/@js-joda/core/dist/js-joda.d.ts +2452 -0
  201. package/node_modules/@js-joda/core/dist/js-joda.esm.js +13564 -0
  202. package/node_modules/@js-joda/core/dist/js-joda.flow.js +949 -0
  203. package/node_modules/@js-joda/core/dist/js-joda.js +13630 -0
  204. package/node_modules/@js-joda/core/dist/js-joda.js.map +1 -0
  205. package/node_modules/@js-joda/core/dist/js-joda.min.js +5 -0
  206. package/node_modules/@js-joda/core/package.json +76 -0
  207. package/node_modules/@js-joda/core/src/Clock.js +330 -0
  208. package/node_modules/@js-joda/core/src/DayOfWeek.js +451 -0
  209. package/node_modules/@js-joda/core/src/Duration.js +1205 -0
  210. package/node_modules/@js-joda/core/src/Enum.js +30 -0
  211. package/node_modules/@js-joda/core/src/Instant.js +1012 -0
  212. package/node_modules/@js-joda/core/src/LocalDate.js +1703 -0
  213. package/node_modules/@js-joda/core/src/LocalDateTime.js +1686 -0
  214. package/node_modules/@js-joda/core/src/LocalTime.js +1415 -0
  215. package/node_modules/@js-joda/core/src/MathUtil.js +236 -0
  216. package/node_modules/@js-joda/core/src/Month.js +600 -0
  217. package/node_modules/@js-joda/core/src/MonthDay.js +730 -0
  218. package/node_modules/@js-joda/core/src/OffsetDateTime.js +757 -0
  219. package/node_modules/@js-joda/core/src/OffsetTime.js +621 -0
  220. package/node_modules/@js-joda/core/src/Period.js +933 -0
  221. package/node_modules/@js-joda/core/src/StringUtil.js +43 -0
  222. package/node_modules/@js-joda/core/src/Year.js +1015 -0
  223. package/node_modules/@js-joda/core/src/YearConstants.js +21 -0
  224. package/node_modules/@js-joda/core/src/YearMonth.js +1096 -0
  225. package/node_modules/@js-joda/core/src/ZoneId.js +236 -0
  226. package/node_modules/@js-joda/core/src/ZoneIdFactory.js +201 -0
  227. package/node_modules/@js-joda/core/src/ZoneOffset.js +486 -0
  228. package/node_modules/@js-joda/core/src/ZoneRegion.js +72 -0
  229. package/node_modules/@js-joda/core/src/ZonedDateTime.js +2007 -0
  230. package/node_modules/@js-joda/core/src/_init.js +68 -0
  231. package/node_modules/@js-joda/core/src/assert.js +33 -0
  232. package/node_modules/@js-joda/core/src/chrono/ChronoLocalDate.js +230 -0
  233. package/node_modules/@js-joda/core/src/chrono/ChronoLocalDateTime.js +130 -0
  234. package/node_modules/@js-joda/core/src/chrono/ChronoZonedDateTime.js +194 -0
  235. package/node_modules/@js-joda/core/src/chrono/IsoChronology.js +235 -0
  236. package/node_modules/@js-joda/core/src/convert.js +83 -0
  237. package/node_modules/@js-joda/core/src/errors.js +54 -0
  238. package/node_modules/@js-joda/core/src/format/DateTimeBuilder.js +535 -0
  239. package/node_modules/@js-joda/core/src/format/DateTimeFormatter.js +728 -0
  240. package/node_modules/@js-joda/core/src/format/DateTimeFormatterBuilder.js +1545 -0
  241. package/node_modules/@js-joda/core/src/format/DateTimeParseContext.js +297 -0
  242. package/node_modules/@js-joda/core/src/format/DateTimePrintContext.js +138 -0
  243. package/node_modules/@js-joda/core/src/format/DecimalStyle.js +113 -0
  244. package/node_modules/@js-joda/core/src/format/EnumMap.js +70 -0
  245. package/node_modules/@js-joda/core/src/format/ParsePosition.js +31 -0
  246. package/node_modules/@js-joda/core/src/format/ResolverStyle.js +93 -0
  247. package/node_modules/@js-joda/core/src/format/SignStyle.js +38 -0
  248. package/node_modules/@js-joda/core/src/format/StringBuilder.js +47 -0
  249. package/node_modules/@js-joda/core/src/format/TextStyle.js +114 -0
  250. package/node_modules/@js-joda/core/src/format/parser/CharLiteralPrinterParser.js +46 -0
  251. package/node_modules/@js-joda/core/src/format/parser/CompositePrinterParser.js +89 -0
  252. package/node_modules/@js-joda/core/src/format/parser/FractionPrinterParser.js +162 -0
  253. package/node_modules/@js-joda/core/src/format/parser/NumberPrinterParser.js +367 -0
  254. package/node_modules/@js-joda/core/src/format/parser/OffsetIdPrinterParser.js +177 -0
  255. package/node_modules/@js-joda/core/src/format/parser/PadPrinterParserDecorator.js +81 -0
  256. package/node_modules/@js-joda/core/src/format/parser/SettingsParser.js +44 -0
  257. package/node_modules/@js-joda/core/src/format/parser/StringLiteralPrinterParser.js +39 -0
  258. package/node_modules/@js-joda/core/src/format/parser/ZoneIdPrinterParser.js +218 -0
  259. package/node_modules/@js-joda/core/src/js-joda.js +210 -0
  260. package/node_modules/@js-joda/core/src/license-preamble.js +3 -0
  261. package/node_modules/@js-joda/core/src/temporal/ChronoField.js +470 -0
  262. package/node_modules/@js-joda/core/src/temporal/ChronoUnit.js +310 -0
  263. package/node_modules/@js-joda/core/src/temporal/DefaultInterfaceTemporal.js +118 -0
  264. package/node_modules/@js-joda/core/src/temporal/IsoFields.js +859 -0
  265. package/node_modules/@js-joda/core/src/temporal/NativeJsTemporal.js +105 -0
  266. package/node_modules/@js-joda/core/src/temporal/Temporal.js +346 -0
  267. package/node_modules/@js-joda/core/src/temporal/TemporalAccessor.js +125 -0
  268. package/node_modules/@js-joda/core/src/temporal/TemporalAdjuster.js +92 -0
  269. package/node_modules/@js-joda/core/src/temporal/TemporalAdjusters.js +464 -0
  270. package/node_modules/@js-joda/core/src/temporal/TemporalAmount.js +166 -0
  271. package/node_modules/@js-joda/core/src/temporal/TemporalField.js +248 -0
  272. package/node_modules/@js-joda/core/src/temporal/TemporalQueries.js +223 -0
  273. package/node_modules/@js-joda/core/src/temporal/TemporalQueriesFactory.js +77 -0
  274. package/node_modules/@js-joda/core/src/temporal/TemporalQuery.js +107 -0
  275. package/node_modules/@js-joda/core/src/temporal/TemporalUnit.js +179 -0
  276. package/node_modules/@js-joda/core/src/temporal/ValueRange.js +257 -0
  277. package/node_modules/@js-joda/core/src/use.js +20 -0
  278. package/node_modules/@js-joda/core/src/zone/SystemDefaultZoneId.js +31 -0
  279. package/node_modules/@js-joda/core/src/zone/SystemDefaultZoneRules.js +162 -0
  280. package/node_modules/@js-joda/core/src/zone/ZoneOffsetTransition.js +289 -0
  281. package/node_modules/@js-joda/core/src/zone/ZoneRules.js +464 -0
  282. package/node_modules/@js-joda/core/src/zone/ZoneRulesProvider.js +36 -0
  283. package/node_modules/@noble/hashes/LICENSE +21 -0
  284. package/node_modules/@noble/hashes/README.md +563 -0
  285. package/node_modules/@noble/hashes/_blake.d.ts +21 -0
  286. package/node_modules/@noble/hashes/_blake.d.ts.map +1 -0
  287. package/node_modules/@noble/hashes/_blake.js +52 -0
  288. package/node_modules/@noble/hashes/_blake.js.map +1 -0
  289. package/node_modules/@noble/hashes/_md.d.ts +99 -0
  290. package/node_modules/@noble/hashes/_md.d.ts.map +1 -0
  291. package/node_modules/@noble/hashes/_md.js +202 -0
  292. package/node_modules/@noble/hashes/_md.js.map +1 -0
  293. package/node_modules/@noble/hashes/_u64.d.ts +63 -0
  294. package/node_modules/@noble/hashes/_u64.d.ts.map +1 -0
  295. package/node_modules/@noble/hashes/_u64.js +84 -0
  296. package/node_modules/@noble/hashes/_u64.js.map +1 -0
  297. package/node_modules/@noble/hashes/argon2.d.ts +112 -0
  298. package/node_modules/@noble/hashes/argon2.d.ts.map +1 -0
  299. package/node_modules/@noble/hashes/argon2.js +517 -0
  300. package/node_modules/@noble/hashes/argon2.js.map +1 -0
  301. package/node_modules/@noble/hashes/blake1.d.ts +156 -0
  302. package/node_modules/@noble/hashes/blake1.d.ts.map +1 -0
  303. package/node_modules/@noble/hashes/blake1.js +560 -0
  304. package/node_modules/@noble/hashes/blake1.js.map +1 -0
  305. package/node_modules/@noble/hashes/blake2.d.ts +184 -0
  306. package/node_modules/@noble/hashes/blake2.d.ts.map +1 -0
  307. package/node_modules/@noble/hashes/blake2.js +501 -0
  308. package/node_modules/@noble/hashes/blake2.js.map +1 -0
  309. package/node_modules/@noble/hashes/blake3.d.ts +68 -0
  310. package/node_modules/@noble/hashes/blake3.d.ts.map +1 -0
  311. package/node_modules/@noble/hashes/blake3.js +284 -0
  312. package/node_modules/@noble/hashes/blake3.js.map +1 -0
  313. package/node_modules/@noble/hashes/eskdf.d.ts +92 -0
  314. package/node_modules/@noble/hashes/eskdf.d.ts.map +1 -0
  315. package/node_modules/@noble/hashes/eskdf.js +218 -0
  316. package/node_modules/@noble/hashes/eskdf.js.map +1 -0
  317. package/node_modules/@noble/hashes/hkdf.d.ts +64 -0
  318. package/node_modules/@noble/hashes/hkdf.d.ts.map +1 -0
  319. package/node_modules/@noble/hashes/hkdf.js +120 -0
  320. package/node_modules/@noble/hashes/hkdf.js.map +1 -0
  321. package/node_modules/@noble/hashes/hmac.d.ts +47 -0
  322. package/node_modules/@noble/hashes/hmac.d.ts.map +1 -0
  323. package/node_modules/@noble/hashes/hmac.js +93 -0
  324. package/node_modules/@noble/hashes/hmac.js.map +1 -0
  325. package/node_modules/@noble/hashes/index.d.ts +2 -0
  326. package/node_modules/@noble/hashes/index.d.ts.map +1 -0
  327. package/node_modules/@noble/hashes/index.js +35 -0
  328. package/node_modules/@noble/hashes/index.js.map +1 -0
  329. package/node_modules/@noble/hashes/legacy.d.ts +102 -0
  330. package/node_modules/@noble/hashes/legacy.d.ts.map +1 -0
  331. package/node_modules/@noble/hashes/legacy.js +327 -0
  332. package/node_modules/@noble/hashes/legacy.js.map +1 -0
  333. package/node_modules/@noble/hashes/package.json +97 -0
  334. package/node_modules/@noble/hashes/pbkdf2.d.ts +56 -0
  335. package/node_modules/@noble/hashes/pbkdf2.d.ts.map +1 -0
  336. package/node_modules/@noble/hashes/pbkdf2.js +135 -0
  337. package/node_modules/@noble/hashes/pbkdf2.js.map +1 -0
  338. package/node_modules/@noble/hashes/scrypt.d.ts +65 -0
  339. package/node_modules/@noble/hashes/scrypt.d.ts.map +1 -0
  340. package/node_modules/@noble/hashes/scrypt.js +257 -0
  341. package/node_modules/@noble/hashes/scrypt.js.map +1 -0
  342. package/node_modules/@noble/hashes/sha2.d.ts +249 -0
  343. package/node_modules/@noble/hashes/sha2.d.ts.map +1 -0
  344. package/node_modules/@noble/hashes/sha2.js +458 -0
  345. package/node_modules/@noble/hashes/sha2.js.map +1 -0
  346. package/node_modules/@noble/hashes/sha3-addons.d.ts +461 -0
  347. package/node_modules/@noble/hashes/sha3-addons.d.ts.map +1 -0
  348. package/node_modules/@noble/hashes/sha3-addons.js +783 -0
  349. package/node_modules/@noble/hashes/sha3-addons.js.map +1 -0
  350. package/node_modules/@noble/hashes/sha3.d.ts +199 -0
  351. package/node_modules/@noble/hashes/sha3.d.ts.map +1 -0
  352. package/node_modules/@noble/hashes/sha3.js +431 -0
  353. package/node_modules/@noble/hashes/sha3.js.map +1 -0
  354. package/node_modules/@noble/hashes/src/_blake.ts +57 -0
  355. package/node_modules/@noble/hashes/src/_md.ts +222 -0
  356. package/node_modules/@noble/hashes/src/_u64.ts +117 -0
  357. package/node_modules/@noble/hashes/src/argon2.ts +667 -0
  358. package/node_modules/@noble/hashes/src/blake1.ts +614 -0
  359. package/node_modules/@noble/hashes/src/blake2.ts +582 -0
  360. package/node_modules/@noble/hashes/src/blake3.ts +317 -0
  361. package/node_modules/@noble/hashes/src/eskdf.ts +259 -0
  362. package/node_modules/@noble/hashes/src/hkdf.ts +133 -0
  363. package/node_modules/@noble/hashes/src/hmac.ts +126 -0
  364. package/node_modules/@noble/hashes/src/index.ts +33 -0
  365. package/node_modules/@noble/hashes/src/legacy.ts +339 -0
  366. package/node_modules/@noble/hashes/src/pbkdf2.ts +175 -0
  367. package/node_modules/@noble/hashes/src/scrypt.ts +315 -0
  368. package/node_modules/@noble/hashes/src/sha2.ts +530 -0
  369. package/node_modules/@noble/hashes/src/sha3-addons.ts +983 -0
  370. package/node_modules/@noble/hashes/src/sha3.ts +485 -0
  371. package/node_modules/@noble/hashes/src/utils.ts +848 -0
  372. package/node_modules/@noble/hashes/src/webcrypto.ts +239 -0
  373. package/node_modules/@noble/hashes/utils.d.ts +519 -0
  374. package/node_modules/@noble/hashes/utils.d.ts.map +1 -0
  375. package/node_modules/@noble/hashes/utils.js +578 -0
  376. package/node_modules/@noble/hashes/utils.js.map +1 -0
  377. package/node_modules/@noble/hashes/webcrypto.d.ts +120 -0
  378. package/node_modules/@noble/hashes/webcrypto.d.ts.map +1 -0
  379. package/node_modules/@noble/hashes/webcrypto.js +165 -0
  380. package/node_modules/@noble/hashes/webcrypto.js.map +1 -0
  381. package/node_modules/ws/LICENSE +20 -0
  382. package/node_modules/ws/README.md +548 -0
  383. package/node_modules/ws/browser.js +8 -0
  384. package/node_modules/ws/index.js +22 -0
  385. package/node_modules/ws/lib/buffer-util.js +131 -0
  386. package/node_modules/ws/lib/constants.js +19 -0
  387. package/node_modules/ws/lib/event-target.js +292 -0
  388. package/node_modules/ws/lib/extension.js +203 -0
  389. package/node_modules/ws/lib/limiter.js +55 -0
  390. package/node_modules/ws/lib/permessage-deflate.js +530 -0
  391. package/node_modules/ws/lib/receiver.js +743 -0
  392. package/node_modules/ws/lib/sender.js +607 -0
  393. package/node_modules/ws/lib/stream.js +161 -0
  394. package/node_modules/ws/lib/subprotocol.js +62 -0
  395. package/node_modules/ws/lib/validation.js +152 -0
  396. package/node_modules/ws/lib/websocket-server.js +562 -0
  397. package/node_modules/ws/lib/websocket.js +1407 -0
  398. package/node_modules/ws/package.json +74 -0
  399. package/node_modules/ws/wrapper.mjs +21 -0
  400. package/package.json +53 -0
@@ -0,0 +1,128 @@
1
+ import { createRequire } from 'node:module';
2
+ import { AttoMnemonic, AttoTransaction, attoAccountSend, attoAccountReceive, attoAccountOpen, attoAccountChange, privateKeyToSigner, toAttoIndex, toSeedAsync, } from '@attocash/commons-core';
3
+ import { AttoNodeClientAsyncBuilder } from '@attocash/commons-node-remote';
4
+ import { AttoWorkerAsyncBuilder } from '@attocash/commons-worker-remote';
5
+ import { AttoError } from '../domain/errors.js';
6
+ import { NodeReader } from '../network/reader.js';
7
+ import { retryNetwork } from '../network/retry.js';
8
+ // Ktor's Node transport calls require even when the package is imported as ESM.
9
+ Object.assign(globalThis, { require: createRequire(import.meta.url) });
10
+ export async function mnemonicSeed(phrase) {
11
+ const normalized = phrase.normalize('NFKD').trim().replace(/\s+/g, ' ');
12
+ try {
13
+ if (normalized.split(' ').length !== 24)
14
+ throw new Error();
15
+ return await toSeedAsync(await AttoMnemonic.fromPhrase(normalized));
16
+ }
17
+ catch {
18
+ throw new AttoError('INVALID_MNEMONIC', 'Enter a valid 24-word Atto mnemonic.');
19
+ }
20
+ }
21
+ export async function derivedSigner(seed, index) {
22
+ if (!Number.isSafeInteger(index) || index < 0 || index > 0x7fff_ffff) {
23
+ throw new AttoError('INVALID_INDEX', 'Key index must be between 0 and 2147483647.');
24
+ }
25
+ return privateKeyToSigner(await seed.toPrivateKey(toAttoIndex(index)));
26
+ }
27
+ export async function signingWallet(seed, index, settings, beforeSign, suppliedWorker, retry) {
28
+ const client = new AttoNodeClientAsyncBuilder(settings.nodeUrl).build();
29
+ const worker = suppliedWorker ?? new AttoWorkerAsyncBuilder(settings.workerUrl).cached(false).timeoutSeconds(60n).build();
30
+ let signer = await derivedSigner(seed, index);
31
+ const address = signer.address;
32
+ const found = await retryNetwork(() => client.accountByAddresses([address]), retry);
33
+ for (const value of found) {
34
+ if (value.address.value !== address.value)
35
+ throw new AttoError('ACCOUNT_MISMATCH', 'The node returned a different wallet account.');
36
+ if (value.network.name !== settings.network)
37
+ throw new AttoError('NETWORK_MISMATCH', 'Account network differs from the configured wallet network.');
38
+ }
39
+ if (found.length > 1)
40
+ throw new AttoError('ACCOUNT_MISMATCH', 'The node returned duplicate wallet accounts.');
41
+ let account = found[0];
42
+ let queue = Promise.resolve();
43
+ function requireIndex(value) {
44
+ if (value.toInt() !== index)
45
+ throw new AttoError('INVALID_INDEX', 'This signing operation belongs to a different account.');
46
+ }
47
+ function requireAccount() {
48
+ if (!account)
49
+ throw new AttoError('ACCOUNT_NOT_OPEN', 'Receive funds to open this account before sending.');
50
+ return account;
51
+ }
52
+ async function perform(build, timestamp) {
53
+ const previous = queue;
54
+ let release;
55
+ queue = new Promise(resolve => { release = resolve; });
56
+ await previous;
57
+ try {
58
+ if (!signer)
59
+ throw new AttoError('WALLET_CLOSED', 'This signing operation is closed.');
60
+ const update = build((timestamp ?? await retryNetwork(() => client.now(), retry)).toString());
61
+ const block = update.block;
62
+ if (block.address.value !== address.value)
63
+ throw new AttoError('ACCOUNT_MISMATCH', 'The constructed block belongs to a different wallet account.');
64
+ if (block.network.name !== settings.network)
65
+ throw new AttoError('NETWORK_MISMATCH', 'Account network differs from the configured wallet network.');
66
+ if (!block.isValid())
67
+ throw new AttoError('INVALID_TRANSACTION', 'The constructed transaction is invalid.');
68
+ await beforeSign(block);
69
+ const signature = await signer.signBlock(block);
70
+ const work = await retryNetwork(() => suppliedWorker ? suppliedWorker.workBlock(block, retry?.signal) : worker.workBlock(block), retry);
71
+ const transaction = new AttoTransaction(block, signature, work);
72
+ if (!await transaction.isValid())
73
+ throw new AttoError('INVALID_TRANSACTION', 'The signed transaction is invalid.');
74
+ let attempted = false;
75
+ await retryNetwork(async () => {
76
+ if (attempted) {
77
+ const confirmed = await new NodeReader(settings).transaction(transaction.hash.toString(), retry?.signal);
78
+ if (confirmed) {
79
+ if (confirmed.hash.toString() !== transaction.hash.toString() || !await confirmed.isValid()) {
80
+ throw new AttoError('INVALID_NODE_RESPONSE', 'The node returned a different or invalid transaction.');
81
+ }
82
+ return;
83
+ }
84
+ }
85
+ // Approval may have changed while waiting for work or during backoff.
86
+ // Recheck that the journal still permits this exact block.
87
+ if (retry)
88
+ await beforeSign(block);
89
+ if (retry?.signal.aborted)
90
+ throw new AttoError('CANCELLED', 'Send cancelled.');
91
+ attempted = true;
92
+ await client.publish(transaction);
93
+ }, retry);
94
+ account = update.account;
95
+ return transaction;
96
+ }
97
+ finally {
98
+ release();
99
+ }
100
+ }
101
+ // Commons' wallet builder calls its private Kotlin worker interface directly.
102
+ // Compose its public block, signing, validation, and publication APIs so the
103
+ // persistent JS work cache has an explicit supported boundary.
104
+ const wallet = {
105
+ async sendByIndex(value, destination, amount, timestamp) {
106
+ requireIndex(value);
107
+ return perform(time => attoAccountSend(requireAccount(), destination, amount, time), timestamp);
108
+ },
109
+ async receive(receivable, representative, timestamp) {
110
+ if (receivable.receiverAddress.value !== address.value)
111
+ throw new AttoError('WRONG_RECEIVER', 'The payment belongs to a different address.');
112
+ return perform(time => {
113
+ if (account)
114
+ return attoAccountReceive(account, receivable, time);
115
+ if (!representative)
116
+ throw new AttoError('REPRESENTATIVE_REQUIRED', 'Choose a representative to open this account.');
117
+ return attoAccountOpen(representative, receivable, time);
118
+ }, timestamp);
119
+ },
120
+ async change(value, representative, timestamp) {
121
+ requireIndex(value);
122
+ return perform(time => attoAccountChange(requireAccount(), representative, time), timestamp);
123
+ },
124
+ async getAccountByIndex(value) { requireIndex(value); return account ?? null; },
125
+ close() { signer = undefined; },
126
+ };
127
+ return { wallet, client, address };
128
+ }
@@ -0,0 +1,65 @@
1
+ import type { PaymentMetadata } from '../spending/journal.js';
2
+ export type NetworkName = 'LIVE' | 'BETA' | 'DEV' | 'LOCAL';
3
+ export type AmountUnit = 'ATTO' | 'RAW';
4
+ export interface SpendingRule {
5
+ days: number;
6
+ amount: string;
7
+ unit: AmountUnit;
8
+ }
9
+ export interface SpendingPolicy {
10
+ perRequest: {
11
+ amount: string;
12
+ unit: AmountUnit;
13
+ } | null;
14
+ rolling: SpendingRule[];
15
+ }
16
+ export interface AccountPool {
17
+ indexes: number[];
18
+ consolidate: boolean;
19
+ }
20
+ export interface WalletSettings {
21
+ network: NetworkName;
22
+ nodeUrl: string;
23
+ workerUrl: string;
24
+ representative: string;
25
+ autoReceive: boolean;
26
+ minReceiveRaw: string;
27
+ }
28
+ export interface WalletAddress {
29
+ index: number;
30
+ address: string;
31
+ publicKey: string;
32
+ active: boolean;
33
+ }
34
+ export interface WalletIdentity {
35
+ fingerprint: string;
36
+ address: string;
37
+ }
38
+ export type EventKind = 'account' | 'transaction' | 'entry' | 'receivable';
39
+ export interface StreamFilter {
40
+ event: EventKind;
41
+ addresses?: string[];
42
+ hash?: string;
43
+ fromHeight?: string;
44
+ toHeight?: string;
45
+ minAmountRaw?: string;
46
+ }
47
+ export interface ListRequest extends StreamFilter {
48
+ limit?: number;
49
+ timeoutMs?: number;
50
+ cursor?: string;
51
+ }
52
+ export interface SendRequest {
53
+ index?: number;
54
+ destination?: string;
55
+ destinationLabel?: string;
56
+ amount: string;
57
+ unit?: AmountUnit | 'USD';
58
+ requestId: string;
59
+ metadata?: PaymentMetadata;
60
+ }
61
+ export interface ReceiveRequest {
62
+ index?: number;
63
+ hash: string;
64
+ representative?: string;
65
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { AttoAccount, AttoBlock, AttoWork } from '@attocash/commons-core';
2
+ import type { StateStore } from '../storage/state.js';
3
+ import type { WalletSettings } from './types.js';
4
+ export interface BlockWorker {
5
+ workBlock(block: AttoBlock, signal?: AbortSignal): Promise<AttoWork>;
6
+ close(): void;
7
+ }
8
+ /** Public work only: no signer, mnemonic, or seed enters this owner. */
9
+ export declare class WalletWork {
10
+ private readonly store;
11
+ private readonly settings;
12
+ private readonly queued;
13
+ private readonly pending;
14
+ private readonly requests;
15
+ private backgroundCount;
16
+ private closed;
17
+ constructor(store: StateStore, settings: () => WalletSettings);
18
+ isReady(account: AttoAccount): boolean;
19
+ prepare(accounts: readonly AttoAccount[]): void;
20
+ worker(): BlockWorker;
21
+ close(): Promise<void>;
22
+ private nextBlock;
23
+ private key;
24
+ private read;
25
+ private save;
26
+ private obtain;
27
+ private compute;
28
+ private drain;
29
+ }
@@ -0,0 +1,154 @@
1
+ import { AttoInstant, AttoWork, attoAccountChange, attoBlockWorkTarget, } from '@attocash/commons-core';
2
+ import { requestWork } from '../network/work.js';
3
+ import { AttoError } from '../domain/errors.js';
4
+ const BACKGROUND_LIMIT = 2;
5
+ const QUEUE_LIMIT = 100;
6
+ /** Public work only: no signer, mnemonic, or seed enters this owner. */
7
+ export class WalletWork {
8
+ store;
9
+ settings;
10
+ queued = new Map();
11
+ pending = new Map();
12
+ requests = new Set();
13
+ backgroundCount = 0;
14
+ closed = false;
15
+ constructor(store, settings) {
16
+ this.store = store;
17
+ this.settings = settings;
18
+ }
19
+ isReady(account) {
20
+ if (this.closed || account.network.name !== this.settings().network)
21
+ return false;
22
+ return this.read(this.nextBlock(account)) !== undefined;
23
+ }
24
+ prepare(accounts) {
25
+ if (this.closed)
26
+ return;
27
+ for (const account of accounts) {
28
+ if (account.network.name !== this.settings().network)
29
+ continue;
30
+ const block = this.nextBlock(account);
31
+ if (this.read(block))
32
+ continue;
33
+ const key = this.key(block);
34
+ if (this.queued.size < QUEUE_LIMIT || this.queued.has(key))
35
+ this.queued.set(key, block);
36
+ }
37
+ this.drain();
38
+ }
39
+ worker() {
40
+ // A signing operation borrows this cache; only the application closes it.
41
+ return { workBlock: (block, signal) => this.obtain(block, false, signal), close() { } };
42
+ }
43
+ async close() {
44
+ this.closed = true;
45
+ this.queued.clear();
46
+ for (const request of this.requests)
47
+ request.abort();
48
+ await Promise.allSettled([...this.pending.values()].map(job => job.promise));
49
+ }
50
+ nextBlock(account) {
51
+ // Commons constructs the next-head target and validates its own work. This
52
+ // unsigned block is only a work request; it is never signed or published.
53
+ return attoAccountChange(account, account.representativeAddress, AttoInstant.Companion.now().toString()).block;
54
+ }
55
+ key(block) { return `work.${block.network.name}.${block.publicKey.toString()}`; }
56
+ read(block) {
57
+ try {
58
+ const record = this.store.get(this.key(block));
59
+ if (!record || record.target !== attoBlockWorkTarget(block) || !/^[0-9a-f]{16}$/i.test(record.work))
60
+ return;
61
+ const work = AttoWork.Companion.parse(record.work);
62
+ return work.isValid(block) ? work : undefined;
63
+ }
64
+ catch {
65
+ return undefined;
66
+ }
67
+ }
68
+ save(block, work) {
69
+ if (this.closed || block.network.name !== this.settings().network)
70
+ return;
71
+ const key = this.key(block);
72
+ this.store.transaction(() => {
73
+ const prior = this.store.get(key);
74
+ // An older in-flight job must not displace work for a newer account head,
75
+ // including work another CLI/MCP process has already persisted.
76
+ if (prior && /^\d+$/.test(prior.height) && BigInt(prior.height) > BigInt(block.height.toString()))
77
+ return;
78
+ this.store.set(key, { target: attoBlockWorkTarget(block), height: block.height.toString(), work: work.toString() });
79
+ });
80
+ }
81
+ async obtain(block, speculative = false, signal) {
82
+ if (this.closed)
83
+ throw new AttoError('WORK_CLOSED', 'The wallet work cache is closed.');
84
+ const settings = this.settings();
85
+ if (block.network.name !== settings.network)
86
+ throw new AttoError('NETWORK_MISMATCH', 'Work must use the configured wallet network.');
87
+ const ready = this.read(block);
88
+ if (ready)
89
+ return ready;
90
+ const key = `${this.key(block)}.${attoBlockWorkTarget(block)}`;
91
+ const existing = this.pending.get(key);
92
+ if (existing) {
93
+ try {
94
+ const work = await existing.promise;
95
+ // A job begun before a threshold change may no longer satisfy this block.
96
+ if (work.isValid(block))
97
+ return work;
98
+ }
99
+ catch (error) {
100
+ // Speculation has a shorter deadline. A real transaction retains the
101
+ // normal worker budget if that earlier background attempt failed.
102
+ if (speculative || !existing.speculative)
103
+ throw error;
104
+ }
105
+ }
106
+ if (this.closed)
107
+ throw new AttoError('WORK_CLOSED', 'The wallet work cache is closed.');
108
+ const job = { promise: this.compute(block, settings.workerUrl, speculative ? 10 : 60, signal), speculative };
109
+ this.pending.set(key, job);
110
+ try {
111
+ return await job.promise;
112
+ }
113
+ finally {
114
+ if (this.pending.get(key) === job)
115
+ this.pending.delete(key);
116
+ }
117
+ }
118
+ async compute(block, url, timeoutSeconds, signal) {
119
+ const controller = new AbortController();
120
+ this.requests.add(controller);
121
+ const timer = setTimeout(() => controller.abort(), timeoutSeconds * 1000);
122
+ try {
123
+ const work = await requestWork(block, url, AbortSignal.any([controller.signal, ...(signal ? [signal] : [])]));
124
+ this.save(block, work);
125
+ return work;
126
+ }
127
+ catch (error) {
128
+ if (signal?.aborted)
129
+ throw new AttoError('CANCELLED', 'Send cancelled.');
130
+ if (this.closed)
131
+ throw new AttoError('WORK_CLOSED', 'The wallet work cache is closed.');
132
+ if (controller.signal.aborted)
133
+ throw new AttoError('WORK_TIMEOUT', 'Proof-of-work generation timed out.');
134
+ if (error instanceof AttoError)
135
+ throw error;
136
+ throw new AttoError('WORK_FAILED', 'Proof-of-work generation failed. Check the worker endpoint.');
137
+ }
138
+ finally {
139
+ clearTimeout(timer);
140
+ this.requests.delete(controller);
141
+ }
142
+ }
143
+ drain() {
144
+ while (!this.closed && this.backgroundCount < BACKGROUND_LIMIT && this.queued.size > 0) {
145
+ const [key, block] = this.queued.entries().next().value;
146
+ this.queued.delete(key);
147
+ this.backgroundCount++;
148
+ void this.obtain(block, true).catch(() => { }).finally(() => {
149
+ this.backgroundCount--;
150
+ this.drain();
151
+ });
152
+ }
153
+ }
154
+ }
@@ -0,0 +1,75 @@
1
+ import { NodeReader } from '../network/reader.js';
2
+ import type { StreamFilter } from '../wallet/types.js';
3
+ interface Checkpoints {
4
+ get<T>(key: string): T | undefined;
5
+ set(key: string, value: unknown): void;
6
+ }
7
+ interface WatchOptions {
8
+ backoffMs?: number;
9
+ maxBackoffMs?: number;
10
+ retention?: number;
11
+ maxWatches?: number;
12
+ }
13
+ /** Session watches retain events until read; acknowledged heights survive process restarts. */
14
+ export declare class WatchManager {
15
+ private readonly reader;
16
+ private readonly checkpoints?;
17
+ private readonly watches;
18
+ private readonly backoffMs;
19
+ private readonly maxBackoffMs;
20
+ private readonly retention;
21
+ private readonly maxWatches;
22
+ private closed;
23
+ constructor(reader: NodeReader, checkpoints?: Checkpoints | undefined, options?: WatchOptions);
24
+ start(input: StreamFilter): {
25
+ lastError?: {
26
+ code: string;
27
+ message: string;
28
+ } | undefined;
29
+ id: string;
30
+ filter: StreamFilter;
31
+ status: "running" | "reconnecting" | "stopped" | "completed";
32
+ createdAt: string;
33
+ reconnects: number;
34
+ replayable: boolean;
35
+ };
36
+ list(): {
37
+ lastError?: {
38
+ code: string;
39
+ message: string;
40
+ } | undefined;
41
+ id: string;
42
+ filter: StreamFilter;
43
+ status: "running" | "reconnecting" | "stopped" | "completed";
44
+ createdAt: string;
45
+ reconnects: number;
46
+ replayable: boolean;
47
+ }[];
48
+ read(id: string, cursor?: number, limit?: number): {
49
+ events: {
50
+ cursor: number;
51
+ data: unknown;
52
+ }[];
53
+ nextCursor: number;
54
+ gapDetected: boolean;
55
+ oldestCursor: number;
56
+ latestCursor: number;
57
+ lastError?: {
58
+ code: string;
59
+ message: string;
60
+ } | undefined;
61
+ id: string;
62
+ filter: StreamFilter;
63
+ status: "running" | "reconnecting" | "stopped" | "completed";
64
+ createdAt: string;
65
+ reconnects: number;
66
+ replayable: boolean;
67
+ };
68
+ stop(id: string): Promise<void>;
69
+ close(): Promise<void>;
70
+ private get;
71
+ private describe;
72
+ private append;
73
+ private run;
74
+ }
75
+ export {};
@@ -0,0 +1,197 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { AttoError, errorResult } from '../domain/errors.js';
3
+ import { eventPosition, isReplayable, normalizeFilter, publicModel } from '../network/reader.js';
4
+ function sleep(milliseconds, signal) {
5
+ if (signal.aborted)
6
+ return Promise.resolve();
7
+ return new Promise(resolve => {
8
+ const finish = () => { clearTimeout(timer); signal.removeEventListener('abort', finish); resolve(); };
9
+ const timer = setTimeout(finish, milliseconds);
10
+ signal.addEventListener('abort', finish, { once: true });
11
+ });
12
+ }
13
+ /** Session watches retain events until read; acknowledged heights survive process restarts. */
14
+ export class WatchManager {
15
+ reader;
16
+ checkpoints;
17
+ watches = new Map();
18
+ backoffMs;
19
+ maxBackoffMs;
20
+ retention;
21
+ maxWatches;
22
+ closed = false;
23
+ constructor(reader, checkpoints, options = {}) {
24
+ this.reader = reader;
25
+ this.checkpoints = checkpoints;
26
+ this.backoffMs = options.backoffMs ?? 1000;
27
+ this.maxBackoffMs = options.maxBackoffMs ?? 30_000;
28
+ this.retention = options.retention ?? 1000;
29
+ this.maxWatches = options.maxWatches ?? 32;
30
+ if (![this.backoffMs, this.maxBackoffMs, this.retention, this.maxWatches].every(value => Number.isInteger(value) && value > 0)) {
31
+ throw new AttoError('INVALID_WATCH_OPTIONS', 'Watch limits and backoff values must be positive integers.');
32
+ }
33
+ }
34
+ start(input) {
35
+ if (this.closed)
36
+ throw new AttoError('WATCH_MANAGER_CLOSED', 'This watch session has closed.');
37
+ if (this.watches.size >= this.maxWatches) {
38
+ const finished = [...this.watches.values()].find(watch => watch.status === 'stopped' || watch.status === 'completed');
39
+ if (finished)
40
+ this.watches.delete(finished.id);
41
+ else
42
+ throw new AttoError('WATCH_LIMIT', 'The session watch limit has been reached.');
43
+ }
44
+ const filter = normalizeFilter(input);
45
+ const checkpointKey = `watch:${createHash('sha256').update(JSON.stringify([this.reader.settings.nodeUrl, this.reader.settings.network, filter])).digest('hex')}`;
46
+ const stored = isReplayable(filter) ? this.checkpoints?.get(checkpointKey) : undefined;
47
+ const acknowledged = {};
48
+ for (const address of filter.addresses ?? []) {
49
+ const value = stored?.[address];
50
+ if (typeof value === 'string' && /^\d+$/.test(value) && BigInt(value) < 18446744073709551616n)
51
+ acknowledged[address] = value;
52
+ }
53
+ const watch = {
54
+ id: randomUUID(), filter, status: 'running', createdAt: new Date().toISOString(), reconnects: 0,
55
+ controller: new AbortController(), task: Promise.resolve(), events: [], sequence: 0,
56
+ acknowledged, observed: { ...acknowledged }, checkpointKey, seen: new Set(),
57
+ };
58
+ this.watches.set(watch.id, watch);
59
+ const filters = isReplayable(filter) ? filter.addresses.map(address => ({ ...filter, addresses: [address] })) : [filter];
60
+ watch.task = Promise.all(filters.map(part => this.run(watch, part))).then(() => {
61
+ if (!watch.controller.signal.aborted)
62
+ watch.status = 'completed';
63
+ });
64
+ return this.describe(watch);
65
+ }
66
+ list() { return [...this.watches.values()].map(watch => this.describe(watch)); }
67
+ read(id, cursor = 0, limit = 100) {
68
+ const watch = this.get(id);
69
+ if (!Number.isSafeInteger(cursor) || cursor < 0 || cursor > watch.sequence || !Number.isInteger(limit) || limit < 1 || limit > 1000) {
70
+ throw new AttoError('INVALID_WATCH_CURSOR', 'Use an existing nonnegative cursor and a limit from 1 to 1000.');
71
+ }
72
+ // A supplied cursor acknowledges a prior page. Saving on arrival would lose unread
73
+ // events when a process exits between receiving an event and returning its page.
74
+ let checkpointChanged = false;
75
+ for (const event of watch.events) {
76
+ if (event.cursor > cursor)
77
+ break;
78
+ const position = event.position;
79
+ if (position && BigInt(position.height) > BigInt(watch.acknowledged[position.address] ?? '0')) {
80
+ watch.acknowledged[position.address] = position.height;
81
+ checkpointChanged = true;
82
+ }
83
+ }
84
+ if (checkpointChanged)
85
+ this.checkpoints?.set(watch.checkpointKey, { ...watch.acknowledged });
86
+ const page = watch.events.filter(event => event.cursor > cursor).slice(0, limit);
87
+ const oldestCursor = watch.events[0]?.cursor ?? watch.sequence + 1;
88
+ return {
89
+ ...this.describe(watch),
90
+ events: page.map(({ cursor: eventCursor, data }) => ({ cursor: eventCursor, data })),
91
+ nextCursor: page.at(-1)?.cursor ?? cursor,
92
+ gapDetected: cursor < oldestCursor - 1 || page.some(event => event.gap),
93
+ oldestCursor,
94
+ latestCursor: watch.sequence,
95
+ };
96
+ }
97
+ async stop(id) {
98
+ const watch = this.get(id);
99
+ watch.status = 'stopped';
100
+ watch.controller.abort();
101
+ await watch.task;
102
+ // Keep buffered events readable after stopping; a new watch may replace the
103
+ // oldest finished watch when the session reaches its retention limit.
104
+ }
105
+ async close() {
106
+ this.closed = true;
107
+ await Promise.all([...this.watches.keys()].map(id => this.stop(id)));
108
+ this.watches.clear();
109
+ }
110
+ get(id) {
111
+ const watch = this.watches.get(id);
112
+ if (!watch)
113
+ throw new AttoError('WATCH_NOT_FOUND', 'The watch does not exist in this session.');
114
+ return watch;
115
+ }
116
+ describe(watch) {
117
+ return {
118
+ id: watch.id, filter: watch.filter, status: watch.status, createdAt: watch.createdAt,
119
+ reconnects: watch.reconnects, replayable: isReplayable(watch.filter),
120
+ ...(watch.lastError ? { lastError: watch.lastError } : {}),
121
+ };
122
+ }
123
+ append(watch, data, position, gap = false) {
124
+ watch.events.push({ cursor: ++watch.sequence, data, ...(position ? { position } : {}), ...(gap ? { gap } : {}) });
125
+ if (watch.events.length > this.retention)
126
+ watch.events.splice(0, watch.events.length - this.retention);
127
+ }
128
+ async run(watch, original) {
129
+ const signal = watch.controller.signal;
130
+ const replayable = isReplayable(original);
131
+ const address = original.addresses?.[0];
132
+ let delay = this.backoffMs;
133
+ while (!signal.aborted) {
134
+ let filter = original;
135
+ if (replayable && address) {
136
+ const next = BigInt(watch.observed[address] ?? (BigInt(original.fromHeight ?? '1') - 1n).toString()) + 1n;
137
+ const from = next < BigInt(original.fromHeight ?? '1') ? BigInt(original.fromHeight ?? '1') : next;
138
+ if (from > BigInt(original.toHeight ?? '18446744073709551615'))
139
+ return;
140
+ filter = { ...original, fromHeight: from.toString() };
141
+ watch.observed[address] = (from - 1n).toString();
142
+ }
143
+ try {
144
+ watch.status = 'running';
145
+ await this.reader.stream(filter, model => {
146
+ if (signal.aborted)
147
+ return;
148
+ const position = replayable ? eventPosition(model) : undefined;
149
+ if (replayable) {
150
+ if (!position || position.address !== address)
151
+ throw new AttoError('INVALID_NODE_RESPONSE', 'The node returned an event for a different watched account.');
152
+ const last = BigInt(watch.observed[address] ?? (BigInt(filter.fromHeight) - 1n).toString());
153
+ if (BigInt(position.height) <= last)
154
+ return;
155
+ if (BigInt(position.height) !== last + 1n || (filter.toHeight && BigInt(position.height) > BigInt(filter.toHeight))) {
156
+ throw new AttoError('HISTORY_GAP', 'The node returned a gap in watched account history.');
157
+ }
158
+ }
159
+ const data = publicModel(model);
160
+ if (!replayable) {
161
+ const identity = createHash('sha256').update(JSON.stringify(data)).digest('hex');
162
+ if (watch.seen.has(identity))
163
+ return;
164
+ watch.seen.add(identity);
165
+ if (watch.seen.size > this.retention)
166
+ watch.seen.delete(watch.seen.values().next().value);
167
+ }
168
+ this.append(watch, data, position);
169
+ if (position)
170
+ watch.observed[position.address] = position.height;
171
+ delay = this.backoffMs;
172
+ watch.status = 'running';
173
+ delete watch.lastError;
174
+ }, signal);
175
+ if (signal.aborted)
176
+ return;
177
+ if (replayable && address && original.toHeight && BigInt(watch.observed[address] ?? '0') >= BigInt(original.toHeight))
178
+ return;
179
+ if (original.hash && watch.events.some(event => !event.gap))
180
+ return;
181
+ watch.lastError = { code: 'STREAM_CLOSED', message: 'The node closed the stream; reconnecting.' };
182
+ }
183
+ catch (error) {
184
+ if (signal.aborted)
185
+ return;
186
+ const safe = errorResult(error);
187
+ watch.lastError = { code: safe.code, message: safe.message };
188
+ }
189
+ watch.status = 'reconnecting';
190
+ watch.reconnects++;
191
+ if (!replayable)
192
+ this.append(watch, { type: 'gap', reason: 'disconnected', replayable: false }, undefined, true);
193
+ await sleep(delay, signal);
194
+ delay = Math.min(delay * 2, this.maxBackoffMs);
195
+ }
196
+ }
197
+ }