@steve02081504/fount-p2p 0.0.4 → 0.0.6

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 (64) hide show
  1. package/core/bytes_codec.mjs +0 -27
  2. package/core/constants.mjs +0 -31
  3. package/core/hexIds.mjs +0 -13
  4. package/crypto/key.mjs +14 -15
  5. package/dag/canonicalize_row.mjs +5 -5
  6. package/dag/index.mjs +0 -73
  7. package/dag/storage.mjs +0 -22
  8. package/discovery/mdns.mjs +4 -4
  9. package/discovery/nostr.mjs +3 -3
  10. package/federation/chunk_fetch_pending.mjs +3 -3
  11. package/federation/chunk_fetch_scheduler.mjs +3 -3
  12. package/federation/dag_order_cache.mjs +3 -3
  13. package/federation/entity_key_chain.mjs +0 -9
  14. package/federation/topo_order_memo.mjs +3 -18
  15. package/files/assemble.mjs +55 -39
  16. package/files/assemble_stream.mjs +96 -44
  17. package/files/chunk_provider_registry.mjs +0 -6
  18. package/files/chunk_responder.mjs +1 -1
  19. package/files/chunk_store.mjs +1 -11
  20. package/files/evfs.mjs +74 -47
  21. package/files/manifest.mjs +11 -2
  22. package/files/manifest_acl_registry.mjs +0 -6
  23. package/files/transfer_key.mjs +21 -15
  24. package/files/transfer_key_registry.mjs +9 -16
  25. package/governance/branch.mjs +0 -11
  26. package/governance/join_pow.mjs +17 -17
  27. package/link/channel_mux.mjs +9 -7
  28. package/link/frame.mjs +5 -5
  29. package/link/handshake.mjs +17 -17
  30. package/link/link.mjs +33 -33
  31. package/mailbox/deliver_or_store.mjs +13 -13
  32. package/mailbox/importance.mjs +0 -33
  33. package/mailbox/settings.mjs +0 -9
  34. package/mailbox/wire.mjs +4 -4
  35. package/node/denylist.mjs +0 -24
  36. package/node/network.mjs +0 -51
  37. package/node/personal_block.mjs +0 -35
  38. package/node/reputation_store.mjs +5 -14
  39. package/node/retention_policy.mjs +8 -23
  40. package/overlay/index.mjs +6 -6
  41. package/package.json +1 -1
  42. package/registries/event_type.mjs +0 -12
  43. package/registries/inbound.mjs +8 -30
  44. package/reputation/engine.mjs +0 -11
  45. package/rooms/scoped_link.mjs +18 -18
  46. package/schemas/part_query.mjs +156 -0
  47. package/timeline/append_core.mjs +3 -3
  48. package/transport/group_link_set.mjs +30 -30
  49. package/transport/ice_servers.mjs +0 -8
  50. package/transport/link_registry.mjs +13 -13
  51. package/transport/peer_identity_maps.mjs +0 -58
  52. package/transport/peer_pool.mjs +4 -4
  53. package/transport/remote_user_room.mjs +0 -12
  54. package/transport/rtc_connection_budget.mjs +2 -0
  55. package/transport/user_room.mjs +13 -24
  56. package/trust_graph/cache.mjs +0 -13
  57. package/trust_graph/engine.mjs +0 -24
  58. package/utils/async_mutex.mjs +0 -9
  59. package/wire/group_part.mjs +3 -3
  60. package/wire/part_fanout.mjs +0 -19
  61. package/wire/part_ingress.mjs +14 -14
  62. package/wire/part_query.mjs +486 -0
  63. package/wire/part_query.tunables.json +14 -0
  64. package/wire/part_query_cache.mjs +101 -0
