@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,289 +0,0 @@
1
- import { attachNodeScopeFedChunkResponder } from '../files/chunk_responder.mjs'
2
- import { attachMailboxWire } from '../mailbox/wire.mjs'
3
- import { attachPartWire } from '../wire/part_ingress.mjs'
4
- import { attachPartQueryWire } from '../wire/part_query.mjs'
5
-
6
- import { sendToNodeLink, subscribeScope } from './link_registry.mjs'
7
-
8
- /** @type {Map<string, Set<(payload: unknown, peerId: string) => void>>} */
9
- const nodeActionHandlers = new Map()
10
-
11
- /** @type {Set<(context: NodeScopeContext, wire: NodeScopeWire) => void>} */
12
- const nodeScopeWireHooks = new Set()
13
-
14
- /** @type {NodeScopeContext} */
15
- const nodeScopeContext = { replicaUsername: '' }
16
-
17
- /** @type {NodeScopeWire | null} */
18
- let nodeScopeWire = null
19
-
20
- /** @type {(() => void) | null} */
21
- let nodeScopeSubscribeCleanup = null
22
-
23
- /** @type {Set<() => void>} */
24
- const nodeScopeFeatureDisposers = new Set()
25
-
26
- /** @type {Map<string, { count: number, disposeCore: () => void }>} */
27
- const featureAttachRefs = new Map()
28
-
29
- /**
30
- * @typedef {{ replicaUsername: string }} NodeScopeContext
31
- */
32
-
33
- /**
34
- * @typedef {{
35
- * on: (name: string, handler: (payload: unknown, peerId: string) => void) => () => void
36
- * send: (name: string, payload: unknown, peerId: string | null) => void
37
- * }} NodeScopeWire
38
- */
39
-
40
- /**
41
- * @param {string} peerId 对端 nodeHash
42
- * @param {string} action 动作名
43
- * @param {unknown} payload 载荷
44
- * @returns {Promise<boolean>} 是否成功发出
45
- */
46
- const sendNodeAction = (peerId, action, payload) =>
47
- sendToNodeLink(peerId, { scope: 'node', action, payload })
48
-
49
- /**
50
- * @returns {NodeScopeWire} node scope 的 wire 适配器
51
- */
52
- function createNodeScopeWire() {
53
- return {
54
- /**
55
- * @param {string} name - action 名
56
- * @param {(payload: unknown, peerId: string) => void} handler - 入站处理器
57
- * @returns {() => void} 取消注册的 dispose
58
- */
59
- on(name, handler) {
60
- if (!nodeActionHandlers.has(name)) nodeActionHandlers.set(name, new Set())
61
- nodeActionHandlers.get(name).add(handler)
62
- return () => {
63
- const set = nodeActionHandlers.get(name)
64
- if (!set) return
65
- set.delete(handler)
66
- if (!set.size) nodeActionHandlers.delete(name)
67
- }
68
- },
69
- /**
70
- * @param {string} name - action 名
71
- * @param {unknown} payload - 出站载荷
72
- * @param {string | null} peerId - 目标 peer,null 时忽略
73
- * @returns {void}
74
- */
75
- send(name, payload, peerId) {
76
- if (!peerId) return
77
- void sendNodeAction(peerId, name, payload).catch(() => { })
78
- },
79
- }
80
- }
81
-
82
- /**
83
- * @returns {boolean} 是否已订阅 node scope
84
- */
85
- export function isNodeScopeSubscribed() {
86
- return nodeScopeSubscribeCleanup != null
87
- }
88
-
89
- /**
90
- * @param {string} action - action 名
91
- * @returns {boolean} 是否已挂载处理器
92
- */
93
- export function hasNodeScopeAction(action) {
94
- return (nodeActionHandlers.get(action)?.size ?? 0) > 0
95
- }
96
-
97
- /**
98
- * @param {string} action - action 名
99
- * @returns {number} 已注册的处理器数量
100
- */
101
- export function countNodeScopeActionHandlers(action) {
102
- return nodeActionHandlers.get(action)?.size ?? 0
103
- }
104
-
105
- /**
106
- * 测试/调试:直接派发已挂载的 node action。
107
- * @param {string} action - action 名
108
- * @param {unknown} payload - 载荷
109
- * @param {string} peerId - 发送方 nodeHash
110
- * @returns {boolean} 是否有处理器被调用
111
- */
112
- export function dispatchNodeScopeAction(action, payload, peerId) {
113
- const handlers = nodeActionHandlers.get(action)
114
- if (!handlers?.size) return false
115
- for (const handler of handlers)
116
- try { handler(payload, peerId) } catch { /* ignore */ }
117
- return true
118
- }
119
-
120
- /**
121
- * @returns {NodeScopeWire | null} 当前 wire,未 ensure 时为 null
122
- */
123
- export function getNodeScopeWire() {
124
- return nodeScopeWire
125
- }
126
-
127
- /**
128
- * @returns {NodeScopeContext} 可变 node scope 上下文
129
- */
130
- export function getNodeScopeContext() {
131
- return nodeScopeContext
132
- }
133
-
134
- /**
135
- * 只订阅 node scope 派发,不挂任何 feature。
136
- * @param {{ replicaUsername?: string }} [options] - 可选副本用户名
137
- * @returns {() => void} 取消订阅的 dispose
138
- */
139
- export function ensureNodeScope(options = {}) {
140
- if (options.replicaUsername != null)
141
- nodeScopeContext.replicaUsername = String(options.replicaUsername)
142
- if (nodeScopeSubscribeCleanup) return nodeScopeSubscribeCleanup
143
- nodeScopeSubscribeCleanup = subscribeScope('node', (senderNodeHash, envelope) => {
144
- const handlers = nodeActionHandlers.get(envelope.action)
145
- if (!handlers?.size) return
146
- for (const handler of handlers)
147
- try { handler(envelope.payload, senderNodeHash) } catch { /* ignore */ }
148
- })
149
- if (!nodeScopeWire) {
150
- nodeScopeWire = createNodeScopeWire()
151
- for (const hook of nodeScopeWireHooks)
152
- try { hook(nodeScopeContext, nodeScopeWire) } catch { /* ignore */ }
153
- }
154
- return nodeScopeSubscribeCleanup
155
- }
156
-
157
- /**
158
- * @param {(context: NodeScopeContext, wire: NodeScopeWire) => void} hook - wire 创建时回调
159
- * @returns {() => void} 取消注册的 dispose
160
- */
161
- export function registerNodeScopeWireHook(hook) {
162
- nodeScopeWireHooks.add(hook)
163
- if (nodeScopeWire)
164
- try { hook(nodeScopeContext, nodeScopeWire) } catch { /* ignore */ }
165
- return () => nodeScopeWireHooks.delete(hook)
166
- }
167
-
168
- /**
169
- * @param {() => void} dispose - feature 卸载函数
170
- * @returns {() => void} 包装后的 dispose(同时从跟踪集移除)
171
- */
172
- function trackFeatureDisposer(dispose) {
173
- nodeScopeFeatureDisposers.add(dispose)
174
- return () => {
175
- dispose()
176
- nodeScopeFeatureDisposers.delete(dispose)
177
- }
178
- }
179
-
180
- /**
181
- * 同一 feature 多次 attach 共享一份 wire;dispose 引用计数归零才卸。
182
- * @param {string} key - feature 去重键
183
- * @param {() => () => void} attachCore - 首次 attach 时执行,返回核心 dispose
184
- * @returns {() => void} 引用计数包装的 dispose
185
- */
186
- function attachFeatureRefCounted(key, attachCore) {
187
- let entry = featureAttachRefs.get(key)
188
- if (!entry) {
189
- entry = { count: 0, disposeCore: attachCore() }
190
- featureAttachRefs.set(key, entry)
191
- }
192
- entry.count++
193
- return trackFeatureDisposer(() => {
194
- const cur = featureAttachRefs.get(key)
195
- if (!cur) return
196
- cur.count--
197
- if (cur.count > 0) return
198
- cur.disposeCore()
199
- featureAttachRefs.delete(key)
200
- })
201
- }
202
-
203
- /**
204
- * 自定义 node scope feature(与 mailbox/part 相同 refcount 语义)。
205
- * @param {string} key - feature 去重键
206
- * @param {(wire: NodeScopeWire, context: NodeScopeContext) => () => void} attachCore - 首次 attach 注册 handler,返回核心 dispose
207
- * @param {{ replicaUsername?: string }} [options] - 可选副本用户名
208
- * @returns {() => void} 引用计数包装的 dispose
209
- */
210
- export function attachNodeScopeFeature(key, attachCore, options = {}) {
211
- ensureNodeScope(options)
212
- return attachFeatureRefCounted(key, () => attachCore(nodeScopeWire, nodeScopeContext))
213
- }
214
-
215
- /**
216
- * refcount 挂载 mailbox wire。
217
- * @param {{ replicaUsername?: string }} [options] - 可选副本用户名
218
- * @returns {() => void} 取消挂载的 dispose
219
- */
220
- export function attachNodeScopeMailbox(options = {}) {
221
- ensureNodeScope(options)
222
- return attachFeatureRefCounted('mailbox', () => attachMailboxWire(nodeScopeContext, nodeScopeWire))
223
- }
224
-
225
- /**
226
- * refcount 挂载 part ingress wire。
227
- * @param {{ replicaUsername?: string }} [options] - 可选副本用户名
228
- * @returns {() => void} 取消挂载的 dispose
229
- */
230
- export function attachNodeScopePart(options = {}) {
231
- ensureNodeScope(options)
232
- return attachFeatureRefCounted('part', () => attachPartWire(nodeScopeContext, nodeScopeWire))
233
- }
234
-
235
- /**
236
- * refcount 挂载 part_query wire。
237
- * @param {{ replicaUsername?: string }} [options] - 可选副本用户名
238
- * @returns {() => void} 取消挂载的 dispose
239
- */
240
- export function attachNodeScopePartQuery(options = {}) {
241
- ensureNodeScope(options)
242
- return attachFeatureRefCounted('partQuery', () => attachPartQueryWire(nodeScopeContext, nodeScopeWire))
243
- }
244
-
245
- /**
246
- * refcount 挂载 fed chunk responder。
247
- * @param {{ replicaUsername?: string }} [options] - 可选副本用户名
248
- * @returns {() => void} 取消挂载的 dispose
249
- */
250
- export function attachNodeScopeChunks(options = {}) {
251
- ensureNodeScope(options)
252
- return attachFeatureRefCounted('chunks', () =>
253
- attachNodeScopeFedChunkResponder(() => nodeScopeContext.replicaUsername, nodeScopeWire))
254
- }
255
-
256
- /**
257
- * 全业务 preset(等价旧 ensureNodeScopeRuntime 默认行为)。
258
- * @param {{ replicaUsername?: string }} [options] - 可选副本用户名
259
- * @returns {() => void} 取消全部 preset 挂载的 dispose
260
- */
261
- export function attachUserRoomDefaultWires(options = {}) {
262
- const disposers = [
263
- attachNodeScopePart(options),
264
- attachNodeScopePartQuery(options),
265
- attachNodeScopeMailbox(options),
266
- attachNodeScopeChunks(options),
267
- ]
268
- return () => {
269
- for (const dispose of disposers) dispose()
270
- }
271
- }
272
-
273
- /**
274
- * 卸掉 feature 挂载;可选保留 node scope 订阅。
275
- * @param {{ keepSubscribe?: boolean }} [options] - true 时保留 node scope 订阅
276
- * @returns {void}
277
- */
278
- export function stopNodeScopeRuntime(options = {}) {
279
- for (const dispose of [...nodeScopeFeatureDisposers]) dispose()
280
- nodeScopeFeatureDisposers.clear()
281
- for (const entry of featureAttachRefs.values())
282
- try { entry.disposeCore() } catch { /* ignore */ }
283
- featureAttachRefs.clear()
284
- if (options.keepSubscribe) return
285
- nodeScopeSubscribeCleanup?.()
286
- nodeScopeSubscribeCleanup = null
287
- nodeScopeWire = null
288
- nodeActionHandlers.clear()
289
- }
@@ -1,43 +0,0 @@
1
- import { isPlainObject } from './ingress.mjs'
2
- import { attachPartWire } from './part_ingress.mjs'
3
-
4
- /**
5
- * @param {unknown} data part_invoke 载荷
6
- * @param {string} groupId 群 ID
7
- * @returns {object | null} 校验通过后的载荷
8
- */
9
- function assertGroupContext(data, groupId) {
10
- if (!isPlainObject(data)) return null
11
- if (data.groupId !== groupId) return null
12
- return data
13
- }
14
-
15
- /**
16
- * @param {import('./part_ingress.mjs').PartWireAdapter} wire 底层适配器
17
- * @param {string} groupId 群 ID
18
- * @returns {import('./part_ingress.mjs').PartWireAdapter['on']} 注入 groupId 的 on 包装
19
- */
20
- function wrapWireOn(wire, groupId) {
21
- return (name, handler) => {
22
- wire.on(name, (data, peerId) => {
23
- const payload = assertGroupContext(data, groupId)
24
- if (!payload) return
25
- handler(payload, peerId)
26
- })
27
- }
28
- }
29
-
30
- /**
31
- * 群联邦房间挂载 part_wire(要求线载荷带 `groupId`)。
32
- * @param {{ replicaUsername?: string }} wireContext 入站上下文
33
- * @param {string} groupId 群 ID
34
- * @param {import('./part_ingress.mjs').PartWireAdapter} wire Trystero 适配器
35
- * @param {{ allowPartInvoke?: (payload: object) => boolean }} [options] 入站过滤
36
- * @returns {void}
37
- */
38
- export function attachGroupPartWire(wireContext, groupId, wire, options = {}) {
39
- attachPartWire(wireContext, {
40
- send: wire.send.bind(wire),
41
- on: wrapWireOn(wire, groupId),
42
- }, options)
43
- }
@@ -1,55 +0,0 @@
1
- import { unwrapPartInvokeResult } from './part_invoke.mjs'
2
-
3
- /** 时间线 part_timeline_put fanout 上限 */
4
- export const TIMELINE_FANOUT_LIMIT = 8
5
- /** part_invoke RPC collect 默认响应数 */
6
- export const PART_INVOKE_FANOUT_DEFAULT = 6
7
- /** User Room 随机 peer 转发默认上限 */
8
- export const USER_ROOM_PEER_FANOUT_DEFAULT = 6
9
-
10
- /**
11
- * @param {object} fields 载荷字段
12
- * @param {string} fields.partpath part 路径
13
- * @param {import('./part_invoke.mjs').PartInvoke} fields.invoke 调用体
14
- * @param {string} [fields.nodeHash] 来源节点
15
- * @param {string} [fields.requestId] RPC 请求 id
16
- * @param {string} [fields.groupId] 群上下文(mailbox give ingest)
17
- * @returns {object} part_invoke 线载荷
18
- */
19
- export function buildPartInvokePayload({ partpath, invoke, nodeHash, requestId, groupId }) {
20
- return {
21
- partpath,
22
- invoke,
23
- ...nodeHash ? { nodeHash } : {},
24
- ...requestId ? { requestId } : {},
25
- ...groupId ? { groupId } : {},
26
- }
27
- }
28
-
29
- /**
30
- * @param {import('./part_invoke.mjs').PartInvokeResponse[]} results collect 原始结果
31
- * @returns {object[]} 仅含成功 result 的载荷
32
- */
33
- export function partInvokeDataRows(results) {
34
- /** @type {object[]} */
35
- const rows = []
36
- for (const row of results) {
37
- const data = unwrapPartInvokeResult(row)
38
- if (data != null) rows.push(/** @type {object} */ data)
39
- }
40
- return rows
41
- }
42
-
43
- /**
44
- * @param {import('./part_invoke.mjs').PartInvokeResponse[]} results collect 原始结果
45
- * @returns {string[]} 邻居返回的错误信息
46
- */
47
- export function partInvokeErrorMessages(results) {
48
- /** @type {string[]} */
49
- const errors = []
50
- for (const row of results) {
51
- const message = row?.error?.message
52
- if (message) errors.push(message)
53
- }
54
- return errors
55
- }
@@ -1,52 +0,0 @@
1
- import { randomUUID } from 'node:crypto'
2
-
3
- import { getNodeHash } from '../node/identity.mjs'
4
- import { DEFAULT_TRUST_GRAPH_OWNER, requireTrustGraphProvider } from '../trust_graph/registry.mjs'
5
-
6
- import {
7
- buildPartInvokePayload,
8
- PART_INVOKE_FANOUT_DEFAULT,
9
- } from './part_common.mjs'
10
- import { pendingPartInvoke } from './part_ingress.mjs'
11
-
12
- /** @typedef {import('./part_invoke.mjs').PartInvokeResponse} PartInvokeResponse */
13
-
14
- /**
15
- * @param {string} username 用户(trust graph fanout 上下文)
16
- * @param {string} partpath part 路径
17
- * @param {import('./part_invoke.mjs').PartInvoke} invoke 调用体
18
- * @param {number} [timeoutMs=2500] 超时
19
- * @param {number} [maxResponses=6] 最多响应数
20
- * @returns {Promise<PartInvokeResponse[]>} 邻居 PartInvokeResponse(含 error)
21
- */
22
- export async function collectPartInvokeResponses(username, partpath, invoke, timeoutMs = 2500, maxResponses = PART_INVOKE_FANOUT_DEFAULT) {
23
- const requestId = randomUUID()
24
- const nodeHash = getNodeHash()
25
- /** @type {PartInvokeResponse[]} */
26
- const responses = []
27
-
28
- const waitForResponses = new Promise(resolve => {
29
- /**
30
- * 超时或收齐响应后结束等待并清理 pending part_invoke。
31
- */
32
- const finish = () => {
33
- clearTimeout(timer)
34
- pendingPartInvoke.delete(requestId)
35
- resolve(responses)
36
- }
37
- const timer = setTimeout(finish, timeoutMs)
38
- pendingPartInvoke.set(requestId, { responses, finish, maxResponses, respondedPeers: new Set() })
39
- })
40
-
41
- const sent = await requireTrustGraphProvider(DEFAULT_TRUST_GRAPH_OWNER).fanoutToTopNodes(username, 'part_invoke', buildPartInvokePayload({
42
- partpath,
43
- invoke,
44
- nodeHash,
45
- requestId,
46
- }), maxResponses)
47
-
48
- const pending = pendingPartInvoke.get(requestId)
49
- if (pending && sent === 0) pending.finish()
50
-
51
- return waitForResponses
52
- }
File without changes