@steve02081504/fount-p2p 0.0.22 → 0.0.24

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 (71) hide show
  1. package/README.md +8 -6
  2. package/core/entity_id.mjs +5 -5
  3. package/core/logical_entity.mjs +1 -1
  4. package/core/object.mjs +9 -0
  5. package/core/partpath.mjs +10 -0
  6. package/core/random_id.mjs +4 -2
  7. package/crypto/crypto.mjs +26 -93
  8. package/dag/storage.mjs +0 -5
  9. package/dag/strip_extensions.mjs +1 -3
  10. package/{wire/part_query_cache.mjs → federation/part_query/cache.mjs} +7 -7
  11. package/{wire/part_query.mjs → federation/part_query/runtime.mjs} +36 -77
  12. package/files/assemble.mjs +5 -5
  13. package/files/assemble_stream.mjs +2 -2
  14. package/files/chunk/fetch.mjs +101 -0
  15. package/files/chunk/pending.mjs +59 -0
  16. package/files/{chunk_provider_registry.mjs → chunk/provider_registry.mjs} +4 -5
  17. package/files/chunk/responder.mjs +104 -0
  18. package/files/{chunk_store.mjs → chunk/store.mjs} +2 -2
  19. package/files/{chunk_fetch_verify.mjs → chunk/verify.mjs} +2 -2
  20. package/files/evfs.mjs +18 -19
  21. package/files/fed/fetch_shared.mjs +39 -0
  22. package/files/fed/responder.mjs +47 -0
  23. package/files/{acl.mjs → manifest/acl.mjs} +4 -4
  24. package/files/{manifest_acl_registry.mjs → manifest/acl_registry.mjs} +14 -17
  25. package/files/{manifest_fetch.mjs → manifest/fetch.mjs} +47 -52
  26. package/files/{manifest.mjs → manifest/normalize.mjs} +2 -2
  27. package/files/manifest/pending.mjs +53 -0
  28. package/files/{public_manifest.mjs → manifest/public.mjs} +12 -12
  29. package/files/transfer_key.mjs +2 -2
  30. package/files/transfer_key_registry.mjs +7 -7
  31. package/index.mjs +4 -4
  32. package/infra/service.mjs +1 -1
  33. package/link/handshake.mjs +0 -3
  34. package/mailbox/deliver_or_store.mjs +0 -2
  35. package/mailbox/parse.mjs +1 -1
  36. package/mailbox/store.mjs +0 -9
  37. package/mailbox/wire.mjs +24 -16
  38. package/node/reputation_sync.mjs +33 -29
  39. package/package.json +9 -8
  40. package/registries/inbound.mjs +19 -9
  41. package/registries/part_path.mjs +3 -8
  42. package/schemas/discovery.mjs +1 -1
  43. package/schemas/federation_pull.mjs +1 -1
  44. package/schemas/part_query.mjs +6 -5
  45. package/transport/group_link_set.mjs +2 -2
  46. package/transport/link_registry.mjs +2 -2
  47. package/transport/node_scope/features.mjs +166 -0
  48. package/transport/node_scope/wire.mjs +124 -0
  49. package/transport/remote_user_room.mjs +0 -1
  50. package/{rooms → transport}/scoped_link.mjs +1 -1
  51. package/transport/user_room.mjs +15 -28
  52. package/utils/fetch_wait.mjs +98 -0
  53. package/wire/adapter.mjs +13 -0
  54. package/wire/ingress.mjs +1 -8
  55. package/wire/part/fanout.mjs +99 -0
  56. package/wire/part/group.mjs +45 -0
  57. package/wire/{part_ingress.mjs → part/ingress.mjs} +58 -67
  58. package/wire/{part_invoke.mjs → part/invoke.mjs} +1 -22
  59. package/wire/part/pending.mjs +32 -0
  60. package/wire/part/query.mjs +91 -0
  61. package/wire/subscribe.mjs +13 -0
  62. package/federation/chunk_fetch_pending.mjs +0 -124
  63. package/federation/manifest_fetch_pending.mjs +0 -88
  64. package/files/chunk_fetch.mjs +0 -99
  65. package/files/chunk_responder.mjs +0 -142
  66. package/transport/advert_ingest.mjs +0 -17
  67. package/transport/node_scope.mjs +0 -289
  68. package/wire/group_part.mjs +0 -43
  69. package/wire/part_common.mjs +0 -55
  70. package/wire/part_fanout.mjs +0 -52
  71. /package/{wire → schemas}/part_query.tunables.json +0 -0
@@ -1,20 +1,19 @@
1
- import { randomUUID } from 'node:crypto'
1
+ import { parseEntityHash } from '../../core/entity_id.mjs'
2
+ import { assertSafeEvfsLogicalPath } from '../../core/evfs_logical_path.mjs'
3
+ import { isWritableLocalEntity } from '../../node/identity.mjs'
4
+ import { getEntityStore } from '../../node/instance.mjs'
5
+ import { ms } from '../../utils/duration.mjs'
6
+ import { createInflightTable } from '../../utils/inflight_table.mjs'
7
+ import { loadFileManifest, saveFileManifest } from '../evfs.mjs'
8
+ import { beginFedFanoutFetch } from '../fed/fetch_shared.mjs'
2
9
 