@@ -85,40 +85,40 @@ export function powVoluntaryBonus(achievedBits, floorBits, tunables = admissionT
85
85
 
86
86
  /**
87
87
  * @param {object} powSolution 客户端 solution
88
- * @param {object} opts 校验上下文
89
- * @param {string} opts.groupId 群 ID
90
- * @param {string} opts.senderNodeHash 签名者 nodeHash(须与 joiner 绑定)
91
- * @param {string[]} opts.knownAnchors 近期 tip / checkpoint root 列表
92
- * @param {number} [opts.now=Date.now()] 当前时间
93
- * @param {number} [opts.difficultyBits=0] 准入 floor(前导零 bit)
94
- * @param {number} [opts.epochMs=JOIN_POW_DEFAULT_EPOCH_MS] epoch 长度
95
- * @param {number} [opts.epochSkew=JOIN_POW_DEFAULT_EPOCH_SKEW] 允许 epoch 偏移
88
+ * @param {object} options 校验上下文
89
+ * @param {string} options.groupId 群 ID
90
+ * @param {string} options.senderNodeHash 签名者 nodeHash(须与 joiner 绑定)
91
+ * @param {string[]} options.knownAnchors 近期 tip / checkpoint root 列表
92
+ * @param {number} [options.now=Date.now()] 当前时间
93
+ * @param {number} [options.difficultyBits=0] 准入 floor(前导零 bit)
94
+ * @param {number} [options.epochMs=JOIN_POW_DEFAULT_EPOCH_MS] epoch 长度
95
+ * @param {number} [options.epochSkew=JOIN_POW_DEFAULT_EPOCH_SKEW] 允许 epoch 偏移
96
96
  * @returns {{ ok: boolean, achievedBits: number }} 校验结果与实际达成 bit
97
97
  */
98
- export function verifyJoinPow(powSolution, opts) {
99
- const floorBits = Math.max(0, Math.floor(Number(opts.difficultyBits) || 0))
98
+ export function verifyJoinPow(powSolution, options) {
99
+ const floorBits = Math.max(0, Math.floor(Number(options.difficultyBits) || 0))
100
100
  if (floorBits <= 0) return { ok: true, achievedBits: 0 }
101
101
  if (!powSolution || typeof powSolution !== 'object') return { ok: false, achievedBits: 0 }
102
102
 
103
103
  const anchorRef = String(powSolution.anchorRef ?? '').trim()
104
104
  const { nonce } = powSolution
105
105
  const epoch = Number(powSolution.epoch)
106
- const joinerNodeHash = String(powSolution.joinerNodeHash ?? opts.senderNodeHash ?? '').trim().toLowerCase()
107
- const senderNodeHash = String(opts.senderNodeHash ?? '').trim().toLowerCase()
106
+ const joinerNodeHash = String(powSolution.joinerNodeHash ?? options.senderNodeHash ?? '').trim().toLowerCase()
107
+ const senderNodeHash = String(options.senderNodeHash ?? '').trim().toLowerCase()
108
108
 
109
109
  if (!anchorRef || nonce == null || !Number.isFinite(epoch)) return { ok: false, achievedBits: 0 }
110
110
  if (!joinerNodeHash || joinerNodeHash !== senderNodeHash) return { ok: false, achievedBits: 0 }
111
111
 
112
- const anchors = (opts.knownAnchors ?? []).map(a => String(a).trim()).filter(Boolean)
112
+ const anchors = (options.knownAnchors ?? []).map(a => String(a).trim()).filter(Boolean)
113
113
  if (!anchors.length || !anchors.includes(anchorRef)) return { ok: false, achievedBits: 0 }
114
114
 
115
- const epochMs = Math.max(60_000, Number(opts.epochMs) || JOIN_POW_DEFAULT_EPOCH_MS)
116
- const skew = Math.max(0, Math.floor(Number(opts.epochSkew) ?? JOIN_POW_DEFAULT_EPOCH_SKEW))
117
- const nowEpoch = Math.floor((opts.now ?? Date.now()) / epochMs)
115
+ const epochMs = Math.max(60_000, Number(options.epochMs) || JOIN_POW_DEFAULT_EPOCH_MS)
116
+ const skew = Math.max(0, Math.floor(Number(options.epochSkew) ?? JOIN_POW_DEFAULT_EPOCH_SKEW))
117
+ const nowEpoch = Math.floor((options.now ?? Date.now()) / epochMs)
118
118
  if (Math.abs(epoch - nowEpoch) > skew) return { ok: false, achievedBits: 0 }
119
119
 
120
120
  const hash = computeJoinPowHash({
121
- groupId: opts.groupId,
121
+ groupId: options.groupId,
122
122
  anchorRef,
123
123
  joinerNodeHash,
124
124
  epoch,
@@ -38,6 +38,8 @@ const DEFAULT_ACTION_PRIORITIES = Object.freeze({
38
38
  discovery_query_response: 3,
39
39
  part_invoke: 3,
40
40
  part_invoke_response: 3,
41
+ part_query_req: 5,
42
+ part_query_res: 5,
41
43
  char_rpc: 3,
42
44
  fed_chunk_put: 5,
43
45
  fed_chunk_get: 5,
@@ -108,15 +110,15 @@ export function configureBufferedAmountLowThreshold(channel, thresholdBytes = CH
108
110
  /**
109
111
  * 订阅 bufferedamountlow 事件,返回取消订阅函数。
110
112
  * @param {RTCDataChannel} channel RTC 数据通道
111
- * @param {() => void} cb 低水位回调
113
+ * @param {() => void} callback 低水位回调
112
114
  * @returns {() => void} 取消订阅函数
113
115
  */
114
- export function onBufferedAmountLow(channel, cb) {
116
+ export function onBufferedAmountLow(channel, callback) {
115
117
  /**
116
118
  * bufferedamountlow 事件处理函数。
117
119
  * @returns {void}
118
120
  */
119
- const handler = () => cb()
121
+ const handler = () => callback()
120
122
  channel.addEventListener?.('bufferedamountlow', handler)
121
123
  channel.onbufferedamountlow = handler
122
124
  if (channel.bufferedAmountLow?.subscribe)
@@ -129,12 +131,12 @@ export function onBufferedAmountLow(channel, cb) {
129
131
 
130
132
  /**
131
133
  * 创建带优先级队列的双通道发送器(control/bulk)。
132
- * @param {{ getChannel: (name: 'control' | 'bulk') => RTCDataChannel | null | undefined, highWatermarkBytes?: number }} opts 通道访问与高水位配置
134
+ * @param {{ getChannel: (name: 'control' | 'bulk') => RTCDataChannel | null | undefined, highWatermarkBytes?: number }} options 通道访问与高水位配置
133
135
  * @returns {{ enqueue: (action: string, bytes: Uint8Array, preferredChannel?: 'control' | 'bulk') => void, flush: (channelName?: 'control' | 'bulk') => void, pending: () => { control: number, bulk: number }, clear: () => void }} 发送队列 API
134
136
  */
135
- export function createChannelSendQueues(opts) {
136
- const { getChannel } = opts
137
- const highWatermarkBytes = Math.max(CHANNEL_LOW_THRESHOLD_BYTES, Number(opts.highWatermarkBytes) || CHANNEL_HIGH_WATERMARK_BYTES)
137
+ export function createChannelSendQueues(options) {
138
+ const { getChannel } = options
139
+ const highWatermarkBytes = Math.max(CHANNEL_LOW_THRESHOLD_BYTES, Number(options.highWatermarkBytes) || CHANNEL_HIGH_WATERMARK_BYTES)
138
140
  /** @type {{ control: Array<{ priority: number, seq: number, bytes: Uint8Array }>, bulk: Array<{ priority: number, seq: number, bytes: Uint8Array }> }} */
139
141
  const queues = { control: [], bulk: [] }
140
142
  /** @type {{ control: boolean, bulk: boolean }} */
package/link/frame.mjs CHANGED
@@ -150,13 +150,13 @@ function concatChunks(chunks) {
150
150
 
151
151
  /**
152
152
  * 创建分片消息重组器。
153
- * @param {{ maxMessageBytes?: number, maxPartials?: number, partialTimeoutMs?: number }} [opts] 大小、并发分片数与超时配置
153
+ * @param {{ maxMessageBytes?: number, maxPartials?: number, partialTimeoutMs?: number }} [options] 大小、并发分片数与超时配置
154
154
  * @returns {{ push: (frame: Uint8Array | ArrayBuffer | ArrayBufferView, now?: number) => Uint8Array | null, prune: (now?: number) => string[], clear: () => void, size: () => number }} 重组器 API
155
155
  */
156
- export function createReassembler(opts = {}) {
157
- const maxMessageBytes = Math.max(1024, Number(opts.maxMessageBytes) || DEFAULT_MAX_MESSAGE_BYTES)
158
- const maxPartials = Math.max(1, Number(opts.maxPartials) || DEFAULT_MAX_PARTIAL_MESSAGES)
159
- const partialTimeoutMs = Math.max(1000, Number(opts.partialTimeoutMs) || DEFAULT_PARTIAL_TIMEOUT_MS)
156
+ export function createReassembler(options = {}) {
157
+ const maxMessageBytes = Math.max(1024, Number(options.maxMessageBytes) || DEFAULT_MAX_MESSAGE_BYTES)
158
+ const maxPartials = Math.max(1, Number(options.maxPartials) || DEFAULT_MAX_PARTIAL_MESSAGES)
159
+ const partialTimeoutMs = Math.max(1000, Number(options.partialTimeoutMs) || DEFAULT_PARTIAL_TIMEOUT_MS)
160
160
  /** @type {Map<string, { total: number, chunks: Uint8Array[], seen: boolean[], bytes: number, firstSeenAt: number, lastSeenAt: number }>} */
161
161
  const partials = new Map()
162
162
 
@@ -34,18 +34,18 @@ export function buildAuthMessage(peerNonce, localFingerprint, localNodeHash) {
34
34
 
35
35
  /**
36
36
  * 构造 link hello 握手包。
37
- * @param {{ nodeHash?: string, nodePubKey?: string, nonce?: string }} [opts] 可选身份字段,省略则从本地节点种子推导
37
+ * @param {{ nodeHash?: string, nodePubKey?: string, nonce?: string }} [options] 可选身份字段,省略则从本地节点种子推导
38
38
  * @returns {{ v: 1, nodeHash: string, nodePubKey: string, nonce: string }} hello 对象
39
39
  */
40
- export function buildHello(opts = {}) {
40
+ export function buildHello(options = {}) {
41
41
  let publicKey = null
42
- if (!opts.nodeHash || !opts.nodePubKey) {
42
+ if (!options.nodeHash || !options.nodePubKey) {
43
43
  const derived = keyPairFromSeed(Buffer.from(ensureNodeSeed(), 'hex'))
44
44
  publicKey = derived.publicKey
45
45
  }
46
- const nodeHash = normalizeHex64(opts.nodeHash || getNodeHash())
47
- const nodePubKey = normalizeHex64(opts.nodePubKey || Buffer.from(publicKey).toString('hex'))
48
- const nonce = normalizeHex64(opts.nonce || randomBytes(32).toString('hex'))
46
+ const nodeHash = normalizeHex64(options.nodeHash || getNodeHash())
47
+ const nodePubKey = normalizeHex64(options.nodePubKey || Buffer.from(publicKey).toString('hex'))
48
+ const nonce = normalizeHex64(options.nonce || randomBytes(32).toString('hex'))
49
49
  if (!isHex64(nodeHash) || !isHex64(nodePubKey) || !isHex64(nonce))
50
50
  throw new Error('p2p: invalid hello fields')
51
51
  if (pubKeyHash(Buffer.from(nodePubKey, 'hex')) !== nodeHash)
@@ -57,15 +57,15 @@ export function buildHello(opts = {}) {
57
57
  * 对 link auth 消息签名。
58
58
  * @param {string} peerNonce 对端 hello 中的 nonce
59
59
  * @param {string} localFingerprint 本地 DTLS fingerprint
60
- * @param {{ secretKey?: Uint8Array, nodeHash?: string }} [opts] 签名密钥与 nodeHash 覆盖
60
+ * @param {{ secretKey?: Uint8Array, nodeHash?: string }} [options] 签名密钥与 nodeHash 覆盖
61
61
  * @returns {Promise<{ sig: string }>} hex 签名
62
62
  */
63
- export async function buildAuth(peerNonce, localFingerprint, opts = {}) {
64
- const seed = opts.secretKey
65
- ? Buffer.from(opts.secretKey)
63
+ export async function buildAuth(peerNonce, localFingerprint, options = {}) {
64
+ const seed = options.secretKey
65
+ ? Buffer.from(options.secretKey)
66
66
  : Buffer.from(ensureNodeSeed(), 'hex')
67
67
  const { publicKey, secretKey } = keyPairFromSeed(seed)
68
- const nodeHash = normalizeHex64(opts.nodeHash || pubKeyHash(publicKey))
68
+ const nodeHash = normalizeHex64(options.nodeHash || pubKeyHash(publicKey))
69
69
  if (nodeHash !== pubKeyHash(publicKey))
70
70
  throw new Error('p2p: auth nodeHash does not match secretKey')
71
71
  const message = buildAuthMessage(peerNonce, localFingerprint, nodeHash)
@@ -133,16 +133,16 @@ export function buildAdvertMessage(topic, ts, nodeHash) {
133
133
  * 构造带签名的 discovery advert。
134
134
  * @param {string} topic 广播主题
135
135
  * @param {number} [ts=Date.now()] 时间戳(毫秒)
136
- * @param {{ secretKey?: Uint8Array, nodeHash?: string, nodePubKey?: string } | null} [opts] 签名身份,省略则用本地节点
136
+ * @param {{ secretKey?: Uint8Array, nodeHash?: string, nodePubKey?: string } | null} [options] 签名身份,省略则用本地节点
137
137
  * @returns {Promise<{ nodeHash: string, nodePubKey: string, ts: number, sig: string }>} 签名 advert
138
138
  */
139
- export async function buildSignedAdvert(topic, ts = Date.now(), opts = null) {
140
- const seed = opts?.secretKey
141
- ? Buffer.from(opts.secretKey)
139
+ export async function buildSignedAdvert(topic, ts = Date.now(), options = null) {
140
+ const seed = options?.secretKey
141
+ ? Buffer.from(options.secretKey)
142
142
  : Buffer.from(ensureNodeSeed(), 'hex')
143
143
  const { publicKey, secretKey } = keyPairFromSeed(seed)
144
- const nodeHash = normalizeHex64(opts?.nodeHash || pubKeyHash(publicKey))
145
- const nodePubKey = normalizeHex64(opts?.nodePubKey || Buffer.from(publicKey).toString('hex'))
144
+ const nodeHash = normalizeHex64(options?.nodeHash || pubKeyHash(publicKey))
145
+ const nodePubKey = normalizeHex64(options?.nodePubKey || Buffer.from(publicKey).toString('hex'))
146
146
  if (pubKeyHash(Buffer.from(nodePubKey, 'hex')) !== nodeHash)
147
147
  throw new Error('p2p: advert nodePubKey does not match nodeHash')
148
148
  const message = buildAdvertMessage(topic, ts, nodeHash)
package/link/link.mjs CHANGED
@@ -73,27 +73,27 @@ function formatErrorReason(error) {
73
73
 
74
74
  /**
75
75
  * 建立 P2P link:WebRTC 双通道、握手、分帧收发与心跳。
76
- * @param {object} opts link 配置
77
- * @param {string | null} [opts.nodeHash] 期望的对端 nodeHash,省略则不校验
78
- * @param {boolean} opts.initiator 是否为连接发起方
79
- * @param {{ send: (message: unknown) => void | Promise<void>, onRemote: (handler: (message: unknown) => void) => (() => void) | void }} opts.signal 信令收发接口
80
- * @param {RTCConfiguration['iceServers']} [opts.iceServers] ICE 服务器列表
81
- * @param {number} [opts.heartbeatMs] 心跳间隔(毫秒)
82
- * @param {number} [opts.idleTimeoutMs] 无入站流量超时(毫秒)
83
- * @param {number} [opts.handshakeTimeoutMs] 握手超时(毫秒)
84
- * @param {{ RTCPeerConnection: typeof RTCPeerConnection } | null} [opts.rtc] RTC 构造器,省略则加载 polyfill
85
- * @param {{ nodeHash?: string, nodePubKey?: string, secretKey?: Uint8Array, nonce?: string } | null} [opts.localIdentity] 本地握手身份
86
- * @returns {Promise<{ ready: Promise<void>, get nodeHash(): string | null, send: (envelope: { scope: string, action: string, payload: unknown }) => Promise<boolean>, onEnvelope: (cb: (envelope: { scope: string, action: string, payload: unknown }, remoteNodeHash: string) => void) => () => void, onDown: (cb: (reason: string) => void) => () => void, close: (reason?: string) => Promise<void>, stats: () => object }>} link 句柄
76
+ * @param {object} options link 配置
77
+ * @param {string | null} [options.nodeHash] 期望的对端 nodeHash,省略则不校验
78
+ * @param {boolean} options.initiator 是否为连接发起方
79
+ * @param {{ send: (message: unknown) => void | Promise<void>, onRemote: (handler: (message: unknown) => void) => (() => void) | void }} options.signal 信令收发接口
80
+ * @param {RTCConfiguration['iceServers']} [options.iceServers] ICE 服务器列表
81
+ * @param {number} [options.heartbeatMs] 心跳间隔(毫秒)
82
+ * @param {number} [options.idleTimeoutMs] 无入站流量超时(毫秒)
83
+ * @param {number} [options.handshakeTimeoutMs] 握手超时(毫秒)
84
+ * @param {{ RTCPeerConnection: typeof RTCPeerConnection } | null} [options.rtc] RTC 构造器,省略则加载 polyfill
85
+ * @param {{ nodeHash?: string, nodePubKey?: string, secretKey?: Uint8Array, nonce?: string } | null} [options.localIdentity] 本地握手身份
86
+ * @returns {Promise<{ ready: Promise<void>, get nodeHash(): string | null, send: (envelope: { scope: string, action: string, payload: unknown }) => Promise<boolean>, onEnvelope: (callback: (envelope: { scope: string, action: string, payload: unknown }, remoteNodeHash: string) => void) => () => void, onDown: (callback: (reason: string) => void) => () => void, close: (reason?: string) => Promise<void>, stats: () => object }>} link 句柄
87
87
  */
88
- export async function createLink(opts) {
89
- const heartbeatMs = Number(opts.heartbeatMs) || ms('15s')
90
- const idleTimeoutMs = Number(opts.idleTimeoutMs) || ms('45s')
91
- const handshakeTimeoutMs = Number(opts.handshakeTimeoutMs) || ms('10s')
88
+ export async function createLink(options) {
89
+ const heartbeatMs = Number(options.heartbeatMs) || ms('15s')
90
+ const idleTimeoutMs = Number(options.idleTimeoutMs) || ms('45s')
91
+ const handshakeTimeoutMs = Number(options.handshakeTimeoutMs) || ms('10s')
92
92
  const channelOpenTimeoutMs = Math.max(handshakeTimeoutMs, ms('30s'))
93
93
  const trickleIceOff = getSignalingRuntimeConfig().trickleIceOff === true
94
- const rtc = opts.rtc ?? await loadNodeRtcPolyfill()
95
- const pc = new rtc.RTCPeerConnection(opts.iceServers?.length ? { iceServers: opts.iceServers } : undefined)
96
- const targetNodeHash = normalizeHex64(opts.nodeHash || '')
94
+ const rtc = options.rtc ?? await loadNodeRtcPolyfill()
95
+ const pc = new rtc.RTCPeerConnection(options.iceServers?.length ? { iceServers: options.iceServers } : undefined)
96
+ const targetNodeHash = normalizeHex64(options.nodeHash || '')
97
97
  const remoteSignalQueue = []
98
98
  const seenRemoteSignals = createLruMap(1024)
99
99
  let remoteDescriptionSet = false
@@ -142,7 +142,7 @@ export async function createLink(opts) {
142
142
  * @returns {Promise<void>}
143
143
  */
144
144
  async function sendSignal(message) {
145
- await Promise.resolve(opts.signal.send(message))
145
+ await Promise.resolve(options.signal.send(message))
146
146
  }
147
147
 
148
148
  /**
@@ -185,7 +185,7 @@ export async function createLink(opts) {
185
185
  const fingerprint = localFingerprint()
186
186
  if (!fingerprint) return
187
187
  authSent = true
188
- await sendRawControl(await buildAuth(remoteHello.nonce, fingerprint, opts.localIdentity ?? {}))
188
+ await sendRawControl(await buildAuth(remoteHello.nonce, fingerprint, options.localIdentity ?? {}))
189
189
  }
190
190
 
191
191
  /**
@@ -446,7 +446,7 @@ export async function createLink(opts) {
446
446
  if (!helloSent) {
447
447
  handshakeTimer = setTimeout(() => { void close('handshake-timeout') }, handshakeTimeoutMs)
448
448
  helloSent = true
449
- localHello = buildHello(opts.localIdentity ?? {})
449
+ localHello = buildHello(options.localIdentity ?? {})
450
450
  await sendRawControl(localHello)
451
451
  }
452
452
  await maybeSendAuth()
@@ -507,7 +507,7 @@ export async function createLink(opts) {
507
507
  emitListeners(downListeners, reason)
508
508
  }
509
509
 
510
- unlistenRemote = opts.signal.onRemote(message => {
510
+ unlistenRemote = options.signal.onRemote(message => {
511
511
  void handleRemoteSignal(message).catch(error => close(`signal-error:${formatErrorReason(error)}`))
512
512
  }) ?? null
513
513
 
@@ -535,7 +535,7 @@ export async function createLink(opts) {
535
535
  }
536
536
  }
537
537
 
538
- if (opts.initiator) {
538
+ if (options.initiator) {
539
539
  await attachChannel(pc.createDataChannel(CHANNEL_CONTROL))
540
540
  await attachChannel(pc.createDataChannel(CHANNEL_BULK))
541
541
  const offer = await pc.createOffer()
@@ -559,25 +559,25 @@ export async function createLink(opts) {
559
559
  * 本端是否为连接发起方。
560
560
  * @returns {boolean} 发起方标志
561
561
  */
562
- get initiator() { return !!opts.initiator },
562
+ get initiator() { return !!options.initiator },
563
563
  send,
564
564
  /**
565
565
  * 订阅业务 envelope 入站。
566
- * @param {(envelope: { scope: string, action: string, payload: unknown }, remoteNodeHash: string) => void} cb 回调
566
+ * @param {(envelope: { scope: string, action: string, payload: unknown }, remoteNodeHash: string) => void} callback 回调
567
567
  * @returns {() => void} 取消订阅函数
568
568
  */
569
- onEnvelope(cb) {
570
- envelopeListeners.add(cb)
571
- return () => envelopeListeners.delete(cb)
569
+ onEnvelope(callback) {
570
+ envelopeListeners.add(callback)
571
+ return () => envelopeListeners.delete(callback)
572
572
  },
573
573
  /**
574
574
  * 订阅 link 断开事件。
575
- * @param {(reason: string) => void} cb 回调
575
+ * @param {(reason: string) => void} callback 回调
576
576
  * @returns {() => void} 取消订阅函数
577
577
  */
578
- onDown(cb) {
579
- downListeners.add(cb)
580
- return () => downListeners.delete(cb)
578
+ onDown(callback) {
579
+ downListeners.add(callback)
580
+ return () => downListeners.delete(callback)
581
581
  },
582
582
  close,
583
583
  /**
@@ -597,7 +597,7 @@ export async function createLink(opts) {
597
597
  ready,
598
598
  nodeHash: remoteNodeHash,
599
599
  targetNodeHash: targetNodeHash || null,
600
- initiator: !!opts.initiator,
600
+ initiator: !!options.initiator,
601
601
  connectionState: pc.connectionState,
602
602
  iceConnectionState: pc.iceConnectionState,
603
603
  lastInboundAt,
@@ -59,26 +59,26 @@ async function resolveRouting(username) {
59
59
 
60
60
  /**
61
61
  * @param {string} username 副本用户名(trust graph 投递上下文)
62
- * @param {object} opts 投递选项
62
+ * @param {object} options 投递选项
63
63
  * @returns {Promise<{ stored: boolean, delivered: boolean, relayed: number }>} 存转结果
64
64
  */
65
- export async function deliverOrStoreMailboxPut(username, opts) {
65
+ export async function deliverOrStoreMailboxPut(username, options) {
66
66
  const routing = await resolveRouting(username)
67
- const toPubKeyHash = normalizeHex64(opts.toPubKeyHash)
67
+ const toPubKeyHash = normalizeHex64(options.toPubKeyHash)
68
68
  if (!toPubKeyHash) return { stored: false, delivered: false, relayed: 0 }
69
- const hop = normalizeMailboxHop(opts.hop)
69
+ const hop = normalizeMailboxHop(options.hop)
70
70
  if (hop >= routing.maxHop) return { stored: false, delivered: false, relayed: 0 }
71
71
  const tier = mailboxTierFromHop(hop)
72
72
  const nodeHash = getNodeHash()
73
73
  const record = {
74
- ...opts.record,
74
+ ...options.record,
75
75
  toPubKeyHash,
76
76
  hop,
77
77
  tier,
78
- fromNodeHash: opts.record?.fromNodeHash || nodeHash,
78
+ fromNodeHash: options.record?.fromNodeHash || nodeHash,
79
79
  }
80
80
  const stored = await storeMailboxRecord(record)
81
- const toNodeHash = opts.toNodeHash?.trim().toLowerCase()
81
+ const toNodeHash = options.toNodeHash?.trim().toLowerCase()
82
82
  const delivered = toNodeHash && isMailboxRecordWithinSizeLimit(record)
83
83
  ? await requireTrustGraphProvider(DEFAULT_TRUST_GRAPH_OWNER).sendToNode(username, toNodeHash, 'mailbox_put', { nodeHash, record })
84
84
  : false
@@ -108,17 +108,17 @@ export async function publishMailboxRecord(username, toPubKeyHash, record, toNod
108
108
  }
109
109
 
110
110
  /**
111
- * @param {{ replicaUsername?: string }} ctx 入站上下文
111
+ * @param {{ replicaUsername?: string }} wireContext 入站上下文
112
112
  * @param {object} put 入站 mailbox_put
113
113
  * @param {string} [peerId] Trystero 对端 id(有则校验 nodeHash 绑定)
114
114
  * @returns {Promise<void>}
115
115
  */
116
- export async function ingestMailboxPut(ctx, put, peerId = '') {
116
+ export async function ingestMailboxPut(wireContext, put, peerId = '') {
117
117
  const { record } = put
118
118
  if (!record?.envelope || !record?.toPubKeyHash) return
119
119
  const fromNode = normalizeHex64(put.nodeHash)
120
120
  if (!fromNode || !takeIncomingMailboxPutSlot(fromNode)) return
121
- const username = String(ctx?.replicaUsername || '').trim()
121
+ const username = String(wireContext.replicaUsername || '').trim()
122
122
  if (!username) return
123
123
  if (peerId) {
124
124
  const remote = await resolveRemoteNodeHashForPeer(username, peerId)
@@ -157,14 +157,14 @@ export async function respondMailboxWant(want, sendGive, peerId) {
157
157
  }
158
158
 
159
159
  /**
160
- * @param {{ replicaUsername?: string }} ctx 入站上下文
160
+ * @param {{ replicaUsername?: string }} wireContext 入站上下文
161
161
  * @param {object} give mailbox_give 载荷
162
162
  * @returns {Promise<number>} 投递给消费者的记录数
163
163
  */
164
- export async function ingestMailboxGive(ctx, give) {
164
+ export async function ingestMailboxGive(wireContext, give) {
165
165
  const records = (give.records || []).filter(isDeliverableMailboxRecord)
166
166
  if (!records.length) return 0
167
- const username = String(ctx?.replicaUsername || '').trim()
167
+ const username = String(wireContext.replicaUsername || '').trim()
168
168
  if (!username) return 0
169
169
  const { dispatchMailboxRecordsToConsumers } = await import('./consumer_registry.mjs')
170
170
  const { deleteMailboxRecords } = await import('./store.mjs')
@@ -6,39 +6,6 @@
6
6
 
7
7
  const TIER_ORDER = { quarantine: 0, normal: 1, trusted: 2 }
8
8
 
9
- /**
10
- * @param {number} score 信誉分
11
- * @returns {MailboxTier} 分层
12
- */
13
- export function mailboxTierFromScore(score) {
14
- const numericScore = Number(score)
15
- if (!Number.isFinite(numericScore)) return 'quarantine'
16
- if (numericScore >= 0.45) return 'trusted'
17
- if (numericScore >= 0.12) return 'normal'
18
- return 'quarantine'
19
- }
20
-
21
- /**
22
- * @param {object} opts 参数
23
- * @param {number} [opts.senderScore] 发件方信誉
24
- * @param {number} [opts.recipientScore] 收件方关系信誉(可选)
25
- * @param {boolean} [opts.knownMember] 是否本地已知成员/节点
26
- * @param {number} [opts.hop] 转发跳数
27
- * @returns {{ tier: MailboxTier, score: number }} 分层与分数
28
- */
29
- export function scoreMailboxImportance(opts = {}) {
30
- const sender = Number(opts.senderScore ?? 0)
31
- const recipient = Number(opts.recipientScore ?? sender)
32
- const known = !!opts.knownMember
33
- const hop = Math.max(0, Number(opts.hop) || 0)
34
- let score = sender * 0.65 + recipient * 0.35
35
- if (known) score += 0.15
36
- score -= hop * 0.08
37
- if (score < 0) score = 0
38
- if (score > 1) score = 1
39
- return { tier: mailboxTierFromScore(score), score }
40
- }
41
-
42
9
  /**
43
10
  * 按 tier 与 storedAt 排序(低 tier 先淘汰)。
44
11
  * @param {object[]} rows 记录
@@ -1,4 +1,3 @@
1
- import { getNodeTransportSettings } from '../node/identity.mjs'
2
1
  import {
3
2
  resolveMailboxRelayFanout,
4
3
  resolveMailboxWantFanout,
@@ -43,11 +42,3 @@ export function resolveMailboxRoutingForPeerCount(peerCount, raw = {}, batterySa
43
42
  batterySaver: true,
44
43
  }
45
44
  }
46
-
47
- /**
48
- * @returns {{ maxHop: number, relayFanoutTrusted: number, relayFanoutNormal: number, wantFanout: number, batterySaver: boolean }} mailbox 路由配置
49
- */
50
- export function getMailboxRoutingSettings() {
51
- const { batterySaver, mailbox } = getNodeTransportSettings()
52
- return resolveMailboxRoutingForPeerCount(0, mailbox, batterySaver)
53
- }
package/mailbox/wire.mjs CHANGED
@@ -10,15 +10,15 @@ import { parseMailboxGive, parseMailboxPut, parseMailboxWant } from './parse.mjs
10
10
  */
11
11
 
12
12
  /**
13
- * @param {MailboxWireContext} ctx 入站上下文
13
+ * @param {MailboxWireContext} wireContext 入站上下文
14
14
  * @param {{ on: (name: string, handler: (payload: unknown, peerId: string) => void) => void, send: (name: string, payload: unknown, peerId: string | null) => void }} wire Trystero 适配器
15
15
  * @returns {void}
16
16
  */
17
- export function attachMailboxWire(ctx, wire) {
17
+ export function attachMailboxWire(wireContext, wire) {
18
18
  wire.on('mailbox_put', (payload, peerId) => {
19
19
  const put = parseMailboxPut(payload)
20
20
  if (!put.ok) return
21
- void ingestMailboxPut(ctx, put.value, peerId).catch(err => console.error('mailbox: put ingest failed', err))
21
+ void ingestMailboxPut(wireContext, put.value, peerId).catch(err => console.error('mailbox: put ingest failed', err))
22
22
  })
23
23
 
24
24
  wire.on('mailbox_want', (payload, peerId) => {
@@ -35,6 +35,6 @@ export function attachMailboxWire(ctx, wire) {
35
35
  wire.on('mailbox_give', payload => {
36
36
  const give = parseMailboxGive(payload)
37
37
  if (!give.ok) return
38
- void ingestMailboxGive(ctx, give.value).catch(err => console.error('mailbox: give ingest failed', err))
38
+ void ingestMailboxGive(wireContext, give.value).catch(err => console.error('mailbox: give ingest failed', err))
39
39
  })
40
40
  }
package/node/denylist.mjs CHANGED
@@ -56,13 +56,6 @@ function buildDenylistIndex(blocked) {
56
56
  return index
57
57
  }
58
58
 
59
- /**
60
- * @returns {void}
61
- */
62
- export function invalidateDenylistIndex() {
63
- cachedIndex = null
64
- }
65
-
66
59
  /**
67
60
  * @returns {DenylistIndex} 缓存索引
68
61
  */
@@ -244,23 +237,6 @@ export async function addDenylistFromBanContent(banContent, groupId) {
244
237
  await addDenylistEntry({ scope: 'subject', value: pk, ...sourceGroupId ? { groupId: sourceGroupId } : {} })
245
238
  }
246
239
 
247
- /**
248
- * @param {string} entityHash 128 位十六进制
249
- * @param {boolean} block true=拉黑
250
- * @returns {Promise<boolean>} 当前是否拉黑
251
- */
252
- export async function setEntityBlocked(entityHash, block) {
253
- const id = String(entityHash || '').trim().toLowerCase()
254
- if (!isEntityHash128(id)) throw new Error('invalid entityHash')
255
- await mutateDenylist(() => {
256
- const list = loadDenylist()
257
- const without = list.blocked.filter(e => !(e.scope === 'entity' && e.value === id))
258
- if (block) without.push({ scope: 'entity', value: id })
259
- saveDenylist({ blocked: without })
260
- })
261
- return block
262
- }
263
-
264
240
  /**
265
241
  * 追加群 scope 拉黑项。
266
242
  * @param {string} groupId 群 ID
package/node/network.mjs CHANGED
@@ -121,20 +121,6 @@ export function saveNetwork(data) {
121
121
  invalidateTrustGraphCache()
122
122
  }
123
123
 
124
- /**
125
- * @param {string} nodeHash 64 位十六进制
126
- * @param {'trusted' | 'explore'} tier 池档位
127
- * @returns {void}
128
- */
129
- export function addNetworkPeer(nodeHash, tier = 'explore') {
130
- const id = normalizeHex64(nodeHash)
131
- if (!isHex64(id)) return
132
- const net = loadNetwork()
133
- const list = tier === 'trusted' ? net.trustedPeers : net.explorePeers
134
- if (!list.includes(id)) list.push(id)
135
- saveNetwork(net)
136
- }
137
-
138
124
  /**
139
125
  * @param {{ nodeHash: string, source: string, kind: string, weight?: number, expiresAt?: number, ttlMs?: number, groupId?: string }} hint 扩边 hint
140
126
  * @returns {void}
@@ -165,35 +151,6 @@ export function applyNetworkHint(hint) {
165
151
  saveNetwork(net)
166
152
  }
167
153
 
168
- /**
169
- * @param {{ remoteNodeHash?: string }[]} roster Trystero roster
170
- * @param {string} [groupId] 来源群
171
- * @param {string} [source='roster'] hint 来源标签
172
- * @returns {void}
173
- */
174
- export function recordExplorePeersFromRoster(roster, groupId = '', source = 'roster') {
175
- if (!roster?.length) return
176
- const net = loadNetwork()
177
- const now = Date.now()
178
- for (const peer of roster) {
179
- const nodeHash = normalizeHex64(peer?.remoteNodeHash)
180
- if (!isHex64(nodeHash)) continue
181
- if (!net.explorePeers.includes(nodeHash))
182
- net.explorePeers.push(nodeHash)
183
- net.hints.push({
184
- nodeHash,
185
- source: String(source || 'roster'),
186
- kind: 'roster',
187
- weight: 0.1,
188
- expiresAt: now + 24 * 60 * 60 * 1000,
189
- ...groupId ? { groupId: String(groupId).trim() } : {},
190
- })
191
- }
192
- net.hints = capHintsBySource(net.hints).slice(-MAX_HINTS)
193
- net.lastRosterAt = now
194
- saveNetwork(net)
195
- }
196
-
197
154
  /**
198
155
  * 疑似分区/eclipse 后:用 trusted 锚点加宽 explore,便于恢复联邦可达。
199
156
  * @returns {void}
@@ -239,14 +196,6 @@ export function mergeNetworkPeerPools(patch = {}) {
239
196
  saveNetwork(net)
240
197
  }
241
198
 
242
- /**
243
- * @param {string[]} nodeHashes trusted 候选
244
- * @returns {void}
245
- */
246
- export function mergeTrustedPeers(nodeHashes) {
247
- mergeNetworkPeerPools({ trustedPeers: nodeHashes })
248
- }
249
-
250
199
  /**
251
200
  * @param {string} groupId 群 scope
252
201
  * @param {'node' | 'subject' | 'entity'} scope denylist 作用域
@@ -104,41 +104,6 @@ export async function loadPersonalBlockEntries(viewerEntityHash) {
104
104
  return normalizePersonalListEntries(data?.blocked || [])
105
105
  }
106
106
 
107
- /**
108
- * @param {string} viewerEntityHash 观看者实体
109
- * @param {object} subject 待检主体
110
- * @returns {Promise<boolean>} 是否被隐藏
111
- */
112
- export async function isHiddenBy(viewerEntityHash, subject) {
113
- return matchesPersonalListEntries(await loadPersonalHideEntries(viewerEntityHash), subject)
114
- }
115
-
116
- /**
117
- * @param {string} viewerEntityHash 观看者实体
118
- * @param {object} subject 待检主体
119
- * @returns {Promise<boolean>} 是否被拉黑
120
- */
121
- export async function isBlockedBy(viewerEntityHash, subject) {
122
- return matchesPersonalListEntries(await loadPersonalBlockEntries(viewerEntityHash), subject)
123
- }
124
-
125
- /**
126
- * @param {string} viewerEntityHash 观看者实体
127
- * @param {object} subject 待检主体
128
- * @returns {Promise<boolean>} 是否应过滤
129
- */
130
- export async function isFilteredByPersonalLists(viewerEntityHash, subject) {
131
- if (!viewerEntityHash) return false
132
- const [blocked, hidden, muted] = await Promise.all([
133
- loadPersonalBlockEntries(viewerEntityHash),
134
- loadPersonalHideEntries(viewerEntityHash),
135
- loadPersonalMuteEntries(viewerEntityHash),
136
- ])
137
- return matchesPersonalListEntries(blocked, subject)
138
- || matchesPersonalListEntries(hidden, subject)
139
- || matchesPersonalListEntries(muted, subject)
140
- }
141
-
142
107
  /**
143
108
  * @param {string} viewerEntityHash 本地可写实体
144
109
  * @param {string} targetEntityHash 目标