@steve02081504/fount-p2p 0.0.5 → 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.
@@ -47,19 +47,19 @@ export function canonicalizeRowContent(content, hexKeys, entityHashKeys = new Se
47
47
  * prepare?: (event: object) => object,
48
48
  * contentHexKeys?: ReadonlySet<string>,
49
49
  * entityHashKeys?: ReadonlySet<string>,
50
- * }} [opts] 各域字段集
50
+ * }} [options] 各域字段集
51
51
  * @returns {object} canonical 行
52
52
  */
53
- export function canonicalizeSignedRow(event, opts = {}) {
54
- const out = opts.prepare ? opts.prepare({ ...event }) : { ...event }
53
+ export function canonicalizeSignedRow(event, options = {}) {
54
+ const out = options.prepare ? options.prepare({ ...event }) : { ...event }
55
55
  out.id = assertHex64(out.id, 'id')
56
56
  out.sender = assertHex64(out.sender, 'sender')
57
57
  if (Array.isArray(out.prev_event_ids))
58
58
  out.prev_event_ids = out.prev_event_ids.map((id, index) =>
59
59
  assertHex64(id, `prev_event_ids[${index}]`),
60
60
  )
61
- const hexKeys = opts.contentHexKeys
61
+ const hexKeys = options.contentHexKeys
62
62
  if (out.content && hexKeys?.size)
63
- out.content = canonicalizeRowContent(out.content, hexKeys, opts.entityHashKeys)
63
+ out.content = canonicalizeRowContent(out.content, hexKeys, options.entityHashKeys)
64
64
  return out
65
65
  }
@@ -7,12 +7,12 @@ const DEFAULT_GROUP = '239.255.42.99'
7
7
  /**
8
8
  * 轻量 multicast 发现插件:不做完整 DNS-SD,只复用 mDNS 的 LAN multicast 发现思路。
9
9
  *
10
- * @param {{ port?: number, group?: string }} [opts] 组播端口与组地址
10
+ * @param {{ port?: number, group?: string }} [options] 组播端口与组地址
11
11
  * @returns {import('./index.mjs').DiscoveryProvider} mDNS 发现提供者
12
12
  */
13
- export function createMdnsDiscoveryProvider(opts = {}) {
14
- const port = Number(opts.port) || DEFAULT_PORT
15
- const group = String(opts.group || DEFAULT_GROUP)
13
+ export function createMdnsDiscoveryProvider(options = {}) {
14
+ const port = Number(options.port) || DEFAULT_PORT
15
+ const group = String(options.group || DEFAULT_GROUP)
16
16
  const socket = dgram.createSocket({ type: 'udp4', reuseAddr: true })
17
17
  let bound = false
18
18
  let bindPromise = null
@@ -142,11 +142,11 @@ async function publishEvent(relayUrls, event) {
142
142
 
143
143
  /**
144
144
  * 创建 Nostr discovery provider。
145
- * @param {{ relayUrls?: string[] }} [opts] 可选中继 URL 覆盖
145
+ * @param {{ relayUrls?: string[] }} [options] 可选中继 URL 覆盖
146
146
  * @returns {import('./index.mjs').DiscoveryProvider} Nostr 发现提供者
147
147
  */
148
- export function createNostrDiscoveryProvider(opts = {}) {
149
- const relayUrls = mergeSignalingRelayUrls(opts.relayUrls)
148
+ export function createNostrDiscoveryProvider(options = {}) {
149
+ const relayUrls = mergeSignalingRelayUrls(options.relayUrls)
150
150
  const secretKey = randomBytes(32)
151
151
  return {
152
152
  id: 'nostr',
@@ -14,10 +14,10 @@ export const MAX_PENDING_CHUNK_FETCHES = 2048
14
14
  * @param {string} key 唯一等待键
15
15
  * @param {string} expectedHash 期望 64 hex 密文哈希
16
16
  * @param {number} timeoutMs 超时毫秒
17
- * @param {{ rejectOnTimeout?: boolean }} [opts] rejectOnTimeout 时 Promise 以 Error 拒绝
17
+ * @param {{ rejectOnTimeout?: boolean }} [options] rejectOnTimeout 时 Promise 以 Error 拒绝
18
18
  * @returns {{ done: Promise<Uint8Array | null>, cancel: () => void }} 等待 Promise 与取消函数
19
19
  */
20
- export function registerChunkFetchWait(key, expectedHash, timeoutMs, opts = {}) {
20
+ export function registerChunkFetchWait(key, expectedHash, timeoutMs, options = {}) {
21
21
  if (!key || pendingChunkFetches.size >= MAX_PENDING_CHUNK_FETCHES)
22
22
  return {
23
23
  done: Promise.resolve(null), /**
@@ -41,7 +41,7 @@ export function registerChunkFetchWait(key, expectedHash, timeoutMs, opts = {})
41
41
  })
42
42
  const timer = setTimeout(() => {
43
43
  pendingChunkFetches.delete(key)
44
- if (opts.rejectOnTimeout)
44
+ if (options.rejectOnTimeout)
45
45
  settle(new Error('chunk fetch timeout'))
46
46
  else
47
47
  settle(null)
@@ -23,11 +23,11 @@ export function assignChunksToPeers(chunkHashes, peerIds) {
23
23
  * @param {Map<string, { state: ChunkFetchState, peerId?: string, attempts: number }>} table 状态表
24
24
  * @param {string[]} chunkHashes 待拉取块
25
25
  * @param {string[]} peerIds 可用 peer
26
- * @param {{ maxAttempts?: number }} [opts] 选项
26
+ * @param {{ maxAttempts?: number }} [options] 选项
27
27
  * @returns {{ assignments: Map<string, string>, broadcast: string[] }} 分配与需广播块
28
28
  */
29
- export function planChunkFetches(table, chunkHashes, peerIds, opts = {}) {
30
- const maxAttempts = Math.max(1, Number(opts.maxAttempts) || 3)
29
+ export function planChunkFetches(table, chunkHashes, peerIds, options = {}) {
30
+ const maxAttempts = Math.max(1, Number(options.maxAttempts) || 3)
31
31
  const assignments = assignChunksToPeers(chunkHashes, peerIds)
32
32
  /** @type {string[]} */
33
33
  const broadcast = []
@@ -43,12 +43,12 @@ export function mergeTopologicalOrder(cachedOrder, events) {
43
43
  /**
44
44
  * @param {object[]} events 全量事件
45
45
  * @param {object | null} cache 磁盘缓存
46
- * @param {{ forceFull?: boolean }} [opts] 选项
46
+ * @param {{ forceFull?: boolean }} [options] 选项
47
47
  * @returns {string[]} 拓扑序 id 列表
48
48
  */
49
- export function resolveEventTopologicalOrder(events, cache, opts = {}) {
49
+ export function resolveEventTopologicalOrder(events, cache, options = {}) {
50
50
  if (!events.length) return []
51
- if (opts.forceFull)
51
+ if (options.forceFull)
52
52
  return topologicalCanonicalOrder(eventsToMetas(events))
53
53
 
54
54
  const byId = new Map(events.map(event => [event.id, event]))
@@ -6,11 +6,11 @@ const memoByKey = new Map()
6
6
  * @param {string} memoKey 缓存键
7
7
  * @param {string} fingerprint 文件 stat + 事件数指纹
8
8
  * @param {() => string[]} resolveOrder 实际求序(含磁盘层)
9
- * @param {{ force?: boolean }} [opts] 强制重算
9
+ * @param {{ force?: boolean }} [options] 强制重算
10
10
  * @returns {string[]} 拓扑序 event id
11
11
  */
12
- export function resolveTopologicalOrderMemoCached(memoKey, fingerprint, resolveOrder, opts = {}) {
13
- if (!opts.force) {
12
+ export function resolveTopologicalOrderMemoCached(memoKey, fingerprint, resolveOrder, options = {}) {
13
+ if (!options.force) {
14
14
  const cached = memoByKey.get(memoKey)
15
15
  if (cached?.fp === fingerprint && cached.order.length)
16
16
  return cached.order
@@ -71,7 +71,7 @@ export function attachNodeScopeFedChunkResponder(username, wire) {
71
71
  * Trystero room:注册带 requestId 的 fed_chunk_* + fed_manifest_*(TrustGraph 全局 miss)。
72
72
  * @param {string} username 用户
73
73
  * @param {object} room Trystero room
74
- * @param {{ enqueue: (prio: number, fn: () => void) => void }} [fedOut] 出站队列
74
+ * @param {{ enqueue: (prio: number, cleanup: () => void) => void }} [fedOut] 出站队列
75
75
  * @param {(roomKey: string, action: string, rtcLimits: object) => boolean} [guardGet] RTC 负载守卫
76
76
  * @param {object} [rtcLimits] RTC 限额
77
77
  * @param {string} [roomKey] 房间键
package/files/evfs.mjs CHANGED
@@ -10,21 +10,21 @@ import { fetchChunk } from './chunk_fetch.mjs'
10
10
  import { createChunkReadStream, getChunk, hasChunk, putChunk } from './chunk_store.mjs'
11
11
  import { normalizeFileManifest, publicTransferKeyDescriptor } from './manifest.mjs'
12
12
  import { assembleManifestPlaintext, resolveContentKey } from './transfer_key.mjs'
13
- import { readDagManifestPlaintext, resolveTransferKeyDeps } from './transfer_key_registry.mjs'
13
+ import { readDagManifestPlaintext, resolveTransferKeyDependencies } from './transfer_key_registry.mjs'
14
14
 
15
15
  /**
16
16
  * @param {string} replicaUsername 副本用户名
17
17
  * @param {import('./manifest.mjs').FileManifest} manifest 清单
18
18
  * @returns {{ getGroupFileMasterKey?: Function, getVaultMasterKey?: Function }} 密钥依赖
19
19
  */
20
- function transferKeyDepsForReplica(replicaUsername, manifest) {
21
- const rawDeps = resolveTransferKeyDeps(undefined, manifest)
20
+ function transferKeyDependenciesForReplica(replicaUsername, manifest) {
21
+ const rawDependencies = resolveTransferKeyDependencies(undefined, manifest)
22
22
  return {
23
- getGroupFileMasterKey: rawDeps.getGroupFileMasterKey
24
- ? (groupId, keyGeneration) => rawDeps.getGroupFileMasterKey(replicaUsername, groupId, keyGeneration)
23
+ getGroupFileMasterKey: rawDependencies.getGroupFileMasterKey
24
+ ? (groupId, keyGeneration) => rawDependencies.getGroupFileMasterKey(replicaUsername, groupId, keyGeneration)
25
25
  : undefined,
26
- getVaultMasterKey: rawDeps.getVaultMasterKey
27
- ? entityHash => rawDeps.getVaultMasterKey(replicaUsername, entityHash)
26
+ getVaultMasterKey: rawDependencies.getVaultMasterKey
27
+ ? entityHash => rawDependencies.getVaultMasterKey(replicaUsername, entityHash)
28
28
  : undefined,
29
29
  }
30
30
  }
@@ -32,13 +32,13 @@ function transferKeyDepsForReplica(replicaUsername, manifest) {
32
32
  /**
33
33
  * @param {string} username 拉取身份
34
34
  * @param {import('./manifest.mjs').FileManifest} manifest 清单
35
- * @param {{ fetchChunk?: Function }} [opts] miss 拉取
35
+ * @param {{ fetchChunk?: Function }} [options] miss 拉取
36
36
  * @returns {Promise<boolean>} 全部 part 是否已就位
37
37
  */
38
- async function ensureManifestPartsLocal(username, manifest, opts = {}) {
38
+ async function ensureManifestPartsLocal(username, manifest, options = {}) {
39
39
  for (const part of manifest.parts) {
40
40
  if (await hasChunk(part.hash)) continue
41
- const fetchedChunk = await (opts.fetchChunk || fetchChunk)({
41
+ const fetchedChunk = await (options.fetchChunk || fetchChunk)({
42
42
  username,
43
43
  ciphertextHash: part.hash,
44
44
  ownerEntityHash: manifest.ownerEntityHash,
@@ -81,46 +81,46 @@ export async function storeManifestParts(manifest, partBytes) {
81
81
  /**
82
82
  * @param {string} replicaUsername 副本用户名
83
83
  * @param {import('./manifest.mjs').FileManifest} manifest 清单
84
- * @param {{ username?: string, fetchChunk?: Function }} [opts] miss 拉取
84
+ * @param {{ username?: string, fetchChunk?: Function }} [options] miss 拉取
85
85
  * @returns {Promise<Buffer | null>} 明文内容
86
86
  */
87
- export async function readManifestPlaintext(replicaUsername, manifest, opts = {}) {
87
+ export async function readManifestPlaintext(replicaUsername, manifest, options = {}) {
88
88
  const dagGroupId = manifest.meta?.groupId
89
89
  if (Array.isArray(manifest.meta?.dagParts) && dagGroupId) {
90
90
  const dagPlain = await readDagManifestPlaintext(replicaUsername, manifest)
91
91
  if (dagPlain) return dagPlain
92
92
  }
93
93
 
94
- const username = opts.username || replicaUsername
95
- if (!await ensureManifestPartsLocal(username, manifest, opts)) return null
94
+ const username = options.username || replicaUsername
95
+ if (!await ensureManifestPartsLocal(username, manifest, options)) return null
96
96
 
97
97
  /** @type {Buffer[]} */
98
98
  const partBytes = []
99
99
  for (const part of manifest.parts)
100
100
  partBytes.push(await getChunk(part.hash))
101
101
 
102
- return assembleManifestPlaintext(manifest, partBytes, transferKeyDepsForReplica(replicaUsername, manifest))
102
+ return assembleManifestPlaintext(manifest, partBytes, transferKeyDependenciesForReplica(replicaUsername, manifest))
103
103
  }
104
104
 
105
105
  /**
106
106
  * @param {string} replicaUsername 副本用户名
107
107
  * @param {import('./manifest.mjs').FileManifest} manifest 清单
108
- * @param {{ username?: string, fetchChunk?: Function }} [opts] miss 拉取
108
+ * @param {{ username?: string, fetchChunk?: Function }} [options] miss 拉取
109
109
  * @returns {Promise<import('node:stream').Readable | null>} 明文流
110
110
  */
111
- export async function readManifestPlaintextStream(replicaUsername, manifest, opts = {}) {
111
+ export async function readManifestPlaintextStream(replicaUsername, manifest, options = {}) {
112
112
  const dagGroupId = manifest.meta?.groupId
113
113
  if (Array.isArray(manifest.meta?.dagParts) && dagGroupId) {
114
- const plain = await readManifestPlaintext(replicaUsername, manifest, opts)
114
+ const plain = await readManifestPlaintext(replicaUsername, manifest, options)
115
115
  if (!plain) return null
116
116
  return Readable.from([plain])
117
117
  }
118
118
 
119
- const username = opts.username || replicaUsername
120
- if (!await ensureManifestPartsLocal(username, manifest, opts)) return null
119
+ const username = options.username || replicaUsername
120
+ if (!await ensureManifestPartsLocal(username, manifest, options)) return null
121
121
 
122
- const deps = transferKeyDepsForReplica(replicaUsername, manifest)
123
- const contentKey = await resolveContentKey(manifest.transferKeyDescriptor, manifest, deps)
122
+ const dependencies = transferKeyDependenciesForReplica(replicaUsername, manifest)
123
+ const contentKey = await resolveContentKey(manifest.transferKeyDescriptor, manifest, dependencies)
124
124
  if (manifest.ceMode === 'random' && !contentKey) return null
125
125
 
126
126
  const partStreams = manifest.parts.map(part => createChunkReadStream(part.hash))
@@ -219,16 +219,16 @@ export async function putFileManifestFromStream(params) {
219
219
  * @param {string} replicaUsername 副本用户名
220
220
  * @param {string} entityHash owner entityHash
221
221
  * @param {string} logicalPath EVFS 逻辑路径
222
- * @param {{ username?: string, fetchChunk?: Function }} [opts] miss 拉取
222
+ * @param {{ username?: string, fetchChunk?: Function }} [options] miss 拉取
223
223
  * @returns {Promise<Buffer | null>} 明文或 null
224
224
  */
225
- export async function readPublicFile(replicaUsername, entityHash, logicalPath, opts = {}) {
225
+ export async function readPublicFile(replicaUsername, entityHash, logicalPath, options = {}) {
226
226
  const { fetchPublicManifest } = await import('./manifest_fetch.mjs')
227
227
  const manifest = await fetchPublicManifest({
228
- username: opts.username || replicaUsername,
228
+ username: options.username || replicaUsername,
229
229
  ownerEntityHash: entityHash,
230
230
  logicalPath,
231
231
  })
232
232
  if (!manifest) return null
233
- return readManifestPlaintext(replicaUsername, manifest, opts)
233
+ return readManifestPlaintext(replicaUsername, manifest, options)
234
234
  }
@@ -15,26 +15,26 @@ import {
15
15
  /**
16
16
  * @param {TransferKeyDescriptor} descriptor 传递密钥描述符
17
17
  * @param {FileManifest} manifest manifest
18
- * @param {{ getGroupFileMasterKey?: (groupId: string, keyGeneration?: number) => Promise<Buffer | string | null>, getVaultMasterKey?: (entityHash: string) => Promise<Buffer | string | null> }} deps 密钥源
18
+ * @param {{ getGroupFileMasterKey?: (groupId: string, keyGeneration?: number) => Promise<Buffer | string | null>, getVaultMasterKey?: (entityHash: string) => Promise<Buffer | string | null> }} dependencies 密钥源
19
19
  * @returns {Promise<Buffer | null>} contentKey;plain/convergent 返回 null(按 contentHash 派生)
20
20
  */
21
- export async function resolveContentKey(descriptor, manifest, deps = {}) {
21
+ export async function resolveContentKey(descriptor, manifest, dependencies = {}) {
22
22
  const type = descriptor?.type || 'public'
23
23
  if (type === 'public' || manifest.ceMode === 'plain' || manifest.ceMode === 'convergent')
24
24
  return null
25
25
 
26
26
  if (type === 'file-master-key-wrap') {
27
27
  const { groupId, fileId } = descriptor
28
- if (!groupId || !fileId || !descriptor.wrappedKey || !deps.getGroupFileMasterKey) return null
29
- const groupKey = await deps.getGroupFileMasterKey(String(groupId), descriptor.keyGeneration)
28
+ if (!groupId || !fileId || !descriptor.wrappedKey || !dependencies.getGroupFileMasterKey) return null
29
+ const groupKey = await dependencies.getGroupFileMasterKey(String(groupId), descriptor.keyGeneration)
30
30
  if (!groupKey) return null
31
31
  return unwrapContentKey(descriptor.wrappedKey, groupKey, fileId)
32
32
  }
33
33
 
34
34
  if (type === 'vault-wrap') {
35
35
  const { entityHash, fileId } = descriptor
36
- if (!entityHash || !fileId || !descriptor.wrappedKey || !deps.getVaultMasterKey) return null
37
- const vaultKey = await deps.getVaultMasterKey(String(entityHash))
36
+ if (!entityHash || !fileId || !descriptor.wrappedKey || !dependencies.getVaultMasterKey) return null
37
+ const vaultKey = await dependencies.getVaultMasterKey(String(entityHash))
38
38
  if (!vaultKey) return null
39
39
  return unwrapContentKey(descriptor.wrappedKey, vaultKey, fileId)
40
40
  }
@@ -70,12 +70,12 @@ export function decryptPart(encryptedPartBytes, manifest, contentKey, partIndex
70
70
  /**
71
71
  * @param {FileManifest} manifest manifest
72
72
  * @param {Array<Buffer | Uint8Array>} partBytes 按序密文块
73
- * @param {{ getGroupFileMasterKey?: Function, getVaultMasterKey?: Function }} deps 密钥源
73
+ * @param {{ getGroupFileMasterKey?: Function, getVaultMasterKey?: Function }} dependencies 密钥源
74
74
  * @returns {Promise<Buffer | null>} 完整明文
75
75
  */
76
- export async function assembleManifestPlaintext(manifest, partBytes, deps = {}) {
76
+ export async function assembleManifestPlaintext(manifest, partBytes, dependencies = {}) {
77
77
  if (partBytes.length !== manifest.parts.length) return null
78
- const contentKey = await resolveContentKey(manifest.transferKeyDescriptor, manifest, deps)
78
+ const contentKey = await resolveContentKey(manifest.transferKeyDescriptor, manifest, dependencies)
79
79
  /** @type {Buffer[]} */
80
80
  const plains = []
81
81
  for (let index = 0; index < manifest.parts.length; index++) {
@@ -1,18 +1,18 @@
1
1
  /** @type {Map<string, { getGroupFileMasterKey?: (replicaUsername: string, groupId: string, keyGeneration?: number) => Promise<Buffer | string | null>, getVaultMasterKey?: (replicaUsername: string, entityHash: string) => Promise<Buffer | string | null> }>} */
2
- const transferDepsByOwner = new Map()
2
+ const transferDependenciesByOwner = new Map()
3
3
 
4
4
  /** @type {Map<string, (replicaUsername: string, manifest: import('./manifest.mjs').FileManifest) => Promise<Buffer | null>>} */
5
5
  const dagPlaintextReadersByOwner = new Map()
6
6
 
7
7
  /**
8
8
  * @param {string} ownerId 注册方
9
- * @param {{ getGroupFileMasterKey?: (replicaUsername: string, groupId: string, keyGeneration?: number) => Promise<Buffer | string | null>, getVaultMasterKey?: (replicaUsername: string, entityHash: string) => Promise<Buffer | string | null> }} deps 密钥源
9
+ * @param {{ getGroupFileMasterKey?: (replicaUsername: string, groupId: string, keyGeneration?: number) => Promise<Buffer | string | null>, getVaultMasterKey?: (replicaUsername: string, entityHash: string) => Promise<Buffer | string | null> }} dependencies 密钥源
10
10
  * @returns {void}
11
11
  */
12
- export function registerTransferKeyDeps(ownerId, deps) {
12
+ export function registerTransferKeyDependencies(ownerId, dependencies) {
13
13
  const key = String(ownerId)
14
- const prev = transferDepsByOwner.get(key) || {}
15
- transferDepsByOwner.set(key, { ...prev, ...deps })
14
+ const prev = transferDependenciesByOwner.get(key) || {}
15
+ transferDependenciesByOwner.set(key, { ...prev, ...dependencies })
16
16
  }
17
17
 
18
18
  /**
@@ -50,9 +50,9 @@ export function unregisterManifestOwnerMatchers(ownerId) {
50
50
  * @param {string} ownerId 注册方
51
51
  * @returns {void}
52
52
  */
53
- export function unregisterTransferKeyDeps(ownerId) {
53
+ export function unregisterTransferKeyDependencies(ownerId) {
54
54
  const key = String(ownerId)
55
- transferDepsByOwner.delete(key)
55
+ transferDependenciesByOwner.delete(key)
56
56
  dagPlaintextReadersByOwner.delete(key)
57
57
  unregisterManifestOwnerMatchers(key)
58
58
  }
@@ -73,9 +73,9 @@ export function resolveManifestTransferOwnerId(manifest) {
73
73
  * @param {import('./manifest.mjs').FileManifest} [manifest] 用于推断 ownerId
74
74
  * @returns {{ getGroupFileMasterKey?: (replicaUsername: string, groupId: string, keyGeneration?: number) => Promise<Buffer | string | null>, getVaultMasterKey?: (replicaUsername: string, entityHash: string) => Promise<Buffer | string | null> }} 依赖
75
75
  */
76
- export function resolveTransferKeyDeps(ownerId, manifest) {
76
+ export function resolveTransferKeyDependencies(ownerId, manifest) {
77
77
  const resolved = ownerId || (manifest ? resolveManifestTransferOwnerId(manifest) : null)
78
- if (resolved) return transferDepsByOwner.get(String(resolved)) || {}
78
+ if (resolved) return transferDependenciesByOwner.get(String(resolved)) || {}
79
79
  return {}
80
80
  }
81
81
 
@@ -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,
@@ -110,15 +110,15 @@ export function configureBufferedAmountLowThreshold(channel, thresholdBytes = CH
110
110
  /**
111
111
  * 订阅 bufferedamountlow 事件,返回取消订阅函数。
112
112
  * @param {RTCDataChannel} channel RTC 数据通道
113
- * @param {() => void} cb 低水位回调
113
+ * @param {() => void} callback 低水位回调
114
114
  * @returns {() => void} 取消订阅函数
115
115
  */
116
- export function onBufferedAmountLow(channel, cb) {
116
+ export function onBufferedAmountLow(channel, callback) {
117
117
  /**
118
118
  * bufferedamountlow 事件处理函数。
119
119
  * @returns {void}
120
120
  */
121
- const handler = () => cb()
121
+ const handler = () => callback()
122
122
  channel.addEventListener?.('bufferedamountlow', handler)
123
123
  channel.onbufferedamountlow = handler
124
124
  if (channel.bufferedAmountLow?.subscribe)
@@ -131,12 +131,12 @@ export function onBufferedAmountLow(channel, cb) {
131
131
 
132
132
  /**
133
133
  * 创建带优先级队列的双通道发送器(control/bulk)。
134
- * @param {{ getChannel: (name: 'control' | 'bulk') => RTCDataChannel | null | undefined, highWatermarkBytes?: number }} opts 通道访问与高水位配置
134
+ * @param {{ getChannel: (name: 'control' | 'bulk') => RTCDataChannel | null | undefined, highWatermarkBytes?: number }} options 通道访问与高水位配置
135
135
  * @returns {{ enqueue: (action: string, bytes: Uint8Array, preferredChannel?: 'control' | 'bulk') => void, flush: (channelName?: 'control' | 'bulk') => void, pending: () => { control: number, bulk: number }, clear: () => void }} 发送队列 API
136
136
  */
137
- export function createChannelSendQueues(opts) {
138
- const { getChannel } = opts
139
- 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)
140
140
  /** @type {{ control: Array<{ priority: number, seq: number, bytes: Uint8Array }>, bulk: Array<{ priority: number, seq: number, bytes: Uint8Array }> }} */
141
141
  const queues = { control: [], bulk: [] }
142
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)