10
+ import { normalizeFileManifest } from './normalize.mjs'
3
11
  import {
4
12
  manifestFetchExpectedKey,
5
13
  MAX_PENDING_MANIFEST_FETCHES,
6
14
  registerManifestFetchWait,
7
- } from '../federation/manifest_fetch_pending.mjs'
8
- import { isWritableLocalEntity } from '../node/identity.mjs'
9
- import { getEntityStore } from '../node/instance.mjs'
10
- import { ms } from '../utils/duration.mjs'
11
- import { createInflightTable } from '../utils/inflight_table.mjs'
12
-
13
- import { resolveNodeHash } from './chunk_provider_registry.mjs'
14
- import { loadFileManifest, saveFileManifest } from './evfs.mjs'
15
- import { fanoutFedFetch } from './fetch_fanout.mjs'
16
- import { normalizeFileManifest } from './manifest.mjs'
17
- import { shouldPreferIncomingPublicManifest } from './public_manifest.mjs'
15
+ } from './pending.mjs'
16
+ import { shouldPreferIncomingPublicManifest } from './public.mjs'
18
17
 
19
18
  const DEFAULT_MANIFEST_FETCH_TIMEOUT_MS = ms('8s')
20
19
 
@@ -29,7 +28,7 @@ const manifestInflight = createInflightTable({
29
28
  * 本地已有 publicSig 时立即返回,并后台 fanout;`cache: true` 时按 publishedAt 择新写盘。
30
29
  * 冷 miss 仍等 fanout。同 key in-flight 去重;调用方外层超时不 abort,后台继续填缓存。
31
30
  * @param {{ username: string, ownerEntityHash: string, logicalPath: string, cache?: boolean, timeoutMs?: number }} context - 拉取上下文
32
- * @returns {Promise<import('./manifest.mjs').FileManifest | null>} 验签后的 manifest,失败为 null
31
+ * @returns {Promise<import('./normalize.mjs').FileManifest | null>} 验签后的 manifest,失败为 null
33
32
  */
34
33
  export async function fetchPublicManifest(context) {
35
34
  const ownerEntityHash = String(context.ownerEntityHash || '').trim().toLowerCase()
@@ -41,27 +40,31 @@ export async function fetchPublicManifest(context) {
41
40
  ? Number(context.timeoutMs)
42
41
  : DEFAULT_MANIFEST_FETCH_TIMEOUT_MS
43
42
  const expectedKey = manifestFetchExpectedKey(ownerEntityHash, logicalPath)
44
- const inflightKey = `${username}\0${expectedKey}`
45
43
  const wantCache = context.cache === true
46
44
 
47
45
  // 先挂 in-flight(同步),再读本地 — 避免并发调用在 await 间隙各自 start
48
46
  const localPromise = loadFileManifest(ownerEntityHash, logicalPath)
49
- const shared = manifestInflight.acquire(inflightKey, () => {
50
- const requestId = randomUUID()
51
- const wait = registerManifestFetchWait(requestId, expectedKey, timeoutMs)
52
- void (async () => {
53
- try {
54
- const { nodeHash } = await resolveNodeHash(username)
55
- await fanoutFedFetch(username, 'fed_manifest_get', {
56
- requestId,
57
- nodeHash,
58
- ownerEntityHash,
59
- logicalPath,
60
- })
61
- }
62
- catch { /* pending wait 超时/cancel 负责 settle */ }
63
- })()
64
- return { done: wait.done, cancel: wait.cancel }
47
+ const shared = beginFedFanoutFetch({
48
+ inflight: manifestInflight,
49
+ inflightKey: `${username}\0${expectedKey}`,
50
+ username,
51
+ action: 'fed_manifest_get',
52
+ /**
53
+ * @param {string} requestId 请求 id
54
+ * @returns {{ done: Promise<import('./normalize.mjs').FileManifest | null>, cancel: () => void }} 等待句柄
55
+ */
56
+ registerWait: requestId => registerManifestFetchWait(requestId, expectedKey, timeoutMs),
57
+ /**
58
+ * @param {string} requestId 请求 id
59
+ * @param {string} nodeHash 目标节点 hash
60
+ * @returns {object} fanout 载荷
61
+ */
62
+ buildPayload: (requestId, nodeHash) => ({
63
+ requestId,
64
+ nodeHash,
65
+ ownerEntityHash,
66
+ logicalPath,
67
+ }),
65
68
  })
66
69
 
67
70
  const local = await localPromise
@@ -69,13 +72,12 @@ export async function fetchPublicManifest(context) {
69
72
  if (!shared) return hasLocalPublic ? local : null
70
73
 
71
74
  if (hasLocalPublic) {
72
- // SWR:立刻回本地;后台 fanout 择新写盘(需 cache: true)
73
- if (wantCache) {
75
+ if (wantCache)
74
76
  void shared.then(async result => {
75
77
  if (result && shouldPreferIncomingPublicManifest(local, result))
76
78
  await cachePublicManifest(ownerEntityHash, logicalPath, result)
77
79
  })
78
- }
80
+
79
81
  return local
80
82
  }
81
83
 
@@ -89,41 +91,34 @@ export async function fetchPublicManifest(context) {
89
91
  * 将已验签公开 manifest 写入本地缓存(显式 apply;`fetchPublicManifest` 默认不调用)。
90
92
  * @param {string} ownerEntityHash owner
91
93
  * @param {string} logicalPath 路径
92
- * @param {import('./manifest.mjs').FileManifest} incoming 已验签入站清单
94
+ * @param {import('./normalize.mjs').FileManifest} incoming 已验签入站清单
93
95
  * @returns {Promise<void>}
94
96
  */
95
97
  export async function cachePublicManifest(ownerEntityHash, logicalPath, incoming) {
96
- await maybeCacheIncomingPublicManifest(ownerEntityHash, logicalPath, incoming)
97
- }
98
-
99
- /**
100
- * @param {string} ownerEntityHash owner
101
- * @param {string} logicalPath 路径
102
- * @param {import('./manifest.mjs').FileManifest} incoming 已验签入站清单
103
- * @returns {Promise<void>}
104
- */
105
- async function maybeCacheIncomingPublicManifest(ownerEntityHash, logicalPath, incoming) {
106
98
  if (isWritableLocalEntity(ownerEntityHash)) return
107
- const store = getEntityStore()
108
- const existing = await store.readManifest(ownerEntityHash, logicalPath)
99
+ const existing = await getEntityStore().readManifest(ownerEntityHash, logicalPath)
109
100
  if (existing && !shouldPreferIncomingPublicManifest(existing, incoming)) return
110
101
  await saveFileManifest(incoming)
111
102
  }
112
103
 
113
104
  /**
114
105
  * 若本机有已签名公开 manifest 则响应 fed_manifest_get。
115
- * @param {string} username 用户
116
106
  * @param {object} payload 请求
117
107
  * @param {(response: object, peerId: string) => void} sendResponse 发送
118
108
  * @param {string} peerId 对端
119
109
  * @returns {Promise<void>}
120
110
  */
121
- export async function handleIncomingManifestGet(username, payload, sendResponse, peerId) {
122
- void username
123
- const ownerEntityHash = String(payload?.ownerEntityHash || '').trim().toLowerCase()
124
- const logicalPath = String(payload?.logicalPath || '').trim().replace(/^\/+/, '')
111
+ export async function handleIncomingManifestGet(payload, sendResponse, peerId) {
112
+ const parsedOwner = parseEntityHash(payload?.ownerEntityHash)
113
+ if (!parsedOwner) return
114
+ let logicalPath
115
+ try {
116
+ logicalPath = assertSafeEvfsLogicalPath(payload?.logicalPath)
117
+ }
118
+ catch { return }
125
119
  const requestId = String(payload?.requestId || '')
126
- if (!ownerEntityHash || !logicalPath || !requestId) return
120
+ if (!requestId) return
121
+ const { entityHash: ownerEntityHash } = parsedOwner
127
122
 
128
123
  const raw = await getEntityStore().readManifest(ownerEntityHash, logicalPath)
129
124
  const manifest = normalizeFileManifest(raw)
@@ -1,5 +1,5 @@
1
- import { isEntityHash128 } from '../core/entity_id.mjs'
2
- import { isHex64 } from '../core/hexIds.mjs'
1
+ import { isEntityHash128 } from '../../core/entity_id.mjs'
2
+ import { isHex64 } from '../../core/hexIds.mjs'
3
3
 
4
4
  /** @typedef {'plain' | 'convergent' | 'random'} CeMode */
5
5
 
@@ -0,0 +1,53 @@
1
+ import { compositeKey } from '../../core/composite_key.mjs'
2
+ import { createFetchWaitTable } from '../../utils/fetch_wait.mjs'
3
+
4
+ import { verifySignedPublicManifest } from './public.mjs'
5
+
6
+ /** 并发 pending manifest fetch 上限。 */
7
+ export const MAX_PENDING_MANIFEST_FETCHES = 512
8
+
9
+ const table = createFetchWaitTable({ maxSize: MAX_PENDING_MANIFEST_FETCHES })
10
+
11
+ /** @type {typeof table.pending} */
12
+ export const pendingManifestFetches = table.pending
13
+
14
+ /**
15
+ * @param {string} ownerEntityHash owner
16
+ * @param {string} logicalPath 路径
17
+ * @returns {string} 期望键
18
+ */
19
+ export function manifestFetchExpectedKey(ownerEntityHash, logicalPath) {
20
+ return compositeKey(
21
+ String(ownerEntityHash || '').trim().toLowerCase(),
22
+ String(logicalPath || '').trim().replace(/^\/+/, ''),
23
+ )
24
+ }
25
+
26
+ /**
27
+ * @param {string} key requestId
28
+ * @param {string} expectedKey owner+path 复合键
29
+ * @param {number} timeoutMs 超时毫秒
30
+ * @returns {{ done: Promise<object | null>, cancel: () => void }} 等待 Promise 与取消
31
+ */
32
+ export function registerManifestFetchWait(key, expectedKey, timeoutMs) {
33
+ return table.register(key, expectedKey, timeoutMs)
34
+ }
35
+
36
+ /**
37
+ * 处理 fed_manifest_data:验签通过后 resolve pending。
38
+ * @param {object} payload 入站载荷
39
+ * @returns {Promise<boolean>} 是否命中并完成等待
40
+ */
41
+ export async function resolvePendingManifestFetch(payload) {
42
+ const requestId = String(payload?.requestId || '')
43
+ if (!requestId) return false
44
+ const entry = table.peek(requestId)
45
+ if (!entry) return false
46
+
47
+ const verified = await verifySignedPublicManifest(payload?.manifest)
48
+ if (!verified) return false
49
+ const key = manifestFetchExpectedKey(verified.ownerEntityHash, verified.logicalPath)
50
+ if (key !== entry.expectedKey) return false
51
+
52
+ return table.settle(requestId, verified)
53
+ }
@@ -1,13 +1,13 @@
1
1
  import { Buffer } from 'node:buffer'
2
2
 
3
- import { canonicalStringify } from '../core/canonical_json.mjs'
4
- import { hashFromPubKeyHex, parseEntityHash } from '../core/entity_id.mjs'
5
- import { assertSafeEvfsLogicalPath } from '../core/evfs_logical_path.mjs'
6
- import { isHex64, normalizeHex64 } from '../core/hexIds.mjs'
7
- import { sign, verify } from '../crypto/crypto.mjs'
3
+ import { canonicalStringify } from '../../core/canonical_json.mjs'
4
+ import { hashFromPubKeyHex, parseEntityHash } from '../../core/entity_id.mjs'
5
+ import { assertSafeEvfsLogicalPath } from '../../core/evfs_logical_path.mjs'
6
+ import { isHex64, normalizeHex64 } from '../../core/hexIds.mjs'
7
+ import { sign, verify } from '../../crypto/crypto.mjs'
8
+ import { putFileManifest, saveFileManifest } from '../evfs.mjs'
8
9
 
9
- import { putFileManifest, saveFileManifest } from './evfs.mjs'
10
- import { normalizeFileManifest, publicTransferKeyDescriptor } from './manifest.mjs'
10
+ import { normalizeFileManifest, publicTransferKeyDescriptor } from './normalize.mjs'
11
11
 
12
12
  /** 实体公开 manifest 签名域 */
13
13
  export const ENTITY_PUBLIC_MANIFEST_DOMAIN = 'fount-entity-public-manifest'
@@ -32,11 +32,11 @@ export function publicManifestSignBytes(fields) {
32
32
  }
33
33
 
34
34
  /**
35
- * @param {import('./manifest.mjs').FileManifest} manifest 清单
35
+ * @param {import('./normalize.mjs').FileManifest} manifest 清单
36
36
  * @param {number} publishedAt 发布时间
37
37
  * @param {Uint8Array | Buffer} entitySecretKey recovery 私钥种子
38
38
  * @param {string} entityPubKeyHex recovery 公钥 hex
39
- * @returns {Promise<import('./manifest.mjs').FileManifest>} 带 publicSig 的清单
39
+ * @returns {Promise<import('./normalize.mjs').FileManifest>} 带 publicSig 的清单
40
40
  */
41
41
  export async function attachPublicManifestSig(manifest, publishedAt, entitySecretKey, entityPubKeyHex) {
42
42
  const pubKeyHex = normalizeHex64(entityPubKeyHex)
@@ -63,7 +63,7 @@ export async function attachPublicManifestSig(manifest, publishedAt, entitySecre
63
63
 
64
64
  /**
65
65
  * @param {unknown} input 原始 manifest
66
- * @returns {Promise<import('./manifest.mjs').FileManifest | null>} 验签通过的清单;非法为 null
66
+ * @returns {Promise<import('./normalize.mjs').FileManifest | null>} 验签通过的清单;非法为 null
67
67
  */
68
68
  export async function verifySignedPublicManifest(input) {
69
69
  const manifest = normalizeFileManifest(input)
@@ -105,7 +105,7 @@ export async function verifySignedPublicManifest(input) {
105
105
 
106
106
  /**
107
107
  * @param {object | null | undefined} localManifest 本地已有清单
108
- * @param {import('./manifest.mjs').FileManifest} incoming 入站已验签清单
108
+ * @param {import('./normalize.mjs').FileManifest} incoming 入站已验签清单
109
109
  * @returns {boolean} 是否应以 incoming 覆盖本地缓存
110
110
  */
111
111
  export function shouldPreferIncomingPublicManifest(localManifest, incoming) {
@@ -125,7 +125,7 @@ export function shouldPreferIncomingPublicManifest(localManifest, incoming) {
125
125
  * @param {Uint8Array | Buffer} parameters.entitySecretKey recovery 私钥种子
126
126
  * @param {string} parameters.entityPubKeyHex recovery 公钥 hex
127
127
  * @param {number} [parameters.publishedAt] 发布时间(默认 Date.now)
128
- * @returns {Promise<import('./manifest.mjs').FileManifest>} 已签名并落盘的公开清单
128
+ * @returns {Promise<import('./normalize.mjs').FileManifest>} 已签名并落盘的公开清单
129
129
  */
130
130
  export async function publishPublicFile(parameters) {
131
131
  const {
@@ -8,8 +8,8 @@ import {
8
8
  } from '../crypto/key.mjs'
9
9
 
10
10
  /**
11
- * @typedef {import('./manifest.mjs').FileManifest} FileManifest
12
- * @typedef {import('./manifest.mjs').TransferKeyDescriptor} TransferKeyDescriptor
11
+ * @typedef {import('./manifest/normalize.mjs').FileManifest} FileManifest
12
+ * @typedef {import('./manifest/normalize.mjs').TransferKeyDescriptor} TransferKeyDescriptor
13
13
  */
14
14
 
15
15
  /**
@@ -1,7 +1,7 @@
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
2
  const transferDependenciesByOwner = new Map()
3
3
 
4
- /** @type {Map<string, (replicaUsername: string, manifest: import('./manifest.mjs').FileManifest) => Promise<Buffer | null>>} */
4
+ /** @type {Map<string, (replicaUsername: string, manifest: import('./manifest/normalize.mjs').FileManifest) => Promise<Buffer | null>>} */
5
5
  const dagPlaintextReadersByOwner = new Map()
6
6
 
7
7
  /**
@@ -17,19 +17,19 @@ export function registerTransferKeyDependencies(ownerId, dependencies) {
17
17
 
18
18
  /**
19
19
  * @param {string} ownerId 注册方
20
- * @param {(replicaUsername: string, manifest: import('./manifest.mjs').FileManifest) => Promise<Buffer | null>} reader dagParts 明文读取
20
+ * @param {(replicaUsername: string, manifest: import('./manifest/normalize.mjs').FileManifest) => Promise<Buffer | null>} reader dagParts 明文读取
21
21
  * @returns {void}
22
22
  */
23
23
  export function registerDagManifestPlaintextReader(ownerId, reader) {
24
24
  dagPlaintextReadersByOwner.set(String(ownerId), reader)
25
25
  }
26
26
 
27
- /** @type {Array<{ ownerId: string, match: (manifest: import('./manifest.mjs').FileManifest) => boolean }>} */
27
+ /** @type {Array<{ ownerId: string, match: (manifest: import('./manifest/normalize.mjs').FileManifest) => boolean }>} */
28
28
  const manifestOwnerMatchers = []
29
29
 
30
30
  /**
31
31
  * @param {string} ownerId 注册方
32
- * @param {(manifest: import('./manifest.mjs').FileManifest) => boolean} match manifest 匹配谓词
32
+ * @param {(manifest: import('./manifest/normalize.mjs').FileManifest) => boolean} match manifest 匹配谓词
33
33
  * @returns {void}
34
34
  */
35
35
  export function registerManifestOwnerMatcher(ownerId, match) {
@@ -59,7 +59,7 @@ export function unregisterTransferKeyDependencies(ownerId) {
59
59
 
60
60
  /**
61
61
  * 从 manifest 推断 transfer key 注册方 id(按 registerManifestOwnerMatcher 注册顺序匹配)。
62
- * @param {import('./manifest.mjs').FileManifest} manifest 清单
62
+ * @param {import('./manifest/normalize.mjs').FileManifest} manifest 清单
63
63
  * @returns {string | null} 所有者 id
64
64
  */
65
65
  export function resolveManifestTransferOwnerId(manifest) {
@@ -70,7 +70,7 @@ export function resolveManifestTransferOwnerId(manifest) {
70
70
 
71
71
  /**
72
72
  * @param {string} [ownerId] 显式注册方;省略时按 manifest 推断
73
- * @param {import('./manifest.mjs').FileManifest} [manifest] 用于推断 ownerId
73
+ * @param {import('./manifest/normalize.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
76
  export function resolveTransferKeyDependencies(ownerId, manifest) {
@@ -81,7 +81,7 @@ export function resolveTransferKeyDependencies(ownerId, manifest) {
81
81
 
82
82
  /**
83
83
  * @param {string} replicaUsername 副本用户名
84
- * @param {import('./manifest.mjs').FileManifest} manifest 清单
84
+ * @param {import('./manifest/normalize.mjs').FileManifest} manifest 清单
85
85
  * @returns {Promise<Buffer | null>} 明文;无 reader 或未命中为 null
86
86
  */
87
87
  export async function readDagManifestPlaintext(replicaUsername, manifest) {
package/index.mjs CHANGED
@@ -32,7 +32,6 @@ import {
32
32
  unlockReputationMax,
33
33
  } from './node/reputation_sync.mjs'
34
34
  import { getRoutingProfile, setRoutingProfile } from './node/routing_profile.mjs'
35
- import { createScopedLinkRoom } from './rooms/scoped_link.mjs'
36
35
  import { createGroupLinkSet } from './transport/group_link_set.mjs'
37
36
  import {
38
37
  configureLinkRegistry,
@@ -42,9 +41,10 @@ import {
42
41
  reloadDiscoveryRelays,
43
42
  sendToNodeLink,
44
43
  } from './transport/link_registry.mjs'
44
+ import { attachNodeScopeDefaultFeatures } from './transport/node_scope/features.mjs'
45
+ import { ensureNodeScope } from './transport/node_scope/wire.mjs'
46
+ import { createScopedLinkRoom } from './transport/scoped_link.mjs'
45
47
  import {
46
- attachUserRoomDefaultWires,
47
- ensureNodeScope,
48
48
  ensureUserRoom,
49
49
  getUserRoomSlot,
50
50
  } from './transport/user_room.mjs'
@@ -54,7 +54,7 @@ import {
54
54
  */
55
55
  export {
56
56
  attachReputationSyncWire,
57
- attachUserRoomDefaultWires,
57
+ attachNodeScopeDefaultFeatures,
58
58
  configureLinkRegistry,
59
59
  createGroupLinkSet,
60
60
  createScopedLinkRoom,
package/infra/service.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import { isNodeInitialized } from '../node/instance.mjs'
3
3
  import { setOverlayRateGate, clearOverlayRateGate } from '../overlay/index.mjs'
4
4
  import { getLinkRegistry } from '../transport/link_registry.mjs'
5
- import { attachNodeScopeMailbox } from '../transport/node_scope.mjs'
5
+ import { attachNodeScopeMailbox } from '../transport/node_scope/features.mjs'
6
6
 
7
7
  import { attachInfraDebugLog, detachInfraDebugLog } from './debug_log.mjs'
8
8
  import {
@@ -9,9 +9,6 @@ import { ensureNodeSeed, getNodeHash } from '../node/identity.mjs'
9
9
 
10
10
  import { normalizeDtlsFingerprint } from './sdp_fingerprint.mjs'
11
11
 
12
- /** advert / peer-hint 端口规范化(re-export,调用方可直接从 core/tcp_port 导入) */
13
- export const normalizeAdvertTcpPort = normalizeTcpPort
14
-
15
12
  /**
16
13
  * Link 握手签名域标识符。
17
14
  */
@@ -23,7 +23,6 @@ import {
23
23
  * @returns {Promise<string | null>} 已验证的 remote nodeHash
24
24
  */
25
25
  async function resolveRemoteNodeHashForPeer(username, peerId) {
26
- void username
27
26
  if (!peerId) return null
28
27
  const entry = activeLinkRoster().find(row => row.peerId === peerId)
29
28
  const remote = entry?.remoteNodeHash?.trim().toLowerCase()
@@ -51,7 +50,6 @@ async function resolveRelayHopForIngress(record) {
51
50
  * @returns {Promise<{ maxHop: number, relayFanoutTrusted: number, relayFanoutNormal: number, wantFanout: number, batterySaver: boolean }>} 按在线 peer 数缩放的路由
52
51
  */
53
52
  async function resolveRouting(username) {
54
- void username
55
53
  const { batterySaver, mailbox } = getNodeTransportSettings()
56
54
  const peerCount = activeLinkRoster().length
57
55
  return resolveMailboxRoutingForPeerCount(peerCount, mailbox, batterySaver)
package/mailbox/parse.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import { assertHex64 } from '../core/hexIds.mjs'
2
+ import { isPlainObject } from '../core/object.mjs'
2
3
  import {
3
4
  assertMailboxPubKeyHash,
4
5
  assertMailboxRecordShape,
5
6
  } from '../schemas/mailbox.mjs'
6
- import { isPlainObject } from '../wire/ingress.mjs'
7
7
 
8
8
  /**
9
9
  * @typedef {import('../schemas/mailbox.mjs').MailboxWireErr} MailboxParseErr
package/mailbox/store.mjs CHANGED
@@ -11,19 +11,10 @@ import {
11
11
  sortMailboxForRetention,
12
12
  } from './importance.mjs'
13
13
  import {
14
- MAX_BUCKET_BYTES,
15
- MAX_BUCKET_ENTRIES,
16
- mailboxBucketKey,
17
- mailboxRecordBytes,
18
14
  pruneMailboxBuckets,
19
15
  pruneMailboxGlobalFair,
20
16
  } from './prune.mjs'
21
17
 
22
- /**
23
- * mailbox 桶裁剪与计量辅助(re-export)。
24
- */
25
- export { MAX_BUCKET_BYTES, MAX_BUCKET_ENTRIES, mailboxBucketKey, mailboxRecordBytes }
26
-
27
18
  /** 单条 mailbox envelope JSON 字节上限 */
28
19
  export const MAX_ENTRY_BYTES = 256 * 1024
29
20
 
package/mailbox/wire.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { subscribeWire } from '../wire/subscribe.mjs'
2
+
1
3
  import {
2
4
  ingestMailboxGive,
3
5
  ingestMailboxPut,
@@ -5,23 +7,30 @@ import {
5
7
  } from './deliver_or_store.mjs'
6
8
  import { parseMailboxGive, parseMailboxPut, parseMailboxWant } from './parse.mjs'
7
9
 
8
- /**
9
- * @typedef {{ replicaUsername?: string }} MailboxWireContext
10
- */
10
+ /** @typedef {import('../wire/adapter.mjs').WireContext} MailboxWireContext */
11
+ /** @typedef {import('../wire/adapter.mjs').WireAdapter} WireAdapter */
11
12
 
12
13
  /**
13
14
  * @param {MailboxWireContext} wireContext 入站上下文
14
- * @param {{ on: (name: string, handler: (payload: unknown, peerId: string) => void) => (() => void) | void, send: (name: string, payload: unknown, peerId: string | null) => void }} wire Trystero 适配器
15
+ * @param {WireAdapter} wire action 表
15
16
  * @returns {() => void} 取消挂载的 dispose
16
17
  */
17
18
  export function attachMailboxWire(wireContext, wire) {
18
- const offs = [
19
- wire.on('mailbox_put', (payload, peerId) => {
19
+ return subscribeWire(wire, {
20
+ /**
21
+ * @param {unknown} payload mailbox_put 载荷
22
+ * @param {string} peerId 对端 nodeHash
23
+ */
24
+ mailbox_put(payload, peerId) {
20
25
  const put = parseMailboxPut(payload)
21
26
  if (!put.ok) return
22
27
  void ingestMailboxPut(wireContext, put.value, peerId).catch(error => console.error('mailbox: put ingest failed', error))
23
- }),
24
- wire.on('mailbox_want', (payload, peerId) => {
28
+ },
29
+ /**
30
+ * @param {unknown} payload mailbox_want 载荷
31
+ * @param {string} peerId 对端 nodeHash
32
+ */
33
+ mailbox_want(payload, peerId) {
25
34
  const want = parseMailboxWant(payload)
26
35
  if (!want.ok) return
27
36
  void respondMailboxWant(want.value, (giveWire, targetPeerId) => {
@@ -30,15 +39,14 @@ export function attachMailboxWire(wireContext, wire) {
30
39
  }
31
40
  catch { /* disconnected */ }
32
41
  }, peerId).catch(error => console.error('mailbox: want failed', error))
33
- }),
34
- wire.on('mailbox_give', payload => {
42
+ },
43
+ /**
44
+ * @param {unknown} payload mailbox_give 载荷
45
+ */
46
+ mailbox_give(payload) {
35
47
  const give = parseMailboxGive(payload)
36
48
  if (!give.ok) return
37
49
  void ingestMailboxGive(wireContext, give.value).catch(error => console.error('mailbox: give ingest failed', error))
38
- }),
39
- ]
40
- return () => {
41
- for (const off of offs)
42
- try { off?.() } catch { /* ignore */ }
43
- }
50
+ },
51
+ })
44
52
  }
@@ -2,7 +2,9 @@ import { randomUUID } from 'node:crypto'
2
2
 
3
3
  import { isHex64, normalizeHex64 } from '../core/hexIds.mjs'
4
4
  import { sendToNodeLink } from '../transport/link_registry.mjs'
5
- import { attachNodeScopeFeature, ensureNodeScope, getNodeScopeWire } from '../transport/node_scope.mjs'
5
+ import { attachNodeScopeFeature } from '../transport/node_scope/features.mjs'
6
+ import { ensureNodeScope, getNodeScopeWire } from '../transport/node_scope/wire.mjs'
7
+ import { subscribeWire } from '../wire/subscribe.mjs'
6
8
 
7
9
  import {
8
10
  loadReputation,
@@ -226,34 +228,36 @@ function exportScoreTable() {
226
228
  export function attachReputationSyncWire() {
227
229
  ensureNodeScope()
228
230
  if (!getNodeScopeWire()) throw new Error('p2p: attachReputationSyncWire requires node scope wire')
229
- const dispose = attachNodeScopeFeature('rep_sync', wire => {
230
- const offs = [
231
- wire.on('rep_sync_req', (payload, peerId) => {
232
- const requester = normalizeHex64(peerId)
233
- if (!requester || !getReputationExportAllowlist().includes(requester)) return
234
- try {
235
- wire.send('rep_sync_res', {
236
- requestId: payload?.requestId,
237
- ...exportScoreTable(),
238
- }, peerId)
239
- }
240
- catch { /* disconnected */ }
241
- }),
242
- wire.on('rep_sync_res', (payload, peerId) => {
243
- const requestId = String(payload?.requestId || '')
244
- const pending = pendingPulls.get(requestId)
245
- if (!pending) return
246
- if (normalizeHex64(peerId) !== pending.donor) return
247
- clearTimeout(pending.timer)
248
- pendingPulls.delete(requestId)
249
- pending.resolve(payload)
250
- }),
251
- ]
252
- return () => {
253
- for (const off of offs)
254
- try { off?.() } catch { /* ignore */ }
255
- }
256
- })
231
+ const dispose = attachNodeScopeFeature('rep_sync', wire => subscribeWire(wire, {
232
+ /**
233
+ * @param {unknown} payload rep_sync_req 载荷
234
+ * @param {string} peerId 对端 nodeHash
235
+ */
236
+ rep_sync_req(payload, peerId) {
237
+ const requester = normalizeHex64(peerId)
238
+ if (!requester || !getReputationExportAllowlist().includes(requester)) return
239
+ try {
240
+ wire.send('rep_sync_res', {
241
+ requestId: payload?.requestId,
242
+ ...exportScoreTable(),
243
+ }, peerId)
244
+ }
245
+ catch { /* disconnected */ }
246
+ },
247
+ /**
248
+ * @param {unknown} payload rep_sync_res 载荷
249
+ * @param {string} peerId 对端 nodeHash
250
+ */
251
+ rep_sync_res(payload, peerId) {
252
+ const requestId = String(payload?.requestId || '')
253
+ const pending = pendingPulls.get(requestId)
254
+ if (!pending) return
255
+ if (normalizeHex64(peerId) !== pending.donor) return
256
+ clearTimeout(pending.timer)
257
+ pendingPulls.delete(requestId)
258
+ pending.resolve(payload)
259
+ },
260
+ }))
257
261
  syncWireDisposers.add(dispose)
258
262
  return () => {
259
263
  if (!syncWireDisposers.delete(dispose)) return
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steve02081504/fount-p2p",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "fount federation P2P layer — link, trust graph, mailbox, DAG, EVFS.",
5
5
  "keywords": [
6
6
  "network",
@@ -41,19 +41,15 @@
41
41
  "./crypto": "./crypto/crypto.mjs",
42
42
  "./crypto/*": "./crypto/*.mjs",
43
43
  "./wire/*": "./wire/*.mjs",
44
+ "./wire/part/*": "./wire/part/*.mjs",
44
45
  "./schemas/*": "./schemas/*.mjs",
45
46
  "./node/*": "./node/*.mjs",
46
47
  "./discovery": "./discovery/index.mjs",
47
48
  "./discovery/bt": "./discovery/bt/index.mjs",
48
49
  "./discovery/bt/*": "./discovery/bt/*.mjs",
49
50
  "./discovery/*": "./discovery/*.mjs",
50
- "./transport/link_registry": "./transport/link_registry.mjs",
51
- "./transport/user_room": "./transport/user_room.mjs",
52
- "./transport/group_link_set": "./transport/group_link_set.mjs",
53
- "./transport/node_scope": "./transport/node_scope.mjs",
54
- "./transport/room_scopes": "./transport/room_scopes.mjs",
55
- "./transport/remote_user_room": "./transport/remote_user_room.mjs",
56
- "./rooms/*": "./rooms/*.mjs",
51
+ "./transport/node_scope/*": "./transport/node_scope/*.mjs",
52
+ "./transport/*": "./transport/*.mjs",
57
53
  "./overlay": "./overlay/index.mjs",
58
54
  "./overlay/*": "./overlay/*.mjs",
59
55
  "./trust_graph/*": "./trust_graph/*.mjs",
@@ -62,7 +58,11 @@
62
58
  "./dag/*": "./dag/*.mjs",
63
59
  "./timeline/*": "./timeline/*.mjs",
64
60
  "./files/*": "./files/*.mjs",
61
+ "./files/chunk/*": "./files/chunk/*.mjs",
62
+ "./files/manifest/*": "./files/manifest/*.mjs",
63
+ "./files/fed/*": "./files/fed/*.mjs",
65
64
  "./federation/*": "./federation/*.mjs",
65
+ "./federation/part_query/*": "./federation/part_query/*.mjs",
66
66
  "./governance/*": "./governance/*.mjs",
67
67
  "./registries/*": "./registries/*.mjs",
68
68
  "./permissions": "./permissions/index.mjs",
@@ -71,6 +71,7 @@
71
71
  },
72
72
  "dependencies": {
73
73
  "@noble/curves": "latest",
74
+ "@noble/hashes": "latest",
74
75
  "node-rtc-connection": "latest",
75
76
  "on-shutdown": "latest",
76
77
  "ws": "latest"