@steve02081504/fount-p2p 0.0.26 → 0.0.28

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 (100) hide show
  1. package/README.md +3 -2
  2. package/core/bytes_codec.mjs +5 -6
  3. package/core/composite_key.mjs +0 -79
  4. package/core/entity_id_parse.mjs +2 -3
  5. package/core/evfs_logical_path.mjs +2 -3
  6. package/core/hexIds.mjs +5 -5
  7. package/core/logical_entity.mjs +3 -4
  8. package/crypto/channel.mjs +2 -25
  9. package/crypto/checkpoint_sign.mjs +5 -4
  10. package/crypto/crypto.mjs +0 -8
  11. package/crypto/key.mjs +9 -10
  12. package/dag/canonicalize_row.mjs +1 -1
  13. package/discovery/advert_peer_hints.mjs +1 -1
  14. package/discovery/adverts.mjs +7 -12
  15. package/discovery/bt/index.mjs +6 -11
  16. package/discovery/bt/peer_hints.mjs +2 -3
  17. package/discovery/bt/runtime.mjs +1 -1
  18. package/discovery/index.mjs +14 -14
  19. package/discovery/lan.mjs +3 -3
  20. package/discovery/lan_interfaces.mjs +2 -2
  21. package/discovery/lan_peer_hints.mjs +1 -1
  22. package/discovery/nostr.mjs +19 -24
  23. package/federation/entity_key_chain.mjs +5 -24
  24. package/federation/message_rate_limit.mjs +3 -2
  25. package/federation/part_query/runtime.mjs +9 -20
  26. package/files/assemble.mjs +3 -3
  27. package/files/assemble_stream.mjs +1 -1
  28. package/files/chunk/fetch.mjs +1 -2
  29. package/files/chunk/responder.mjs +4 -41
  30. package/files/chunk/store.mjs +2 -3
  31. package/files/chunk/verify.mjs +2 -3
  32. package/files/evfs.mjs +1 -1
  33. package/files/evfs_ref.mjs +3 -4
  34. package/files/fetch_fanout.mjs +3 -3
  35. package/files/manifest/fetch.mjs +2 -3
  36. package/files/manifest/normalize.mjs +9 -9
  37. package/files/manifest/pending.mjs +1 -4
  38. package/files/manifest/public.mjs +8 -8
  39. package/files/transfer_key.mjs +3 -3
  40. package/files/transfer_key_registry.mjs +6 -6
  41. package/governance/branch.mjs +2 -1
  42. package/governance/join_pow.mjs +7 -5
  43. package/governance/owner_succession_ballot.mjs +6 -6
  44. package/index.mjs +8 -2
  45. package/infra/default_node_dir.mjs +2 -2
  46. package/infra/priority.mjs +2 -2
  47. package/link/frame.mjs +2 -3
  48. package/link/handshake.mjs +6 -6
  49. package/link/pipe.mjs +69 -5
  50. package/link/providers/ble_gatt.mjs +1 -9
  51. package/link/providers/index.mjs +2 -2
  52. package/link/providers/nostr.mjs +1 -3
  53. package/link/providers/webrtc.mjs +2 -2
  54. package/link/rtc/ice_local_hostname.mjs +6 -5
  55. package/link/rtc/w3c_bridge.mjs +1 -1
  56. package/link/sdp_fingerprint.mjs +1 -1
  57. package/mailbox/consumer_registry.mjs +2 -2
  58. package/mailbox/deliver_or_store.mjs +4 -4
  59. package/mailbox/parse.mjs +1 -1
  60. package/mailbox/prune.mjs +2 -2
  61. package/mailbox/rate.mjs +3 -10
  62. package/mailbox/store.mjs +7 -7
  63. package/node/denylist.mjs +38 -38
  64. package/node/entity_store.mjs +5 -5
  65. package/node/identity.mjs +7 -8
  66. package/node/instance.mjs +1 -1
  67. package/node/log.mjs +1 -1
  68. package/node/network.mjs +14 -16
  69. package/node/personal_block.mjs +22 -20
  70. package/node/reputation_store.mjs +11 -19
  71. package/node/reputation_sync.mjs +0 -7
  72. package/node/signaling_config.mjs +1 -1
  73. package/node/storage_plugins.mjs +2 -2
  74. package/overlay/index.mjs +1 -1
  75. package/package.json +1 -1
  76. package/registries/event_type.mjs +2 -2
  77. package/registries/room_provider.mjs +2 -2
  78. package/reputation/engine.mjs +17 -15
  79. package/schemas/discovery.mjs +5 -5
  80. package/schemas/federation_pull.mjs +16 -24
  81. package/schemas/part_query.mjs +4 -4
  82. package/timeline/verify_remote.mjs +4 -4
  83. package/transport/group_link_set.mjs +3 -3
  84. package/transport/ice_servers.mjs +2 -2
  85. package/transport/link_registry.mjs +51 -20
  86. package/transport/mesh_keepalive.mjs +1 -1
  87. package/transport/node_scope/wire.mjs +1 -1
  88. package/transport/peer_health.mjs +127 -0
  89. package/transport/remote_user_room.mjs +13 -15
  90. package/transport/rtc_connection_budget.mjs +6 -6
  91. package/transport/runtime_bootstrap.mjs +12 -12
  92. package/trust_graph/cache.mjs +2 -3
  93. package/trust_graph/engine.mjs +1 -1
  94. package/trust_graph/registry.mjs +2 -2
  95. package/trust_graph/send.mjs +1 -1
  96. package/utils/async_mutex.mjs +1 -1
  97. package/wire/ingress.mjs +1 -1
  98. package/wire/part/ingress.mjs +6 -6
  99. package/wire/part/query.mjs +3 -3
  100. package/wire/volatile_signature.mjs +4 -9
