@antseed/node 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.
- package/README.md +411 -0
- package/contracts/AntseedEscrow.sol +310 -0
- package/contracts/MockUSDC.sol +64 -0
- package/contracts/README.md +102 -0
- package/dist/config/encryption.d.ts +17 -0
- package/dist/config/encryption.d.ts.map +1 -0
- package/dist/config/encryption.js +49 -0
- package/dist/config/encryption.js.map +1 -0
- package/dist/config/plugin-config-manager.d.ts +31 -0
- package/dist/config/plugin-config-manager.d.ts.map +1 -0
- package/dist/config/plugin-config-manager.js +135 -0
- package/dist/config/plugin-config-manager.js.map +1 -0
- package/dist/config/plugin-loader.d.ts +25 -0
- package/dist/config/plugin-loader.d.ts.map +1 -0
- package/dist/config/plugin-loader.js +64 -0
- package/dist/config/plugin-loader.js.map +1 -0
- package/dist/discovery/announcer.d.ts +44 -0
- package/dist/discovery/announcer.d.ts.map +1 -0
- package/dist/discovery/announcer.js +129 -0
- package/dist/discovery/announcer.js.map +1 -0
- package/dist/discovery/bootstrap.d.ts +13 -0
- package/dist/discovery/bootstrap.d.ts.map +1 -0
- package/dist/discovery/bootstrap.js +39 -0
- package/dist/discovery/bootstrap.js.map +1 -0
- package/dist/discovery/default-metadata-resolver.d.ts +16 -0
- package/dist/discovery/default-metadata-resolver.d.ts.map +1 -0
- package/dist/discovery/default-metadata-resolver.js +16 -0
- package/dist/discovery/default-metadata-resolver.js.map +1 -0
- package/dist/discovery/dht-health.d.ts +38 -0
- package/dist/discovery/dht-health.d.ts.map +1 -0
- package/dist/discovery/dht-health.js +101 -0
- package/dist/discovery/dht-health.js.map +1 -0
- package/dist/discovery/dht-node.d.ts +34 -0
- package/dist/discovery/dht-node.d.ts.map +1 -0
- package/dist/discovery/dht-node.js +168 -0
- package/dist/discovery/dht-node.js.map +1 -0
- package/dist/discovery/http-metadata-resolver.d.ts +15 -0
- package/dist/discovery/http-metadata-resolver.d.ts.map +1 -0
- package/dist/discovery/http-metadata-resolver.js +33 -0
- package/dist/discovery/http-metadata-resolver.js.map +1 -0
- package/dist/discovery/index.d.ts +16 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +15 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/metadata-codec.d.ts +22 -0
- package/dist/discovery/metadata-codec.d.ts.map +1 -0
- package/dist/discovery/metadata-codec.js +390 -0
- package/dist/discovery/metadata-codec.js.map +1 -0
- package/dist/discovery/metadata-resolver.d.ts +9 -0
- package/dist/discovery/metadata-resolver.d.ts.map +1 -0
- package/dist/discovery/metadata-resolver.js +2 -0
- package/dist/discovery/metadata-resolver.js.map +1 -0
- package/dist/discovery/metadata-server.d.ts +16 -0
- package/dist/discovery/metadata-server.d.ts.map +1 -0
- package/dist/discovery/metadata-server.js +59 -0
- package/dist/discovery/metadata-server.js.map +1 -0
- package/dist/discovery/metadata-validator.d.ts +12 -0
- package/dist/discovery/metadata-validator.d.ts.map +1 -0
- package/dist/discovery/metadata-validator.js +153 -0
- package/dist/discovery/metadata-validator.js.map +1 -0
- package/dist/discovery/peer-lookup.d.ts +26 -0
- package/dist/discovery/peer-lookup.d.ts.map +1 -0
- package/dist/discovery/peer-lookup.js +86 -0
- package/dist/discovery/peer-lookup.js.map +1 -0
- package/dist/discovery/peer-metadata.d.ts +31 -0
- package/dist/discovery/peer-metadata.d.ts.map +1 -0
- package/dist/discovery/peer-metadata.js +2 -0
- package/dist/discovery/peer-metadata.js.map +1 -0
- package/dist/discovery/peer-selector.d.ts +33 -0
- package/dist/discovery/peer-selector.d.ts.map +1 -0
- package/dist/discovery/peer-selector.js +80 -0
- package/dist/discovery/peer-selector.js.map +1 -0
- package/dist/discovery/profile-manager.d.ts +50 -0
- package/dist/discovery/profile-manager.d.ts.map +1 -0
- package/dist/discovery/profile-manager.js +105 -0
- package/dist/discovery/profile-manager.js.map +1 -0
- package/dist/discovery/profile-search.d.ts +27 -0
- package/dist/discovery/profile-search.d.ts.map +1 -0
- package/dist/discovery/profile-search.js +75 -0
- package/dist/discovery/profile-search.js.map +1 -0
- package/dist/discovery/reputation-verifier.d.ts +25 -0
- package/dist/discovery/reputation-verifier.d.ts.map +1 -0
- package/dist/discovery/reputation-verifier.js +27 -0
- package/dist/discovery/reputation-verifier.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/buyer-router.d.ts +21 -0
- package/dist/interfaces/buyer-router.d.ts.map +1 -0
- package/dist/interfaces/buyer-router.js +2 -0
- package/dist/interfaces/buyer-router.js.map +1 -0
- package/dist/interfaces/plugin.d.ts +31 -0
- package/dist/interfaces/plugin.d.ts.map +1 -0
- package/dist/interfaces/plugin.js +2 -0
- package/dist/interfaces/plugin.js.map +1 -0
- package/dist/interfaces/seller-provider.d.ts +69 -0
- package/dist/interfaces/seller-provider.d.ts.map +1 -0
- package/dist/interfaces/seller-provider.js +2 -0
- package/dist/interfaces/seller-provider.js.map +1 -0
- package/dist/metering/index.d.ts +7 -0
- package/dist/metering/index.d.ts.map +1 -0
- package/dist/metering/index.js +7 -0
- package/dist/metering/index.js.map +1 -0
- package/dist/metering/receipt-generator.d.ts +49 -0
- package/dist/metering/receipt-generator.d.ts.map +1 -0
- package/dist/metering/receipt-generator.js +74 -0
- package/dist/metering/receipt-generator.js.map +1 -0
- package/dist/metering/receipt-verifier.d.ts +52 -0
- package/dist/metering/receipt-verifier.d.ts.map +1 -0
- package/dist/metering/receipt-verifier.js +63 -0
- package/dist/metering/receipt-verifier.js.map +1 -0
- package/dist/metering/session-tracker.d.ts +59 -0
- package/dist/metering/session-tracker.d.ts.map +1 -0
- package/dist/metering/session-tracker.js +119 -0
- package/dist/metering/session-tracker.js.map +1 -0
- package/dist/metering/storage.d.ts +72 -0
- package/dist/metering/storage.d.ts.map +1 -0
- package/dist/metering/storage.js +446 -0
- package/dist/metering/storage.js.map +1 -0
- package/dist/metering/token-counter.d.ts +50 -0
- package/dist/metering/token-counter.d.ts.map +1 -0
- package/dist/metering/token-counter.js +96 -0
- package/dist/metering/token-counter.js.map +1 -0
- package/dist/metering/usage-aggregator.d.ts +46 -0
- package/dist/metering/usage-aggregator.d.ts.map +1 -0
- package/dist/metering/usage-aggregator.js +170 -0
- package/dist/metering/usage-aggregator.js.map +1 -0
- package/dist/node.d.ts +179 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +1328 -0
- package/dist/node.js.map +1 -0
- package/dist/p2p/connection-auth.d.ts +35 -0
- package/dist/p2p/connection-auth.d.ts.map +1 -0
- package/dist/p2p/connection-auth.js +102 -0
- package/dist/p2p/connection-auth.js.map +1 -0
- package/dist/p2p/connection-manager.d.ts +101 -0
- package/dist/p2p/connection-manager.d.ts.map +1 -0
- package/dist/p2p/connection-manager.js +726 -0
- package/dist/p2p/connection-manager.js.map +1 -0
- package/dist/p2p/handshake.d.ts +47 -0
- package/dist/p2p/handshake.d.ts.map +1 -0
- package/dist/p2p/handshake.js +107 -0
- package/dist/p2p/handshake.js.map +1 -0
- package/dist/p2p/ice-config.d.ts +27 -0
- package/dist/p2p/ice-config.d.ts.map +1 -0
- package/dist/p2p/ice-config.js +43 -0
- package/dist/p2p/ice-config.js.map +1 -0
- package/dist/p2p/identity.d.ts +27 -0
- package/dist/p2p/identity.d.ts.map +1 -0
- package/dist/p2p/identity.js +76 -0
- package/dist/p2p/identity.js.map +1 -0
- package/dist/p2p/index.d.ts +12 -0
- package/dist/p2p/index.d.ts.map +1 -0
- package/dist/p2p/index.js +11 -0
- package/dist/p2p/index.js.map +1 -0
- package/dist/p2p/keepalive.d.ts +49 -0
- package/dist/p2p/keepalive.d.ts.map +1 -0
- package/dist/p2p/keepalive.js +93 -0
- package/dist/p2p/keepalive.js.map +1 -0
- package/dist/p2p/message-protocol.d.ts +50 -0
- package/dist/p2p/message-protocol.d.ts.map +1 -0
- package/dist/p2p/message-protocol.js +134 -0
- package/dist/p2p/message-protocol.js.map +1 -0
- package/dist/p2p/nat-traversal.d.ts +51 -0
- package/dist/p2p/nat-traversal.d.ts.map +1 -0
- package/dist/p2p/nat-traversal.js +135 -0
- package/dist/p2p/nat-traversal.js.map +1 -0
- package/dist/p2p/payment-codec.d.ts +20 -0
- package/dist/p2p/payment-codec.d.ts.map +1 -0
- package/dist/p2p/payment-codec.js +130 -0
- package/dist/p2p/payment-codec.js.map +1 -0
- package/dist/p2p/payment-mux.d.ts +49 -0
- package/dist/p2p/payment-mux.d.ts.map +1 -0
- package/dist/p2p/payment-mux.js +131 -0
- package/dist/p2p/payment-mux.js.map +1 -0
- package/dist/p2p/reconnect.d.ts +48 -0
- package/dist/p2p/reconnect.d.ts.map +1 -0
- package/dist/p2p/reconnect.js +89 -0
- package/dist/p2p/reconnect.js.map +1 -0
- package/dist/payments/balance-manager.d.ts +17 -0
- package/dist/payments/balance-manager.d.ts.map +1 -0
- package/dist/payments/balance-manager.js +54 -0
- package/dist/payments/balance-manager.js.map +1 -0
- package/dist/payments/buyer-payment-manager.d.ts +122 -0
- package/dist/payments/buyer-payment-manager.d.ts.map +1 -0
- package/dist/payments/buyer-payment-manager.js +280 -0
- package/dist/payments/buyer-payment-manager.js.map +1 -0
- package/dist/payments/disputes.d.ts +18 -0
- package/dist/payments/disputes.d.ts.map +1 -0
- package/dist/payments/disputes.js +47 -0
- package/dist/payments/disputes.js.map +1 -0
- package/dist/payments/evm/escrow-client.d.ts +61 -0
- package/dist/payments/evm/escrow-client.d.ts.map +1 -0
- package/dist/payments/evm/escrow-client.js +170 -0
- package/dist/payments/evm/escrow-client.js.map +1 -0
- package/dist/payments/evm/keypair.d.ts +21 -0
- package/dist/payments/evm/keypair.d.ts.map +1 -0
- package/dist/payments/evm/keypair.js +29 -0
- package/dist/payments/evm/keypair.js.map +1 -0
- package/dist/payments/evm/signatures.d.ts +11 -0
- package/dist/payments/evm/signatures.d.ts.map +1 -0
- package/dist/payments/evm/signatures.js +56 -0
- package/dist/payments/evm/signatures.js.map +1 -0
- package/dist/payments/evm/wallet.d.ts +5 -0
- package/dist/payments/evm/wallet.d.ts.map +1 -0
- package/dist/payments/evm/wallet.js +31 -0
- package/dist/payments/evm/wallet.js.map +1 -0
- package/dist/payments/index.d.ts +13 -0
- package/dist/payments/index.d.ts.map +1 -0
- package/dist/payments/index.js +14 -0
- package/dist/payments/index.js.map +1 -0
- package/dist/payments/settlement.d.ts +6 -0
- package/dist/payments/settlement.d.ts.map +1 -0
- package/dist/payments/settlement.js +25 -0
- package/dist/payments/settlement.js.map +1 -0
- package/dist/payments/types.d.ts +66 -0
- package/dist/payments/types.d.ts.map +1 -0
- package/dist/payments/types.js +2 -0
- package/dist/payments/types.js.map +1 -0
- package/dist/proxy/index.d.ts +4 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/index.js +4 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/proxy/provider-detection.d.ts +20 -0
- package/dist/proxy/provider-detection.d.ts.map +1 -0
- package/dist/proxy/provider-detection.js +61 -0
- package/dist/proxy/provider-detection.js.map +1 -0
- package/dist/proxy/proxy-mux.d.ts +35 -0
- package/dist/proxy/proxy-mux.d.ts.map +1 -0
- package/dist/proxy/proxy-mux.js +137 -0
- package/dist/proxy/proxy-mux.js.map +1 -0
- package/dist/proxy/request-codec.d.ts +33 -0
- package/dist/proxy/request-codec.d.ts.map +1 -0
- package/dist/proxy/request-codec.js +238 -0
- package/dist/proxy/request-codec.js.map +1 -0
- package/dist/reputation/index.d.ts +7 -0
- package/dist/reputation/index.d.ts.map +1 -0
- package/dist/reputation/index.js +6 -0
- package/dist/reputation/index.js.map +1 -0
- package/dist/reputation/rating-manager.d.ts +20 -0
- package/dist/reputation/rating-manager.d.ts.map +1 -0
- package/dist/reputation/rating-manager.js +91 -0
- package/dist/reputation/rating-manager.js.map +1 -0
- package/dist/reputation/report-manager.d.ts +21 -0
- package/dist/reputation/report-manager.d.ts.map +1 -0
- package/dist/reputation/report-manager.js +70 -0
- package/dist/reputation/report-manager.js.map +1 -0
- package/dist/reputation/trust-engine.d.ts +36 -0
- package/dist/reputation/trust-engine.d.ts.map +1 -0
- package/dist/reputation/trust-engine.js +95 -0
- package/dist/reputation/trust-engine.js.map +1 -0
- package/dist/reputation/trust-score.d.ts +43 -0
- package/dist/reputation/trust-score.d.ts.map +1 -0
- package/dist/reputation/trust-score.js +34 -0
- package/dist/reputation/trust-score.js.map +1 -0
- package/dist/reputation/uptime-tracker.d.ts +51 -0
- package/dist/reputation/uptime-tracker.d.ts.map +1 -0
- package/dist/reputation/uptime-tracker.js +123 -0
- package/dist/reputation/uptime-tracker.js.map +1 -0
- package/dist/routing/default-router.d.ts +21 -0
- package/dist/routing/default-router.d.ts.map +1 -0
- package/dist/routing/default-router.js +60 -0
- package/dist/routing/default-router.js.map +1 -0
- package/dist/types/buyer.d.ts +36 -0
- package/dist/types/buyer.d.ts.map +1 -0
- package/dist/types/buyer.js +2 -0
- package/dist/types/buyer.js.map +1 -0
- package/dist/types/capability.d.ts +25 -0
- package/dist/types/capability.d.ts.map +1 -0
- package/dist/types/capability.js +2 -0
- package/dist/types/capability.js.map +1 -0
- package/dist/types/connection.d.ts +27 -0
- package/dist/types/connection.d.ts.map +1 -0
- package/dist/types/connection.js +11 -0
- package/dist/types/connection.js.map +1 -0
- package/dist/types/http.d.ts +19 -0
- package/dist/types/http.d.ts.map +1 -0
- package/dist/types/http.js +2 -0
- package/dist/types/http.js.map +1 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +15 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/metering.d.ts +170 -0
- package/dist/types/metering.d.ts.map +1 -0
- package/dist/types/metering.js +2 -0
- package/dist/types/metering.js.map +1 -0
- package/dist/types/peer-profile.d.ts +24 -0
- package/dist/types/peer-profile.d.ts.map +1 -0
- package/dist/types/peer-profile.js +2 -0
- package/dist/types/peer-profile.js.map +1 -0
- package/dist/types/peer.d.ts +56 -0
- package/dist/types/peer.d.ts.map +1 -0
- package/dist/types/peer.js +11 -0
- package/dist/types/peer.js.map +1 -0
- package/dist/types/plugin-config.d.ts +31 -0
- package/dist/types/plugin-config.d.ts.map +1 -0
- package/dist/types/plugin-config.js +2 -0
- package/dist/types/plugin-config.js.map +1 -0
- package/dist/types/protocol.d.ts +141 -0
- package/dist/types/protocol.d.ts.map +1 -0
- package/dist/types/protocol.js +42 -0
- package/dist/types/protocol.js.map +1 -0
- package/dist/types/provider.d.ts +38 -0
- package/dist/types/provider.d.ts.map +1 -0
- package/dist/types/provider.js +11 -0
- package/dist/types/provider.js.map +1 -0
- package/dist/types/rating.d.ts +21 -0
- package/dist/types/rating.d.ts.map +1 -0
- package/dist/types/rating.js +2 -0
- package/dist/types/rating.js.map +1 -0
- package/dist/types/report.d.ts +20 -0
- package/dist/types/report.d.ts.map +1 -0
- package/dist/types/report.js +2 -0
- package/dist/types/report.js.map +1 -0
- package/dist/types/seller.d.ts +36 -0
- package/dist/types/seller.d.ts.map +1 -0
- package/dist/types/seller.js +2 -0
- package/dist/types/seller.js.map +1 -0
- package/dist/types/staking.d.ts +16 -0
- package/dist/types/staking.d.ts.map +1 -0
- package/dist/types/staking.js +6 -0
- package/dist/types/staking.js.map +1 -0
- package/dist/utils/debug.d.ts +4 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +25 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/hex.d.ts +3 -0
- package/dist/utils/hex.d.ts.map +1 -0
- package/dist/utils/hex.js +15 -0
- package/dist/utils/hex.js.map +1 -0
- package/package.json +62 -0
- package/scripts/ensure-node-native-modules.mjs +153 -0
- package/scripts/patch-ethers.js +44 -0
- package/src/config/encryption.test.ts +49 -0
- package/src/config/encryption.ts +53 -0
- package/src/config/plugin-config-manager.test.ts +92 -0
- package/src/config/plugin-config-manager.ts +153 -0
- package/src/config/plugin-loader.ts +90 -0
- package/src/discovery/announcer.ts +169 -0
- package/src/discovery/bootstrap.ts +57 -0
- package/src/discovery/default-metadata-resolver.ts +18 -0
- package/src/discovery/dht-health.ts +136 -0
- package/src/discovery/dht-node.ts +191 -0
- package/src/discovery/http-metadata-resolver.ts +47 -0
- package/src/discovery/index.ts +15 -0
- package/src/discovery/metadata-codec.ts +453 -0
- package/src/discovery/metadata-resolver.ts +7 -0
- package/src/discovery/metadata-server.ts +73 -0
- package/src/discovery/metadata-validator.ts +172 -0
- package/src/discovery/peer-lookup.ts +122 -0
- package/src/discovery/peer-metadata.ts +34 -0
- package/src/discovery/peer-selector.ts +134 -0
- package/src/discovery/profile-manager.ts +131 -0
- package/src/discovery/profile-search.ts +100 -0
- package/src/discovery/reputation-verifier.ts +54 -0
- package/src/index.ts +61 -0
- package/src/interfaces/buyer-router.ts +21 -0
- package/src/interfaces/plugin.ts +36 -0
- package/src/interfaces/seller-provider.ts +81 -0
- package/src/metering/index.ts +6 -0
- package/src/metering/receipt-generator.ts +105 -0
- package/src/metering/receipt-verifier.ts +102 -0
- package/src/metering/session-tracker.ts +145 -0
- package/src/metering/storage.ts +600 -0
- package/src/metering/token-counter.ts +127 -0
- package/src/metering/usage-aggregator.ts +236 -0
- package/src/node.ts +1698 -0
- package/src/p2p/connection-auth.ts +152 -0
- package/src/p2p/connection-manager.ts +916 -0
- package/src/p2p/handshake.ts +162 -0
- package/src/p2p/ice-config.ts +59 -0
- package/src/p2p/identity.ts +110 -0
- package/src/p2p/index.ts +11 -0
- package/src/p2p/keepalive.ts +118 -0
- package/src/p2p/message-protocol.ts +171 -0
- package/src/p2p/nat-traversal.ts +169 -0
- package/src/p2p/payment-codec.ts +165 -0
- package/src/p2p/payment-mux.ts +153 -0
- package/src/p2p/reconnect.ts +117 -0
- package/src/payments/balance-manager.ts +77 -0
- package/src/payments/buyer-payment-manager.ts +414 -0
- package/src/payments/disputes.ts +72 -0
- package/src/payments/evm/escrow-client.ts +263 -0
- package/src/payments/evm/keypair.ts +31 -0
- package/src/payments/evm/signatures.ts +103 -0
- package/src/payments/evm/wallet.ts +42 -0
- package/src/payments/index.ts +50 -0
- package/src/payments/settlement.ts +40 -0
- package/src/payments/types.ts +79 -0
- package/src/proxy/index.ts +3 -0
- package/src/proxy/provider-detection.ts +78 -0
- package/src/proxy/proxy-mux.ts +173 -0
- package/src/proxy/request-codec.ts +294 -0
- package/src/reputation/index.ts +6 -0
- package/src/reputation/rating-manager.ts +118 -0
- package/src/reputation/report-manager.ts +91 -0
- package/src/reputation/trust-engine.ts +120 -0
- package/src/reputation/trust-score.ts +74 -0
- package/src/reputation/uptime-tracker.ts +155 -0
- package/src/routing/default-router.ts +75 -0
- package/src/types/bittorrent-dht.d.ts +19 -0
- package/src/types/buyer.ts +37 -0
- package/src/types/capability.ts +34 -0
- package/src/types/connection.ts +29 -0
- package/src/types/http.ts +20 -0
- package/src/types/index.ts +14 -0
- package/src/types/metering.ts +175 -0
- package/src/types/nat-api.d.ts +29 -0
- package/src/types/peer-profile.ts +25 -0
- package/src/types/peer.ts +62 -0
- package/src/types/plugin-config.ts +31 -0
- package/src/types/protocol.ts +162 -0
- package/src/types/provider.ts +40 -0
- package/src/types/rating.ts +23 -0
- package/src/types/report.ts +30 -0
- package/src/types/seller.ts +38 -0
- package/src/types/staking.ts +23 -0
- package/src/utils/debug.ts +30 -0
- package/src/utils/hex.ts +14 -0
- package/tests/balance-manager.test.ts +156 -0
- package/tests/bootstrap.test.ts +108 -0
- package/tests/buyer-payment-manager.test.ts +358 -0
- package/tests/connection-auth.test.ts +87 -0
- package/tests/default-router.test.ts +148 -0
- package/tests/evm-keypair.test.ts +173 -0
- package/tests/identity.test.ts +133 -0
- package/tests/message-protocol.test.ts +212 -0
- package/tests/metadata-codec.test.ts +165 -0
- package/tests/metadata-validator.test.ts +261 -0
- package/tests/metering-storage.test.ts +244 -0
- package/tests/payment-codec.test.ts +95 -0
- package/tests/payment-mux.test.ts +191 -0
- package/tests/peer-selector.test.ts +184 -0
- package/tests/provider-detection.test.ts +107 -0
- package/tests/proxy-mux-security.test.ts +38 -0
- package/tests/receipt.test.ts +215 -0
- package/tests/reputation-integration.test.ts +195 -0
- package/tests/request-codec.test.ts +144 -0
- package/tests/token-counter.test.ts +122 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { type PeerId, toPeerId } from "../types/peer.js";
|
|
2
|
+
import { MessageType } from "../types/protocol.js";
|
|
3
|
+
import { ConnectionState } from "../types/connection.js";
|
|
4
|
+
import { signData, verifySignature, bytesToHex } from "./identity.js";
|
|
5
|
+
import type { PeerConnection } from "./connection-manager.js";
|
|
6
|
+
|
|
7
|
+
/** Nonce size in bytes. */
|
|
8
|
+
const NONCE_SIZE = 32;
|
|
9
|
+
|
|
10
|
+
/** Handshake timeout in milliseconds. */
|
|
11
|
+
const HANDSHAKE_TIMEOUT_MS = 10_000;
|
|
12
|
+
|
|
13
|
+
export interface HandshakeResult {
|
|
14
|
+
remotePeerId: PeerId;
|
|
15
|
+
verified: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Generate a random nonce for the handshake challenge. */
|
|
19
|
+
function generateNonce(): Uint8Array {
|
|
20
|
+
const nonce = new Uint8Array(NONCE_SIZE);
|
|
21
|
+
crypto.getRandomValues(nonce);
|
|
22
|
+
return nonce;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build the HandshakeInit payload:
|
|
27
|
+
* [32 bytes pubkey] [32 bytes nonce] [64 bytes signature(nonce)]
|
|
28
|
+
*/
|
|
29
|
+
export async function buildHandshakeInit(
|
|
30
|
+
publicKey: Uint8Array,
|
|
31
|
+
privateKey: Uint8Array
|
|
32
|
+
): Promise<{ payload: Uint8Array; nonce: Uint8Array }> {
|
|
33
|
+
const nonce = generateNonce();
|
|
34
|
+
const signature = await signData(privateKey, nonce);
|
|
35
|
+
|
|
36
|
+
const payload = new Uint8Array(32 + 32 + 64);
|
|
37
|
+
payload.set(publicKey, 0);
|
|
38
|
+
payload.set(nonce, 32);
|
|
39
|
+
payload.set(signature, 64);
|
|
40
|
+
|
|
41
|
+
return { payload, nonce };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Verify a received HandshakeInit payload.
|
|
46
|
+
* Returns the remote peer's public key and nonce if valid.
|
|
47
|
+
*/
|
|
48
|
+
export async function verifyHandshakeInit(
|
|
49
|
+
payload: Uint8Array
|
|
50
|
+
): Promise<{ remotePubKey: Uint8Array; nonce: Uint8Array; valid: boolean }> {
|
|
51
|
+
if (payload.length !== 128) {
|
|
52
|
+
return { remotePubKey: new Uint8Array(32), nonce: new Uint8Array(32), valid: false };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const remotePubKey = payload.slice(0, 32);
|
|
56
|
+
const nonce = payload.slice(32, 64);
|
|
57
|
+
const signature = payload.slice(64, 128);
|
|
58
|
+
|
|
59
|
+
const valid = await verifySignature(remotePubKey, signature, nonce);
|
|
60
|
+
|
|
61
|
+
return { remotePubKey, nonce, valid };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build a HandshakeAck payload:
|
|
66
|
+
* [32 bytes pubkey] [32 bytes echo-nonce] [64 bytes signature(echo-nonce)]
|
|
67
|
+
*/
|
|
68
|
+
export async function buildHandshakeAck(
|
|
69
|
+
publicKey: Uint8Array,
|
|
70
|
+
privateKey: Uint8Array,
|
|
71
|
+
remoteNonce: Uint8Array
|
|
72
|
+
): Promise<Uint8Array> {
|
|
73
|
+
const signature = await signData(privateKey, remoteNonce);
|
|
74
|
+
|
|
75
|
+
const payload = new Uint8Array(32 + 32 + 64);
|
|
76
|
+
payload.set(publicKey, 0);
|
|
77
|
+
payload.set(remoteNonce, 32);
|
|
78
|
+
payload.set(signature, 64);
|
|
79
|
+
|
|
80
|
+
return payload;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Verify a received HandshakeAck payload against the nonce we originally sent.
|
|
85
|
+
*/
|
|
86
|
+
export async function verifyHandshakeAck(
|
|
87
|
+
payload: Uint8Array,
|
|
88
|
+
originalNonce: Uint8Array
|
|
89
|
+
): Promise<{ remotePubKey: Uint8Array; valid: boolean }> {
|
|
90
|
+
if (payload.length !== 128) {
|
|
91
|
+
return { remotePubKey: new Uint8Array(32), valid: false };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const remotePubKey = payload.slice(0, 32);
|
|
95
|
+
const echoNonce = payload.slice(32, 64);
|
|
96
|
+
const signature = payload.slice(64, 128);
|
|
97
|
+
|
|
98
|
+
// Verify that the echoed nonce matches what we sent
|
|
99
|
+
const nonceMatches = originalNonce.every((b, i) => b === echoNonce[i]);
|
|
100
|
+
if (!nonceMatches) {
|
|
101
|
+
return { remotePubKey, valid: false };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const valid = await verifySignature(remotePubKey, signature, echoNonce);
|
|
105
|
+
return { remotePubKey, valid };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Perform the full handshake as the initiator.
|
|
110
|
+
* Sends HandshakeInit, waits for HandshakeAck, verifies.
|
|
111
|
+
*/
|
|
112
|
+
export async function performHandshake(
|
|
113
|
+
conn: PeerConnection,
|
|
114
|
+
localPublicKey: Uint8Array,
|
|
115
|
+
localPrivateKey: Uint8Array,
|
|
116
|
+
sendFn: (type: MessageType, payload: Uint8Array) => void,
|
|
117
|
+
waitForMessage: (type: MessageType, timeoutMs: number) => Promise<Uint8Array>
|
|
118
|
+
): Promise<HandshakeResult> {
|
|
119
|
+
const { payload, nonce } = await buildHandshakeInit(localPublicKey, localPrivateKey);
|
|
120
|
+
sendFn(MessageType.HandshakeInit, payload);
|
|
121
|
+
|
|
122
|
+
const ackPayload = await waitForMessage(MessageType.HandshakeAck, HANDSHAKE_TIMEOUT_MS);
|
|
123
|
+
const { remotePubKey, valid } = await verifyHandshakeAck(ackPayload, nonce);
|
|
124
|
+
|
|
125
|
+
const remotePeerId = toPeerId(bytesToHex(remotePubKey));
|
|
126
|
+
|
|
127
|
+
if (valid) {
|
|
128
|
+
conn.setState(ConnectionState.Authenticated);
|
|
129
|
+
} else {
|
|
130
|
+
conn.setState(ConnectionState.Failed);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return { remotePeerId, verified: valid };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Respond to a handshake as the responder.
|
|
138
|
+
* Waits for HandshakeInit, verifies, sends HandshakeAck.
|
|
139
|
+
*/
|
|
140
|
+
export async function respondToHandshake(
|
|
141
|
+
conn: PeerConnection,
|
|
142
|
+
localPublicKey: Uint8Array,
|
|
143
|
+
localPrivateKey: Uint8Array,
|
|
144
|
+
sendFn: (type: MessageType, payload: Uint8Array) => void,
|
|
145
|
+
waitForMessage: (type: MessageType, timeoutMs: number) => Promise<Uint8Array>
|
|
146
|
+
): Promise<HandshakeResult> {
|
|
147
|
+
const initPayload = await waitForMessage(MessageType.HandshakeInit, HANDSHAKE_TIMEOUT_MS);
|
|
148
|
+
const { remotePubKey, nonce, valid } = await verifyHandshakeInit(initPayload);
|
|
149
|
+
|
|
150
|
+
if (!valid) {
|
|
151
|
+
conn.setState(ConnectionState.Failed);
|
|
152
|
+
return { remotePeerId: toPeerId(bytesToHex(remotePubKey)), verified: false };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const ackPayload = await buildHandshakeAck(localPublicKey, localPrivateKey, nonce);
|
|
156
|
+
sendFn(MessageType.HandshakeAck, ackPayload);
|
|
157
|
+
|
|
158
|
+
const remotePeerId = toPeerId(bytesToHex(remotePubKey));
|
|
159
|
+
conn.setState(ConnectionState.Authenticated);
|
|
160
|
+
|
|
161
|
+
return { remotePeerId, verified: true };
|
|
162
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** STUN/TURN server configuration. */
|
|
2
|
+
export interface IceServer {
|
|
3
|
+
urls: string | string[];
|
|
4
|
+
username?: string;
|
|
5
|
+
credential?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Full ICE configuration for a peer connection. */
|
|
9
|
+
export interface IceConfig {
|
|
10
|
+
iceServers: IceServer[];
|
|
11
|
+
iceTransportPolicy?: "all" | "relay";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Returns a sensible default ICE configuration using public STUN servers. */
|
|
15
|
+
export function getDefaultIceConfig(): IceConfig {
|
|
16
|
+
return {
|
|
17
|
+
iceServers: [
|
|
18
|
+
{ urls: "stun:stun.l.google.com:19302" },
|
|
19
|
+
{ urls: "stun:stun1.l.google.com:19302" },
|
|
20
|
+
{ urls: "stun:stun2.l.google.com:19302" },
|
|
21
|
+
],
|
|
22
|
+
iceTransportPolicy: "all",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Extract ICE candidate type from an SDP candidate string. */
|
|
27
|
+
export function extractCandidateType(
|
|
28
|
+
candidate: string
|
|
29
|
+
): "host" | "srflx" | "prflx" | "relay" | "unknown" {
|
|
30
|
+
const match = candidate.match(/typ\s+(host|srflx|prflx|relay)/);
|
|
31
|
+
if (!match) return "unknown";
|
|
32
|
+
return match[1] as "host" | "srflx" | "prflx" | "relay";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Determine if TURN relay fallback is needed based on gathered candidates.
|
|
37
|
+
* Returns true if no srflx (server-reflexive) candidates were gathered,
|
|
38
|
+
* which typically indicates a symmetric NAT.
|
|
39
|
+
*/
|
|
40
|
+
export function needsTurnFallback(candidates: string[]): boolean {
|
|
41
|
+
const types = candidates.map(extractCandidateType);
|
|
42
|
+
const hasSrflx = types.includes("srflx");
|
|
43
|
+
const hasRelay = types.includes("relay");
|
|
44
|
+
// Need TURN if we have no server-reflexive candidates
|
|
45
|
+
// (unless we already have relay candidates working)
|
|
46
|
+
return !hasSrflx && !hasRelay;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Build a complete ICE configuration, optionally adding TURN servers.
|
|
51
|
+
* If turnServers are provided, they are appended to the default STUN servers.
|
|
52
|
+
*/
|
|
53
|
+
export function buildIceConfig(turnServers?: IceServer[]): IceConfig {
|
|
54
|
+
const config = getDefaultIceConfig();
|
|
55
|
+
if (turnServers && turnServers.length > 0) {
|
|
56
|
+
config.iceServers.push(...turnServers);
|
|
57
|
+
}
|
|
58
|
+
return config;
|
|
59
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as ed from "@noble/ed25519";
|
|
2
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import {
|
|
6
|
+
createPrivateKey,
|
|
7
|
+
createPublicKey,
|
|
8
|
+
sign as nodeSign,
|
|
9
|
+
verify as nodeVerify,
|
|
10
|
+
} from "node:crypto";
|
|
11
|
+
import { toPeerId, type PeerId } from "../types/peer.js";
|
|
12
|
+
import { hexToBytes, bytesToHex } from "../utils/hex.js";
|
|
13
|
+
|
|
14
|
+
export { hexToBytes, bytesToHex };
|
|
15
|
+
|
|
16
|
+
/** Directory where identity keys are stored. */
|
|
17
|
+
const CONFIG_DIR = join(homedir(), ".antseed");
|
|
18
|
+
const PRIVATE_KEY_FILE = "identity.key";
|
|
19
|
+
const ED25519_PKCS8_SEED_PREFIX = Buffer.from("302e020100300506032b657004220420", "hex");
|
|
20
|
+
const ED25519_SPKI_PUBLIC_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
21
|
+
|
|
22
|
+
export interface Identity {
|
|
23
|
+
peerId: PeerId;
|
|
24
|
+
privateKey: Uint8Array;
|
|
25
|
+
publicKey: Uint8Array;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Load an existing identity from disk, or create and persist a new one.
|
|
30
|
+
* The private key is stored as hex in ~/.antseed/identity.key.
|
|
31
|
+
*/
|
|
32
|
+
export async function loadOrCreateIdentity(configDir?: string): Promise<Identity> {
|
|
33
|
+
const dir = configDir ?? CONFIG_DIR;
|
|
34
|
+
const keyPath = join(dir, PRIVATE_KEY_FILE);
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const hexKey = (await readFile(keyPath, "utf-8")).trim();
|
|
38
|
+
const privateKey = hexToBytes(hexKey);
|
|
39
|
+
const publicKey = await ed.getPublicKeyAsync(privateKey);
|
|
40
|
+
const peerId = toPeerId(bytesToHex(publicKey));
|
|
41
|
+
return { peerId, privateKey, publicKey };
|
|
42
|
+
} catch {
|
|
43
|
+
// Key doesn't exist — generate a new one.
|
|
44
|
+
const privateKey = ed.utils.randomPrivateKey();
|
|
45
|
+
const publicKey = await ed.getPublicKeyAsync(privateKey);
|
|
46
|
+
const peerId = toPeerId(bytesToHex(publicKey));
|
|
47
|
+
|
|
48
|
+
await mkdir(dir, { recursive: true });
|
|
49
|
+
await writeFile(keyPath, bytesToHex(privateKey), { mode: 0o600 });
|
|
50
|
+
|
|
51
|
+
return { peerId, privateKey, publicKey };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Sign arbitrary data with the local identity's private key. */
|
|
56
|
+
export async function signData(
|
|
57
|
+
privateKey: Uint8Array,
|
|
58
|
+
data: Uint8Array
|
|
59
|
+
): Promise<Uint8Array> {
|
|
60
|
+
return ed.signAsync(data, privateKey);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Verify a signature from a remote peer. */
|
|
64
|
+
export async function verifySignature(
|
|
65
|
+
publicKey: Uint8Array,
|
|
66
|
+
signature: Uint8Array,
|
|
67
|
+
data: Uint8Array
|
|
68
|
+
): Promise<boolean> {
|
|
69
|
+
return ed.verifyAsync(signature, data, publicKey);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Sign a UTF-8 message and return a hex-encoded Ed25519 signature.
|
|
74
|
+
* Uses Node's crypto implementation for synchronous signing.
|
|
75
|
+
*/
|
|
76
|
+
export function signUtf8Ed25519(privateKeySeed: Uint8Array, message: string): string {
|
|
77
|
+
const key = createPrivateKey({
|
|
78
|
+
key: Buffer.concat([ED25519_PKCS8_SEED_PREFIX, Buffer.from(privateKeySeed)]),
|
|
79
|
+
format: "der",
|
|
80
|
+
type: "pkcs8",
|
|
81
|
+
});
|
|
82
|
+
const signature = nodeSign(null, Buffer.from(message, "utf-8"), key);
|
|
83
|
+
return signature.toString("hex");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Verify a UTF-8 message against a hex-encoded Ed25519 signature.
|
|
88
|
+
*/
|
|
89
|
+
export function verifyUtf8Ed25519(
|
|
90
|
+
publicKeyHex: string,
|
|
91
|
+
message: string,
|
|
92
|
+
signatureHex: string
|
|
93
|
+
): boolean {
|
|
94
|
+
try {
|
|
95
|
+
const publicKeyBytes = hexToBytes(publicKeyHex);
|
|
96
|
+
const key = createPublicKey({
|
|
97
|
+
key: Buffer.concat([ED25519_SPKI_PUBLIC_PREFIX, Buffer.from(publicKeyBytes)]),
|
|
98
|
+
format: "der",
|
|
99
|
+
type: "spki",
|
|
100
|
+
});
|
|
101
|
+
return nodeVerify(
|
|
102
|
+
null,
|
|
103
|
+
Buffer.from(message, "utf-8"),
|
|
104
|
+
key,
|
|
105
|
+
Buffer.from(signatureHex, "hex")
|
|
106
|
+
);
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/p2p/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { type Identity, loadOrCreateIdentity, signData, verifySignature, hexToBytes, bytesToHex, signUtf8Ed25519, verifyUtf8Ed25519 } from './identity.js';
|
|
2
|
+
export { encodeFrame, decodeFrame, FrameDecoder, MessageMux, type MessageHandler } from './message-protocol.js';
|
|
3
|
+
export { ConnectionManager, PeerConnection, type PeerEndpoint } from './connection-manager.js';
|
|
4
|
+
export { type IceServer, type IceConfig, getDefaultIceConfig, buildIceConfig, needsTurnFallback, extractCandidateType } from './ice-config.js';
|
|
5
|
+
export { KeepaliveManager, buildPongPayload, type KeepaliveConfig, type KeepaliveCallbacks, DEFAULT_PING_INTERVAL_MS, DEFAULT_PONG_TIMEOUT_MS, MAX_MISSED_PONGS } from './keepalive.js';
|
|
6
|
+
export { ReconnectManager, type ReconnectConfig, type ReconnectCallbacks } from './reconnect.js';
|
|
7
|
+
export { performHandshake, respondToHandshake, buildHandshakeInit, verifyHandshakeInit, buildHandshakeAck, verifyHandshakeAck, type HandshakeResult } from './handshake.js';
|
|
8
|
+
export { NatTraversal, type NatMapping, type NatTraversalResult } from './nat-traversal.js';
|
|
9
|
+
export { PaymentMux } from './payment-mux.js';
|
|
10
|
+
export type { PaymentMessageHandler } from './payment-mux.js';
|
|
11
|
+
export * from './payment-codec.js';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/** Default keepalive interval in milliseconds. */
|
|
2
|
+
export const DEFAULT_PING_INTERVAL_MS = 15_000;
|
|
3
|
+
|
|
4
|
+
/** Default timeout waiting for a pong response. */
|
|
5
|
+
export const DEFAULT_PONG_TIMEOUT_MS = 5_000;
|
|
6
|
+
|
|
7
|
+
/** Maximum consecutive missed pongs before declaring connection dead. */
|
|
8
|
+
export const MAX_MISSED_PONGS = 3;
|
|
9
|
+
|
|
10
|
+
export interface KeepaliveConfig {
|
|
11
|
+
pingIntervalMs?: number;
|
|
12
|
+
pongTimeoutMs?: number;
|
|
13
|
+
maxMissedPongs?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface KeepaliveCallbacks {
|
|
17
|
+
sendPing: (payload: Uint8Array) => void;
|
|
18
|
+
onDead: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Manages the keepalive (ping/pong) cycle for a peer connection.
|
|
23
|
+
*
|
|
24
|
+
* The initiator sends Ping messages at a regular interval. If no Pong
|
|
25
|
+
* is received within pongTimeoutMs, it increments the missed counter.
|
|
26
|
+
* After maxMissedPongs consecutive misses, the connection is declared dead.
|
|
27
|
+
*/
|
|
28
|
+
export class KeepaliveManager {
|
|
29
|
+
private _pingInterval: number;
|
|
30
|
+
private _pongTimeout: number;
|
|
31
|
+
private _maxMissedPongs: number;
|
|
32
|
+
private _missedPongs = 0;
|
|
33
|
+
private _intervalHandle: ReturnType<typeof setInterval> | null = null;
|
|
34
|
+
private _pongTimeoutHandle: ReturnType<typeof setTimeout> | null = null;
|
|
35
|
+
private _callbacks: KeepaliveCallbacks;
|
|
36
|
+
private _lastPingTime = 0;
|
|
37
|
+
private _latencyMs = 0;
|
|
38
|
+
private _running = false;
|
|
39
|
+
|
|
40
|
+
constructor(callbacks: KeepaliveCallbacks, config?: KeepaliveConfig) {
|
|
41
|
+
this._callbacks = callbacks;
|
|
42
|
+
this._pingInterval = config?.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
|
|
43
|
+
this._pongTimeout = config?.pongTimeoutMs ?? DEFAULT_PONG_TIMEOUT_MS;
|
|
44
|
+
this._maxMissedPongs = config?.maxMissedPongs ?? MAX_MISSED_PONGS;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get missedPongs(): number {
|
|
48
|
+
return this._missedPongs;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get latencyMs(): number {
|
|
52
|
+
return this._latencyMs;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get isRunning(): boolean {
|
|
56
|
+
return this._running;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Start the keepalive ping cycle. */
|
|
60
|
+
start(): void {
|
|
61
|
+
if (this._running) return;
|
|
62
|
+
this._running = true;
|
|
63
|
+
this._missedPongs = 0;
|
|
64
|
+
this._sendPing();
|
|
65
|
+
this._intervalHandle = setInterval(() => this._sendPing(), this._pingInterval);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Stop the keepalive cycle. */
|
|
69
|
+
stop(): void {
|
|
70
|
+
this._running = false;
|
|
71
|
+
if (this._intervalHandle) {
|
|
72
|
+
clearInterval(this._intervalHandle);
|
|
73
|
+
this._intervalHandle = null;
|
|
74
|
+
}
|
|
75
|
+
if (this._pongTimeoutHandle) {
|
|
76
|
+
clearTimeout(this._pongTimeoutHandle);
|
|
77
|
+
this._pongTimeoutHandle = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Handle a received Pong message. */
|
|
82
|
+
handlePong(_payload: Uint8Array): void {
|
|
83
|
+
this._missedPongs = 0;
|
|
84
|
+
this._latencyMs = Date.now() - this._lastPingTime;
|
|
85
|
+
|
|
86
|
+
if (this._pongTimeoutHandle) {
|
|
87
|
+
clearTimeout(this._pongTimeoutHandle);
|
|
88
|
+
this._pongTimeoutHandle = null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Send a ping and set up the pong timeout. */
|
|
93
|
+
private _sendPing(): void {
|
|
94
|
+
this._lastPingTime = Date.now();
|
|
95
|
+
|
|
96
|
+
// Payload is the timestamp as 8 bytes (BigUint64)
|
|
97
|
+
const payload = new Uint8Array(8);
|
|
98
|
+
const view = new DataView(payload.buffer);
|
|
99
|
+
view.setBigUint64(0, BigInt(this._lastPingTime), false);
|
|
100
|
+
|
|
101
|
+
this._callbacks.sendPing(payload);
|
|
102
|
+
|
|
103
|
+
// Set timeout for pong response
|
|
104
|
+
this._pongTimeoutHandle = setTimeout(() => {
|
|
105
|
+
this._missedPongs++;
|
|
106
|
+
if (this._missedPongs >= this._maxMissedPongs) {
|
|
107
|
+
this.stop();
|
|
108
|
+
this._callbacks.onDead();
|
|
109
|
+
}
|
|
110
|
+
}, this._pongTimeout);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Build a Pong payload echoing the Ping payload. */
|
|
115
|
+
export function buildPongPayload(pingPayload: Uint8Array): Uint8Array {
|
|
116
|
+
// Echo the same payload back
|
|
117
|
+
return new Uint8Array(pingPayload);
|
|
118
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MessageType,
|
|
3
|
+
type FramedMessage,
|
|
4
|
+
FRAME_HEADER_SIZE,
|
|
5
|
+
MAX_PAYLOAD_SIZE,
|
|
6
|
+
} from "../types/protocol.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Frame layout (9 bytes header + payload):
|
|
10
|
+
* [0] u8 — message type
|
|
11
|
+
* [1..4] u32 — message ID (big-endian)
|
|
12
|
+
* [5..8] u32 — payload length (big-endian)
|
|
13
|
+
* [9..] raw — payload bytes
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Encode a FramedMessage into a binary buffer. */
|
|
17
|
+
export function encodeFrame(msg: FramedMessage): Uint8Array {
|
|
18
|
+
if (msg.payload.length > MAX_PAYLOAD_SIZE) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Payload too large: ${msg.payload.length} > ${MAX_PAYLOAD_SIZE}`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const frame = new Uint8Array(FRAME_HEADER_SIZE + msg.payload.length);
|
|
25
|
+
const view = new DataView(frame.buffer);
|
|
26
|
+
|
|
27
|
+
view.setUint8(0, msg.type);
|
|
28
|
+
view.setUint32(1, msg.messageId);
|
|
29
|
+
view.setUint32(5, msg.payload.length);
|
|
30
|
+
frame.set(msg.payload, FRAME_HEADER_SIZE);
|
|
31
|
+
|
|
32
|
+
return frame;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const VALID_MESSAGE_TYPES = new Set<number>(
|
|
36
|
+
Object.values(MessageType).filter((v): v is number => typeof v === "number"),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
function isValidMessageType(value: number): boolean {
|
|
40
|
+
return VALID_MESSAGE_TYPES.has(value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Decode a single FramedMessage from a binary buffer.
|
|
45
|
+
* Returns the message and the number of bytes consumed.
|
|
46
|
+
* Returns null if the buffer doesn't contain a complete frame.
|
|
47
|
+
*/
|
|
48
|
+
export function decodeFrame(
|
|
49
|
+
data: Uint8Array
|
|
50
|
+
): { message: FramedMessage; bytesConsumed: number } | null {
|
|
51
|
+
if (data.length < FRAME_HEADER_SIZE) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
56
|
+
const rawType = view.getUint8(0);
|
|
57
|
+
if (!isValidMessageType(rawType)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const type = rawType as MessageType;
|
|
61
|
+
const messageId = view.getUint32(1);
|
|
62
|
+
const payloadLength = view.getUint32(5);
|
|
63
|
+
|
|
64
|
+
if (payloadLength > MAX_PAYLOAD_SIZE) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Payload length ${payloadLength} exceeds maximum ${MAX_PAYLOAD_SIZE}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const totalLength = FRAME_HEADER_SIZE + payloadLength;
|
|
71
|
+
if (data.length < totalLength) {
|
|
72
|
+
return null; // incomplete frame
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const payload = data.slice(FRAME_HEADER_SIZE, totalLength);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
message: { type, messageId, payload },
|
|
79
|
+
bytesConsumed: totalLength,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Streaming frame decoder that handles partial frames across
|
|
85
|
+
* multiple data chunks (e.g., from a DataChannel).
|
|
86
|
+
*/
|
|
87
|
+
export class FrameDecoder {
|
|
88
|
+
private _buffer: Uint8Array = new Uint8Array(0);
|
|
89
|
+
|
|
90
|
+
/** Feed new data and return any complete frames. */
|
|
91
|
+
feed(chunk: Uint8Array): FramedMessage[] {
|
|
92
|
+
// Append chunk to buffer
|
|
93
|
+
const newBuffer = new Uint8Array(this._buffer.length + chunk.length);
|
|
94
|
+
newBuffer.set(this._buffer, 0);
|
|
95
|
+
newBuffer.set(chunk, this._buffer.length);
|
|
96
|
+
this._buffer = newBuffer;
|
|
97
|
+
|
|
98
|
+
const messages: FramedMessage[] = [];
|
|
99
|
+
|
|
100
|
+
while (true) {
|
|
101
|
+
let result: ReturnType<typeof decodeFrame>;
|
|
102
|
+
try {
|
|
103
|
+
result = decodeFrame(this._buffer);
|
|
104
|
+
} catch {
|
|
105
|
+
this._buffer = new Uint8Array(0);
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
if (!result) break;
|
|
109
|
+
|
|
110
|
+
messages.push(result.message);
|
|
111
|
+
this._buffer = this._buffer.slice(result.bytesConsumed);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return messages;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Reset the internal buffer. */
|
|
118
|
+
reset(): void {
|
|
119
|
+
this._buffer = new Uint8Array(0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Number of buffered bytes not yet decoded. */
|
|
123
|
+
get bufferedBytes(): number {
|
|
124
|
+
return this._buffer.length;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Handler function for a specific message type. */
|
|
129
|
+
export type MessageHandler = (msg: FramedMessage) => void | Promise<void>;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Message multiplexer — routes decoded frames to registered handlers.
|
|
133
|
+
*/
|
|
134
|
+
export class MessageMux {
|
|
135
|
+
private _handlers = new Map<MessageType, MessageHandler[]>();
|
|
136
|
+
private _defaultHandler: MessageHandler | null = null;
|
|
137
|
+
|
|
138
|
+
/** Register a handler for a specific message type. */
|
|
139
|
+
on(type: MessageType, handler: MessageHandler): void {
|
|
140
|
+
const existing = this._handlers.get(type) ?? [];
|
|
141
|
+
existing.push(handler);
|
|
142
|
+
this._handlers.set(type, existing);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Remove a handler for a specific message type. */
|
|
146
|
+
off(type: MessageType, handler: MessageHandler): void {
|
|
147
|
+
const existing = this._handlers.get(type);
|
|
148
|
+
if (!existing) return;
|
|
149
|
+
const idx = existing.indexOf(handler);
|
|
150
|
+
if (idx !== -1) {
|
|
151
|
+
existing.splice(idx, 1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Set a default handler for unregistered message types. */
|
|
156
|
+
setDefaultHandler(handler: MessageHandler): void {
|
|
157
|
+
this._defaultHandler = handler;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Dispatch a framed message to registered handlers. */
|
|
161
|
+
async dispatch(msg: FramedMessage): Promise<void> {
|
|
162
|
+
const handlers = this._handlers.get(msg.type);
|
|
163
|
+
if (handlers && handlers.length > 0) {
|
|
164
|
+
for (const handler of handlers) {
|
|
165
|
+
await handler(msg);
|
|
166
|
+
}
|
|
167
|
+
} else if (this._defaultHandler) {
|
|
168
|
+
await this._defaultHandler(msg);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|