@@ -0,0 +1,127 @@
1
+ import { normalizeHex64 } from '../core/hexIds.mjs'
2
+ import { emitSafe } from '../utils/emit_safe.mjs'
3
+
4
+ /**
5
+ * @typedef {{
6
+ * nodeHash: string
7
+ * connected: boolean
8
+ * rttMs: number | null
9
+ * avgRttMs: number | null
10
+ * lastSeenAt: number
11
+ * source: string | null
12
+ * }} PeerHealthEntry
13
+ */
14
+
15
+ /**
16
+ * 聚合邻居链路健康:在 mesh/link 常态维护期间顺带统计各邻居的 RTT 与可达性。
17
+ * 订阅 registry 的 link up/down,并对每条活跃链路监听 onRtt/onDown。
18
+ * @param {object} registry link registry(onLinkUp / onLinkDown)
19
+ * @returns {{
20
+ * getPeerHealth: (nodeHash: string) => PeerHealthEntry | null
21
+ * listPeerHealth: () => PeerHealthEntry[]
22
+ * onPeerHealth: (listener: (nodeHash: string, entry: PeerHealthEntry) => void) => () => void
23
+ * stop: () => void
24
+ * }} peer health 聚合器
25
+ */
26
+ export function createPeerHealthTracker(registry) {
27
+ /** @type {Map<string, PeerHealthEntry>} */
28
+ const entries = new Map()
29
+ /** @type {Map<string, () => void>} 每条链路在 registry/link 上的取消订阅回调 */
30
+ const cleanups = new Map()
31
+ /** @type {Set<(nodeHash: string, entry: PeerHealthEntry) => void>} */
32
+ const listeners = new Set()
33
+
34
+ /**
35
+ * 合并补丁并通知订阅方。
36
+ * @param {string} nodeHash 远端节点 64 hex
37
+ * @param {Partial<PeerHealthEntry>} patch 补丁
38
+ * @returns {void}
39
+ */
40
+ function update(nodeHash, patch) {
41
+ const entry = entries.get(nodeHash) ?? {
42
+ nodeHash,
43
+ connected: false,
44
+ rttMs: null,
45
+ avgRttMs: null,
46
+ lastSeenAt: 0,
47
+ source: null,
48
+ }
49
+ if (patch.connected !== undefined) entry.connected = patch.connected
50
+ if (patch.rttMs !== undefined) entry.rttMs = patch.rttMs
51
+ if (patch.avgRttMs !== undefined) entry.avgRttMs = patch.avgRttMs
52
+ if (patch.lastSeenAt !== undefined) entry.lastSeenAt = patch.lastSeenAt
53
+ if (patch.source !== undefined) entry.source = patch.source
54
+ entries.set(nodeHash, entry)
55
+ emitSafe(listeners, nodeHash, entry)
56
+ }
57
+
58
+ const stopUp = registry.onLinkUp?.((nodeHash, link) => {
59
+ const hash = normalizeHex64(nodeHash)
60
+ if (!hash) return
61
+ const stopRtt = link?.onRtt?.(() => {
62
+ const stats = link.stats?.() ?? {}
63
+ update(hash, {
64
+ rttMs: stats.rttMs ?? null,
65
+ avgRttMs: stats.avgRttMs ?? null,
66
+ lastSeenAt: Date.now(),
67
+ })
68
+ }) ?? null
69
+ const stopDown = link?.onDown?.(() => {
70
+ cleanups.get(hash)?.()
71
+ cleanups.delete(hash)
72
+ update(hash, { connected: false, lastSeenAt: Date.now() })
73
+ }) ?? null
74
+ update(hash, {
75
+ connected: true,
76
+ source: link?.providerId ?? null,
77
+ lastSeenAt: Date.now(),
78
+ })
79
+ cleanups.set(hash, () => {
80
+ stopRtt?.()
81
+ stopDown?.()
82
+ })
83
+ }) ?? null
84
+ const stopDown = registry.onLinkDown?.(nodeHash => {
85
+ const hash = normalizeHex64(nodeHash)
86
+ if (!hash) return
87
+ cleanups.get(hash)?.()
88
+ cleanups.delete(hash)
89
+ update(hash, { connected: false, lastSeenAt: Date.now() })
90
+ }) ?? null
91
+
92
+ return {
93
+ /**
94
+ * @param {string} nodeHash 远端节点 64 hex
95
+ * @returns {PeerHealthEntry | null} 健康记录;无记录时 null
96
+ */
97
+ getPeerHealth(nodeHash) {
98
+ return entries.get(normalizeHex64(nodeHash)) ?? null
99
+ },
100
+ /**
101
+ * @returns {PeerHealthEntry[]} 所有邻居健康记录
102
+ */
103
+ listPeerHealth() {
104
+ return [...entries.values()]
105
+ },
106
+ /**
107
+ * @param {(nodeHash: string, entry: PeerHealthEntry) => void} listener 变化回调
108
+ * @returns {() => void} 取消订阅
109
+ */
110
+ onPeerHealth(listener) {
111
+ listeners.add(listener)
112
+ return () => listeners.delete(listener)
113
+ },
114
+ /**
115
+ * 停止监听并清空聚合。
116
+ * @returns {void}
117
+ */
118
+ stop() {
119
+ stopUp?.()
120
+ stopDown?.()
121
+ for (const cleanup of cleanups.values()) cleanup()
122
+ cleanups.clear()
123
+ entries.clear()
124
+ listeners.clear()
125
+ },
126
+ }
127
+ }
@@ -23,21 +23,19 @@ registerFederationRoomProvider('remote-user-room', () => {
23
23
 
24
24
  /**
25
25
  * 加入目标节点的用户房间(幂等)。
26
- * @param {string} username 本地 replica 用户名
27
26
  * @param {string} targetNodeHash 目标节点 64 hex
28
27
  * @returns {Promise<RemoteUserRoomSlot>} 房间槽
29
28
  */
30
- export async function ensureRemoteUserRoom(username, targetNodeHash) {
31
- const key = targetNodeHash.toLowerCase()
32
- const existing = slots.get(key)
29
+ export async function ensureRemoteUserRoom(targetNodeHash) {
30
+ const existing = slots.get(targetNodeHash)
33
31
  if (existing) return existing
34
- const inflight = inflights.get(key)
32
+ const inflight = inflights.get(targetNodeHash)
35
33
  if (inflight) return await inflight
36
34
 
37
35
  const task = (async () => {
38
36
  try {
39
- if (!await ensureLinkToNode(key))
40
- throw new Error(`p2p: ensureRemoteUserRoom link failed for ${key}`)
37
+ if (!await ensureLinkToNode(targetNodeHash))
38
+ throw new Error(`p2p: ensureRemoteUserRoom link failed for ${targetNodeHash}`)
41
39
 
42
40
  /** @type {import('../registries/room_provider.mjs').FederationRoomSlot} */
43
41
  const roomSlot = {
@@ -46,13 +44,13 @@ export async function ensureRemoteUserRoom(username, targetNodeHash) {
46
44
  * 返回远端用户房间 roster(链路存在时含目标节点)。
47
45
  * @returns {Array<{ peerId: string, remoteNodeHash: string }>} roster 列表
48
46
  */
49
- getRoster: () => getLink(key) ? [{ peerId: key, remoteNodeHash: key }] : [],
47
+ getRoster: () => getLink(targetNodeHash) ? [{ peerId: targetNodeHash, remoteNodeHash: targetNodeHash }] : [],
50
48
  /**
51
49
  * 按 nodeHash 查找 peer id。
52
50
  * @param {string} nh 目标节点 64 hex
53
51
  * @returns {string | null} 对端 id;无链路时为 null
54
52
  */
55
- getPeerIdByNodeHash: nh => getLink(nh) ? String(nh) : null,
53
+ getPeerIdByNodeHash: nh => getLink(nh) ? nh : null,
56
54
  /**
57
55
  * 经 node scope 向远端 peer 发送 action。始终走当前规范链路(`getLink`),
58
56
  * 因为 glare 双 PC 择一后最初返回的链路可能已被关闭,规范链在 registry 内。
@@ -62,7 +60,7 @@ export async function ensureRemoteUserRoom(username, targetNodeHash) {
62
60
  * @returns {void}
63
61
  */
64
62
  sendToPeer(peerId, actionName, payload) {
65
- void getLink(key)?.send({ scope: 'node', action: actionName, payload }).catch(() => { })
63
+ void getLink(targetNodeHash)?.send({ scope: 'node', action: actionName, payload }).catch(() => { })
66
64
  },
67
65
  }
68
66
 
@@ -73,19 +71,19 @@ export async function ensureRemoteUserRoom(username, targetNodeHash) {
73
71
  * @returns {Promise<void>} 关闭完成
74
72
  */
75
73
  leave() {
76
- slots.delete(key)
77
- return closeLink(key, 'remote-user-room-release')
74
+ slots.delete(targetNodeHash)
75
+ return closeLink(targetNodeHash, 'remote-user-room-release')
78
76
  },
79
77
  }
80
- slots.set(key, slot)
78
+ slots.set(targetNodeHash, slot)
81
79
  bumpLocalDataRevision()
82
80
  return slot
83
81
  }
84
82
  finally {
85
- inflights.delete(key)
83
+ inflights.delete(targetNodeHash)
86
84
  }
87
85
  })()
88
86
 
89
- inflights.set(key, task)
87
+ inflights.set(targetNodeHash, task)
90
88
  return await task
91
89
  }
@@ -48,7 +48,7 @@ function bucketFor(roomKey, limits = {}) {
48
48
  budgets.set(roomKey, bucket)
49
49
  }
50
50
  for (const id of resolveRtcBudgetLimits(limits).trustedPeers || [])
51
- bucket.trustedPeers.add(String(id))
51
+ bucket.trustedPeers.add(id)
52
52
  return bucket
53
53
  }
54
54
 
@@ -89,7 +89,7 @@ export function takeRtcJoinSlot(roomKey, peerId, limits = {}, sourceId = 'peer')
89
89
  for (const id of bucket.active)
90
90
  if (!bucket.trustedPeers.has(id)) nonTrustedCount++
91
91
  if (!isTrusted) {
92
- const source = String(sourceId || 'peer')
92
+ const source = sourceId || 'peer'
93
93
  let sameSource = 0
94
94
  for (const peerSource of bucket.sourceByPeer.values())
95
95
  if (peerSource === source) sameSource++
@@ -108,7 +108,7 @@ export function takeRtcJoinSlot(roomKey, peerId, limits = {}, sourceId = 'peer')
108
108
  bucket.joinTimestamps.push(now)
109
109
  if (peerId) {
110
110
  bucket.active.add(peerId)
111
- bucket.sourceByPeer.set(peerId, String(sourceId || 'peer'))
111
+ bucket.sourceByPeer.set(peerId, sourceId || 'peer')
112
112
  }
113
113
  return true
114
114
  }
@@ -123,9 +123,9 @@ export function takeRtcJoinSlot(roomKey, peerId, limits = {}, sourceId = 'peer')
123
123
  export function annotateRtcPeerNodeHash(roomKey, peerId, nodeHash, limits = {}) {
124
124
  const bucket = budgets.get(roomKey)
125
125
  if (!bucket || !peerId || !nodeHash) return
126
- bucket.peerNodeHash.set(peerId, String(nodeHash).trim())
126
+ bucket.peerNodeHash.set(peerId, nodeHash)
127
127
  for (const trusted of resolveRtcBudgetLimits(limits).trustedPeers || [])
128
- if (String(trusted).trim() === String(nodeHash).trim())
128
+ if (trusted === nodeHash)
129
129
  bucket.trustedPeers.add(peerId)
130
130
 
131
131
  }
@@ -139,7 +139,7 @@ export function annotateRtcPeerNodeHash(roomKey, peerId, nodeHash, limits = {})
139
139
  export function setRtcPeerSource(roomKey, peerId, sourceId) {
140
140
  const bucket = budgets.get(roomKey)
141
141
  if (!bucket || !peerId) return
142
- bucket.sourceByPeer.set(peerId, String(sourceId || 'peer'))
142
+ bucket.sourceByPeer.set(peerId, sourceId || 'peer')
143
143
  }
144
144
 
145
145
  /**
@@ -41,14 +41,14 @@ export function collectFastListenProviders(ownedLanTcp) {
41
41
  const listenProviders = []
42
42
  if (ownedLanTcp) listenProviders.push(ownedLanTcp)
43
43
  for (const provider of listLinkProviders()) {
44
- const id = String(provider.id)
44
+ const { id } = provider
45
45
  if (id.startsWith('lan_tcp') || id.startsWith('ble_gatt')) continue
46
- if (typeof provider.ensureListening !== 'function') continue
46
+ if (!provider.ensureListening) continue
47
47
  if (providerHasNativeProbe(provider)) continue
48
- if (typeof provider.isAvailable === 'function')
48
+ if (provider.isAvailable)
49
49
  try {
50
50
  const available = provider.isAvailable()
51
- if (available && typeof available.then === 'function') continue
51
+ if (available?.then) continue
52
52
  if (!available) continue
53
53
  }
54
54
  catch { continue }
@@ -166,7 +166,7 @@ export function createRuntimeBootstrap(deps) {
166
166
  * @returns {number | null} 本机 lan_tcp 监听端口,未就绪为 null
167
167
  */
168
168
  function lanTcpPort() {
169
- const endpoint = typeof ownedLanTcp?.localEndpoint === 'function' ? ownedLanTcp.localEndpoint() : null
169
+ const endpoint = ownedLanTcp?.localEndpoint ? ownedLanTcp.localEndpoint() : null
170
170
  return normalizeTcpPort(endpoint?.port)
171
171
  }
172
172
 
@@ -199,13 +199,13 @@ export function createRuntimeBootstrap(deps) {
199
199
  * @returns {Promise<void>}
200
200
  */
201
201
  async function startProviderListening(provider) {
202
- if (typeof provider.ensureListening !== 'function') return
202
+ if (!provider.ensureListening) return
203
203
  try {
204
204
  const stop = await provider.ensureListening({
205
205
  localIdentity,
206
206
  onInbound: onInboundLink,
207
207
  })
208
- if (typeof stop === 'function') stopLinkListeners.push(stop)
208
+ if (stop) stopLinkListeners.push(stop)
209
209
  }
210
210
  catch { /* provider listen unavailable */ }
211
211
  }
@@ -228,18 +228,18 @@ export function createRuntimeBootstrap(deps) {
228
228
  if (!providerIds.has('bt')) {
229
229
  const bt = await import('../discovery/bt/index.mjs').catch(() => null)
230
230
  if (generation !== gen || !isLive()) return
231
- if (await bt?.canUseBluetoothDiscovery?.()) {
231
+ if (await bt?.canUseBluetoothRuntime?.()) {
232
232
  if (generation !== gen || !isLive()) return
233
233
  const provider = bt.createBluetoothDiscoveryProvider()
234
234
  registerDiscoveryProvider(provider)
235
235
  if (generation !== gen || !isLive()) return
236
- if (typeof provider.startPresence === 'function')
236
+ if (provider.startPresence)
237
237
  try {
238
238
  const stop = await provider.startPresence(async () => ({
239
239
  nodeHash: localIdentity.nodeHash,
240
240
  advertBytes: await buildNetworkAdvertBytes(),
241
241
  }))
242
- if (typeof stop === 'function' && generation === gen && isLive()) {
242
+ if (stop && generation === gen && isLive()) {
243
243
  const prev = stopPresence
244
244
  /**
245
245
  * 停止 presence 广播并链式调用上一轮清理。
@@ -248,12 +248,12 @@ export function createRuntimeBootstrap(deps) {
248
248
  }
249
249
  }
250
250
  catch { /* ignore */ }
251
- if (provider.caps?.canSignal && typeof provider.listenNodeSignals === 'function')
251
+ if (provider.caps?.canSignal && provider.listenNodeSignals)
252
252
  try {
253
253
  const stop = await provider.listenNodeSignals(localIdentity.nodeHash, bytes => {
254
254
  void handleIncomingSignal(bytes).catch(() => { })
255
255
  })
256
- if (typeof stop === 'function' && generation === gen && isLive()) {
256
+ if (stop && generation === gen && isLive()) {
257
257
  const prev = stopSignalListener
258
258
  /**
259
259
  * 停止信令监听并链式调用上一轮清理。
@@ -22,10 +22,9 @@ export function invalidateTrustGraphCache() {
22
22
  * @returns {Promise<Map<string, object>>} 合并后的信任图
23
23
  */
24
24
  export async function getCachedTrustGraph(username, build, ttlMs = DEFAULT_TTL_MS) {
25
- const key = String(username || '')
26
25
  const now = Date.now()
27
26
  const dataRevision = getLocalDataRevision()
28
- const cached = cacheByUsername.get(key)
27
+ const cached = cacheByUsername.get(username)
29
28
  if (
30
29
  cached
31
30
  && cached.revision === revision
@@ -35,6 +34,6 @@ export async function getCachedTrustGraph(username, build, ttlMs = DEFAULT_TTL_M
35
34
  return cached.graph
36
35
 
37
36
  const graph = await build()
38
- cacheByUsername.set(key, { graph, builtAt: now, revision, dataRevision })
37
+ cacheByUsername.set(username, { graph, builtAt: now, revision, dataRevision })
39
38
  return graph
40
39
  }
@@ -89,7 +89,7 @@ export function mergeGraph(inputs, tunables = trustGraphTunables) {
89
89
  const rawWeight = Number(hint.weight ?? tunables.hintDefaultWeight)
90
90
  if (Number.isFinite(rawWeight) && rawWeight > 0)
91
91
  ev.hintWeightSum += rawWeight
92
- ev.hintSources.add(String(hint.source ?? 'unknown'))
92
+ ev.hintSources.add(hint.source ?? 'unknown')
93
93
  }
94
94
 
95
95
  for (const room of inputs.roomRosters || [])
@@ -13,7 +13,7 @@ export const DEFAULT_TRUST_GRAPH_OWNER = 'default'
13
13
  * @returns {void}
14
14
  */
15
15
  export function registerTrustGraphProvider(ownerId, implementation) {
16
- providersByOwner.set(String(ownerId), implementation)
16
+ providersByOwner.set(ownerId, implementation)
17
17
  }
18
18
 
19
19
  /** @returns {void} */
@@ -26,7 +26,7 @@ export function clearTrustGraphProvider() {
26
26
  * @returns {import('./registry.mjs').TrustGraphProvider} 已注册实现
27
27
  */
28
28
  export function requireTrustGraphProvider(ownerId = DEFAULT_TRUST_GRAPH_OWNER) {
29
- const implementation = providersByOwner.get(String(ownerId))
29
+ const implementation = providersByOwner.get(ownerId)
30
30
  if (!implementation)
31
31
  throw new Error(`p2p: registerTrustGraphProvider('${ownerId}') must run before trust graph fanout`)
32
32
  return implementation
@@ -20,7 +20,7 @@ import trustGraphTunables from './tunables.json' with { type: 'json' }
20
20
  * @returns {Promise<boolean>} 是否已发送
21
21
  */
22
22
  export async function sendToNode(username, targetNodeHash, actionName, payload, graph) {
23
- const target = normalizeHex64(targetNodeHash) || String(targetNodeHash || '').trim().toLowerCase()
23
+ const target = normalizeHex64(targetNodeHash)
24
24
  if (!isHex64(target)) return false
25
25
 
26
26
  // 已直连 peer 不经 trust-graph scope 也应能收发 node scope action(非成员 CAS chunk / follow hint 等)
@@ -39,7 +39,7 @@ function releaseMutex(lockKey, state) {
39
39
  * @template T
40
40
  */
41
41
  export async function withAsyncMutex(key, criticalSection) {
42
- const lockKey = String(key)
42
+ const lockKey = key
43
43
  const state = mutexState(lockKey)
44
44
  return new Promise((resolve, reject) => {
45
45
  /**
package/wire/ingress.mjs CHANGED
@@ -46,6 +46,6 @@ export function isSignedDagEventRow(event) {
46
46
  */
47
47
  export function extractInboundSignedEvent(payload, groupId) {
48
48
  if (!isSignedDagEventRow(payload)) return null
49
- if (payload.groupId && String(payload.groupId) !== groupId) return null
49
+ if (payload.groupId && payload.groupId !== groupId) return null
50
50
  return payload
51
51
  }
@@ -21,15 +21,15 @@ import { handleIncomingPartInvokeResponse } from './pending.mjs'
21
21
  * @returns {object | null} 白名单字段
22
22
  */
23
23
  function parsePartTimelinePut(data, partpath) {
24
- const timelineEntityHash = String(data.timelineEntityHash || '').trim().toLowerCase()
24
+ const timelineEntityHash = String(data.timelineEntityHash || '')
25
25
  if (!timelineEntityHash || !isPlainObject(data.event)) return null
26
26
  return {
27
27
  type: 'part_timeline_put',
28
28
  partpath,
29
29
  timelineEntityHash,
30
30
  event: data.event,
31
- ...data.nodeHash ? { nodeHash: String(data.nodeHash).trim() } : {},
32
- ...data.groupId ? { groupId: String(data.groupId).trim() } : {},
31
+ ...data.nodeHash ? { nodeHash: String(data.nodeHash) } : {},
32
+ ...data.groupId ? { groupId: String(data.groupId) } : {},
33
33
  }
34
34
  }
35
35
 
@@ -60,8 +60,8 @@ async function dispatchPartInvoke(wireContext, payload) {
60
60
  if (!partpath || !isPlainObject(invoke)) return null
61
61
  return dispatchRpcInbound({
62
62
  replicaUsername: wireContext.replicaUsername,
63
- requesterNodeHash: payload.peerId ? String(payload.peerId).trim() : null,
64
- groupId: payload.groupId ? String(payload.groupId).trim() : undefined,
63
+ requesterNodeHash: payload.peerId || null,
64
+ groupId: payload.groupId || undefined,
65
65
  peerId: payload.peerId,
66
66
  }, {
67
67
  type: 'part_invoke',
@@ -93,7 +93,7 @@ export function attachPartWire(wireContext, wire, options = {}) {
93
93
  if (!message) return
94
94
  void dispatchDeliveryInbound({
95
95
  replicaUsername: wireContext.replicaUsername,
96
- requesterNodeHash: peerId ? String(peerId).trim() : null,
96
+ requesterNodeHash: peerId || null,
97
97
  }, message)
98
98
  },
99
99
  /**
@@ -48,11 +48,11 @@ export function attachPartQueryWire(wireContext, wire, dependencies = {}) {
48
48
  const request = parsePartQueryReq(data)
49
49
  if (!request) return
50
50
  if (!state.takeDedupe(request.requestId)) return
51
- const source = String(peerId || request.originNodeHash || '').trim().toLowerCase()
51
+ const source = peerId || request.originNodeHash
52
52
  if (source && !consumeWireRateBucket(`part_query:${source}`, {
53
53
  maxCount: partQueryTunables.ratePerSourcePerMin,
54
54
  })) return
55
- void processIncomingPartQueryRequest(wireContext, wire, request, String(peerId || ''), deps)
55
+ void processIncomingPartQueryRequest(wireContext, wire, request, peerId, deps)
56
56
  },
57
57
  /**
58
58
  * @param {unknown} data part_query_res 载荷
@@ -61,7 +61,7 @@ export function attachPartQueryWire(wireContext, wire, dependencies = {}) {
61
61
  part_query_res(data, peerId) {
62
62
  const response = parsePartQueryRes(data)
63
63
  if (!response) return
64
- handleIncomingPartQueryResponse(response, String(peerId || ''), deps)
64
+ handleIncomingPartQueryResponse(response, peerId, deps)
65
65
  },
66
66
  })
67
67
  }
@@ -28,10 +28,9 @@ export function boundStreamSlices(slices) {
28
28
  * @returns {Uint8Array} 验签消息字节
29
29
  */
30
30
  export function streamChunkSignBytes(pendingStreamId, chunkSeq, slices) {
31
- const streamId = String(pendingStreamId ?? '')
32
31
  const sequence = Number.isFinite(Number(chunkSeq)) ? String(chunkSeq) : '0'
33
32
  const body = JSON.stringify(slices ?? [])
34
- return new TextEncoder().encode(`${CHUNK_DOMAIN}\0${streamId}\0${sequence}\0${body}`)
33
+ return new TextEncoder().encode(`${CHUNK_DOMAIN}\0${pendingStreamId}\0${sequence}\0${body}`)
35
34
  }
36
35
 
37
36
  /**
@@ -51,11 +50,9 @@ export async function signStreamSignatureHex(bytes, secretKey) {
51
50
  * @returns {Promise<boolean>} 验签是否通过
52
51
  */
53
52
  export async function verifyStreamSignatureHex(bytes, signatureHex, publicKeyHex) {
54
- const signature = String(signatureHex || '').trim().toLowerCase()
55
- const publicKey = String(publicKeyHex || '').trim().toLowerCase()
56
- if (!isSignatureHex128(signature) || !isHex64(publicKey)) return false
53
+ if (!isSignatureHex128(signatureHex) || !isHex64(publicKeyHex)) return false
57
54
  try {
58
- return await verify(Buffer.from(signature, 'hex'), bytes, Buffer.from(publicKey, 'hex'))
55
+ return await verify(Buffer.from(signatureHex, 'hex'), bytes, Buffer.from(publicKeyHex, 'hex'))
59
56
  }
60
57
  catch {
61
58
  return false
@@ -69,8 +66,6 @@ export async function verifyStreamSignatureHex(bytes, signatureHex, publicKeyHex
69
66
  * @returns {{ publicKey: Uint8Array, secretKey: Uint8Array }} 密钥对
70
67
  */
71
68
  export function streamKeyPairFromUserSeed(username, seedMaterial) {
72
- const user = String(username ?? '')
73
- const material = String(seedMaterial ?? '')
74
- const seed = createHash('sha256').update(`fount-stream-sign\0${user}\0${material}`).digest()
69
+ const seed = createHash('sha256').update(`fount-stream-sign\0${username}\0${seedMaterial}`).digest()
75
70
  return keyPairFromSeed(seed)
76
71
  }