@steve02081504/fount-p2p 0.0.0

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 (154) hide show
  1. package/README.md +62 -0
  2. package/core/bytes_codec.mjs +51 -0
  3. package/core/canonical_json.mjs +21 -0
  4. package/core/composite_key.mjs +91 -0
  5. package/core/constants.mjs +53 -0
  6. package/core/entity_id.mjs +53 -0
  7. package/core/entity_id_parse.mjs +40 -0
  8. package/core/evfs_logical_path.mjs +15 -0
  9. package/core/hexIds.mjs +73 -0
  10. package/core/hlc.mjs +114 -0
  11. package/core/random_id.mjs +9 -0
  12. package/crypto/channel.mjs +101 -0
  13. package/crypto/checkpoint_sign.mjs +67 -0
  14. package/crypto/crypto.mjs +158 -0
  15. package/crypto/key.mjs +431 -0
  16. package/dag/canonicalize_row.mjs +65 -0
  17. package/dag/event_query.mjs +15 -0
  18. package/dag/index.mjs +335 -0
  19. package/dag/storage.mjs +333 -0
  20. package/dag/strip_extensions.mjs +18 -0
  21. package/dag/tunables.json +6 -0
  22. package/discovery/bt.mjs +260 -0
  23. package/discovery/index.mjs +134 -0
  24. package/discovery/mdns.mjs +138 -0
  25. package/discovery/nostr.mjs +273 -0
  26. package/entity/files/acl.mjs +38 -0
  27. package/entity/files/evfs.mjs +189 -0
  28. package/entity/files/evfs_ref.mjs +37 -0
  29. package/entity/files/manifest_acl_registry.mjs +93 -0
  30. package/entity/files/replica_host_cache.mjs +46 -0
  31. package/entity/files/url.mjs +19 -0
  32. package/entity/hosting_registry.mjs +47 -0
  33. package/entity/localized_core.mjs +141 -0
  34. package/entity/logical_entity.mjs +25 -0
  35. package/entity/logical_entity_id_registry.mjs +40 -0
  36. package/entity/node_hash.mjs +15 -0
  37. package/entity/presentation_registry.mjs +44 -0
  38. package/entity/profile.mjs +256 -0
  39. package/entity/replica.mjs +20 -0
  40. package/entity/session_snapshot_registry.mjs +38 -0
  41. package/federation/chunk_fetch_pending.mjs +128 -0
  42. package/federation/chunk_fetch_scheduler.mjs +97 -0
  43. package/federation/dag_order_cache.mjs +124 -0
  44. package/federation/dedupe_slot.mjs +33 -0
  45. package/federation/message_rate_limit.mjs +23 -0
  46. package/federation/operator_key_chain.mjs +200 -0
  47. package/federation/topo_order_memo.mjs +44 -0
  48. package/federation/volatile_streams.mjs +52 -0
  49. package/federation/want_ids.mjs +158 -0
  50. package/files/assemble.mjs +220 -0
  51. package/files/assemble_stream.mjs +129 -0
  52. package/files/chunk_fetch.mjs +113 -0
  53. package/files/chunk_fetch_verify.mjs +23 -0
  54. package/files/chunk_provider_registry.mjs +70 -0
  55. package/files/chunk_responder.mjs +79 -0
  56. package/files/chunk_store.mjs +87 -0
  57. package/files/manifest.mjs +99 -0
  58. package/files/transfer_key.mjs +87 -0
  59. package/files/transfer_key_registry.mjs +108 -0
  60. package/governance/branch.mjs +230 -0
  61. package/governance/join_pow.mjs +165 -0
  62. package/governance/owner_succession_ballot.mjs +57 -0
  63. package/governance/tunables.json +7 -0
  64. package/index.mjs +39 -0
  65. package/link/channel_mux.mjs +222 -0
  66. package/link/frame.mjs +246 -0
  67. package/link/handshake.mjs +175 -0
  68. package/link/link.mjs +617 -0
  69. package/link/rtc.mjs +109 -0
  70. package/link/sdp_fingerprint.mjs +25 -0
  71. package/mailbox/consumer_registry.mjs +71 -0
  72. package/mailbox/deliver_or_store.mjs +190 -0
  73. package/mailbox/importance.mjs +72 -0
  74. package/mailbox/parse.mjs +73 -0
  75. package/mailbox/prune.mjs +106 -0
  76. package/mailbox/rate.mjs +86 -0
  77. package/mailbox/settings.mjs +53 -0
  78. package/mailbox/store.mjs +256 -0
  79. package/mailbox/tunables.json +12 -0
  80. package/mailbox/wire.mjs +40 -0
  81. package/node/denylist.mjs +309 -0
  82. package/node/entity_store.mjs +214 -0
  83. package/node/identity.mjs +99 -0
  84. package/node/instance.mjs +115 -0
  85. package/node/network.mjs +306 -0
  86. package/node/personal_block.mjs +286 -0
  87. package/node/reputation_store.mjs +326 -0
  88. package/node/retention_policy.mjs +82 -0
  89. package/node/signaling_config.mjs +32 -0
  90. package/node/storage.mjs +30 -0
  91. package/node/storage_plugins.mjs +71 -0
  92. package/node/user_paths.mjs +10 -0
  93. package/overlay/index.mjs +191 -0
  94. package/package.json +73 -0
  95. package/permissions/bitmask.mjs +35 -0
  96. package/permissions/evaluator.mjs +90 -0
  97. package/permissions/index.mjs +12 -0
  98. package/permissions/layered.mjs +39 -0
  99. package/registries/action.mjs +94 -0
  100. package/registries/event_type.mjs +75 -0
  101. package/registries/inbound.mjs +90 -0
  102. package/registries/p2p_viewer.mjs +35 -0
  103. package/registries/part_path.mjs +35 -0
  104. package/registries/room_provider.mjs +47 -0
  105. package/reputation/engine.mjs +458 -0
  106. package/reputation/math.mjs +73 -0
  107. package/reputation/pick_score.mjs +11 -0
  108. package/reputation/relay_dedupe.mjs +22 -0
  109. package/reputation/tunables.json +36 -0
  110. package/rooms/scoped_link.mjs +185 -0
  111. package/schemas/discovery.mjs +110 -0
  112. package/schemas/federation_pull.mjs +102 -0
  113. package/schemas/mailbox.mjs +37 -0
  114. package/schemas/remote_event.mjs +17 -0
  115. package/timeline/append_core.mjs +54 -0
  116. package/timeline/materialize_runner.mjs +26 -0
  117. package/timeline/prune.mjs +39 -0
  118. package/timeline/retention.mjs +54 -0
  119. package/timeline/retention_runner.mjs +35 -0
  120. package/timeline/verify_remote.mjs +25 -0
  121. package/transport/group_link_set.mjs +324 -0
  122. package/transport/ice_servers.mjs +72 -0
  123. package/transport/link_registry.mjs +749 -0
  124. package/transport/peer_identity_maps.mjs +94 -0
  125. package/transport/peer_pool.mjs +355 -0
  126. package/transport/remote_user_room.mjs +109 -0
  127. package/transport/room_scopes.mjs +2 -0
  128. package/transport/room_wire_action.mjs +19 -0
  129. package/transport/rtc_connection_budget.mjs +180 -0
  130. package/transport/rtc_mdns_filter.mjs +118 -0
  131. package/transport/stale_peer_log.mjs +50 -0
  132. package/transport/user_room.mjs +263 -0
  133. package/trust_graph/build.mjs +99 -0
  134. package/trust_graph/cache.mjs +44 -0
  135. package/trust_graph/engine.mjs +184 -0
  136. package/trust_graph/registry.mjs +38 -0
  137. package/trust_graph/resolve.mjs +105 -0
  138. package/trust_graph/send.mjs +88 -0
  139. package/trust_graph/tunables.json +13 -0
  140. package/utils/async_mutex.mjs +69 -0
  141. package/utils/debug_log.mjs +16 -0
  142. package/utils/duration.mjs +28 -0
  143. package/utils/json_io.mjs +57 -0
  144. package/utils/lru.mjs +22 -0
  145. package/utils/map_pool.mjs +27 -0
  146. package/wire/group_part.mjs +43 -0
  147. package/wire/ingress.mjs +58 -0
  148. package/wire/part_common.mjs +55 -0
  149. package/wire/part_fanout.mjs +71 -0
  150. package/wire/part_ingress.mjs +172 -0
  151. package/wire/part_invoke.mjs +81 -0
  152. package/wire/rate_bucket.mjs +49 -0
  153. package/wire/volatile_signature.mjs +76 -0
  154. package/wire/wait.mjs +127 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * DAG / 时间线签名行入库前的共用 hex 规范化(非权限校验)。
3
+ */
4
+ import { isEntityHash128 } from '../core/entity_id.mjs'
5
+ import { assertHex64, HEX_ID_64, normalizeHex64 } from '../core/hexIds.mjs'
6
+ /**
7
+ * @param {Record<string, unknown>} obj 可变对象
8
+ * @param {string} key 字段名
9
+ */
10
+ function canonicalizeHexField(obj, key) {
11
+ if (obj[key] == null || obj[key] === '') return
12
+ const normalized = normalizeHex64(obj[key])
13
+ if (!HEX_ID_64.test(normalized))
14
+ throw new Error(`${key} must be 64 hex characters`)
15
+ obj[key] = normalized
16
+ }
17
+
18
+ /**
19
+ * @param {unknown} content 事件 content
20
+ * @param {ReadonlySet<string>} hexKeys content 内 hex64 字段名
21
+ * @param {ReadonlySet<string>} [entityHashKeys] content 内 128 位 entityHash 字段名
22
+ * @returns {object | undefined} 规范化后的 content
23
+ */
24
+ export function canonicalizeRowContent(content, hexKeys, entityHashKeys = new Set()) {
25
+ if (!content) return content
26
+ const out = { ...content }
27
+ for (const key of hexKeys)
28
+ canonicalizeHexField(out, key)
29
+ for (const key of entityHashKeys) {
30
+ if (out[key] == null || out[key] === '') continue
31
+ const entityHash = String(out[key]).toLowerCase()
32
+ if (!isEntityHash128(entityHash))
33
+ throw new Error(`${key} must be 128 hex characters`)
34
+ out[key] = entityHash
35
+ }
36
+ if (out.content_ref) {
37
+ const ref = { ...out.content_ref }
38
+ canonicalizeHexField(ref, 'contentHash')
39
+ out.content_ref = ref
40
+ }
41
+ return out
42
+ }
43
+
44
+ /**
45
+ * @param {object} event 签名事件
46
+ * @param {{
47
+ * prepare?: (event: object) => object,
48
+ * contentHexKeys?: ReadonlySet<string>,
49
+ * entityHashKeys?: ReadonlySet<string>,
50
+ * }} [opts] 各域字段集
51
+ * @returns {object} canonical 行
52
+ */
53
+ export function canonicalizeSignedRow(event, opts = {}) {
54
+ const out = opts.prepare ? opts.prepare({ ...event }) : { ...event }
55
+ out.id = assertHex64(out.id, 'id')
56
+ out.sender = assertHex64(out.sender, 'sender')
57
+ if (Array.isArray(out.prev_event_ids))
58
+ out.prev_event_ids = out.prev_event_ids.map((id, index) =>
59
+ assertHex64(id, `prev_event_ids[${index}]`),
60
+ )
61
+ const hexKeys = opts.contentHexKeys
62
+ if (out.content && hexKeys?.size)
63
+ out.content = canonicalizeRowContent(out.content, hexKeys, opts.entityHashKeys)
64
+ return out
65
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * DAG 事件列表查询辅助。
3
+ */
4
+
5
+ /**
6
+ * @param {object[]} events 事件列表
7
+ * @param {string} type 事件类型
8
+ * @returns {{ event: object, index: number } | null} 自后向前最近一条
9
+ */
10
+ export function findLastEventOfType(events, type) {
11
+ for (let index = events.length - 1; index >= 0; index--)
12
+ if (events[index]?.type === type) return { event: events[index], index }
13
+
14
+ return null
15
+ }
package/dag/index.mjs ADDED
@@ -0,0 +1,335 @@
1
+ import { Buffer } from 'node:buffer'
2
+ import { createHash } from 'node:crypto'
3
+
4
+
5
+ import { canonicalStringify } from '../core/canonical_json.mjs'
6
+ import { HEX_ID_64 } from '../core/hexIds.mjs'
7
+ import { HLC } from '../core/hlc.mjs'
8
+ import { sign, verify } from '../crypto/crypto.mjs'
9
+
10
+ /**
11
+ * 重导出 64 位十六进制事件 ID 正则(`HEX_ID_64` 别名)。
12
+ */
13
+ export { HEX_ID_64 as EVENT_ID_HEX }
14
+ const EVENT_ID_HEX = HEX_ID_64
15
+ const HEX_PAIR = /^[\da-f]{2}$/iu
16
+
17
+ /**
18
+ * 对事件 id 列表做二叉 Merkle 根(字典序叶子,§7 checkpoint)。
19
+ * @param {string[]} ids 事件 id
20
+ * @returns {string} 64 位十六进制根
21
+ */
22
+ export function merkleRoot(ids) {
23
+ const sorted = [...new Set(ids.filter(id => EVENT_ID_HEX.test(String(id))))].sort()
24
+ if (!sorted.length)
25
+ return createHash('sha256').update('', 'utf8').digest('hex')
26
+ /** @type {Buffer[]} */
27
+ let level = sorted.map(id => createHash('sha256').update(id, 'utf8').digest())
28
+ while (level.length > 1) {
29
+ /** @type {Buffer[]} */
30
+ const next = []
31
+ for (let index = 0; index < level.length; index += 2) {
32
+ const left = level[index]
33
+ const right = index + 1 < level.length ? level[index + 1] : left
34
+ next.push(createHash('sha256').update(Buffer.concat([left, right])).digest())
35
+ }
36
+ level = next
37
+ }
38
+ return Buffer.from(level[0]).toString('hex')
39
+ }
40
+
41
+ /**
42
+ * 本节点可见 DAG 叶集合摘要(§7.1 `local_tips_hash`)。
43
+ * @param {string[]} tipIds 叶事件 id
44
+ * @returns {string} SHA-256 十六进制
45
+ */
46
+ export function computeLocalTipsHash(tipIds) {
47
+ const sorted = [...new Set(tipIds.filter(id => EVENT_ID_HEX.test(String(id))))].sort()
48
+ return createHash('sha256').update(sorted.join(','), 'utf8').digest('hex')
49
+ }
50
+
51
+ /**
52
+ * 规范化父 id 列表:去重、去空、字典序(§6 验签域)。
53
+ * @param {unknown} raw 原始 prev_event_ids 字段
54
+ * @returns {string[]} 去重并字典序排序后的父事件 id 数组
55
+ */
56
+ export function sortedPrevEventIds(raw) {
57
+ if (!Array.isArray(raw)) return []
58
+ return [...new Set(raw.filter(id => EVENT_ID_HEX.test(String(id))))].sort()
59
+ }
60
+
61
+ /**
62
+ * 移除对象中值为 `undefined` 的键(浅拷贝)。
63
+ * @param {object} object 输入对象
64
+ * @returns {object} 不含 undefined 值的浅拷贝
65
+ */
66
+ function stripUndefined(object) {
67
+ /** @type {Record<string, unknown>} */
68
+ const out = {}
69
+ for (const [key, value] of Object.entries(object))
70
+ if (value !== undefined) out[key] = value
71
+ return out
72
+ }
73
+
74
+ /**
75
+ * 从事件中取出参与 ID 计算与签名的字段(不含 id、signature、received_at 等)。
76
+ * @param {object} event 完整事件对象
77
+ * @returns {object} 用于 canonical 序列化与验签的正文子集
78
+ */
79
+ export function eventBodyForSign(event) {
80
+ const body = {
81
+ type: event.type,
82
+ groupId: event.groupId,
83
+ channelId: event.channelId ?? null,
84
+ sender: event.sender,
85
+ charId: event.charId ?? null,
86
+ timestamp: event.timestamp,
87
+ hlc: event.hlc,
88
+ prev_event_ids: sortedPrevEventIds(event.prev_event_ids),
89
+ content: event.content,
90
+ }
91
+ if (event.node_id != null) body.node_id = event.node_id
92
+ return stripUndefined(body)
93
+ }
94
+
95
+ /**
96
+ * 事件 ID = SHA256(canonical unsigned body) 十六进制
97
+ * @param {object} body `eventBodyForSign` 或同形对象
98
+ * @returns {string} SHA256 十六进制事件 id
99
+ */
100
+ export function computeEventId(body) {
101
+ const normalized = { ...body }
102
+ if ('prev_event_ids' in normalized)
103
+ normalized.prev_event_ids = sortedPrevEventIds(normalized.prev_event_ids)
104
+ return createHash('sha256').update(canonicalStringify(stripUndefined(normalized)), 'utf8').digest('hex')
105
+ }
106
+
107
+ /**
108
+ * 验签 / 哈希用的 UTF-8 字节序列
109
+ * @param {object} body 与 `computeEventId` 同形的正文对象
110
+ * @returns {import('node:buffer').Buffer} canonical JSON 的 UTF-8 字节
111
+ */
112
+ export function signPayloadBytes(body) {
113
+ return Buffer.from(canonicalStringify(stripUndefined({
114
+ ...body,
115
+ prev_event_ids: sortedPrevEventIds(body.prev_event_ids),
116
+ })), 'utf8')
117
+ }
118
+
119
+ /**
120
+ * 最小堆(按 compare 取最小元素)。
121
+ * @template T
122
+ */
123
+ class MinHeap {
124
+ /** @type {T[]} */
125
+ #data = []
126
+ /** @type {(left: T, right: T) => number} */
127
+ #compare
128
+
129
+ /** @param {(left: T, right: T) => number} compare 比较函数 */
130
+ constructor(compare) {
131
+ this.#compare = compare
132
+ }
133
+
134
+ /** @returns {number} 元素个数 */
135
+ get size() {
136
+ return this.#data.length
137
+ }
138
+
139
+ /** @param {T} value 入堆 */
140
+ push(value) {
141
+ const data = this.#data
142
+ data.push(value)
143
+ let index = data.length - 1
144
+ while (index > 0) {
145
+ const parent = (index - 1) >> 1
146
+ if (this.#compare(data[index], data[parent]) >= 0) break
147
+ ;[data[index], data[parent]] = [data[parent], data[index]]
148
+ index = parent
149
+ }
150
+ }
151
+
152
+ /** @returns {T | undefined} 弹出最小元素 */
153
+ pop() {
154
+ const data = this.#data
155
+ if (!data.length) return undefined
156
+ const top = data[0]
157
+ const last = data.pop()
158
+ if (data.length && last !== undefined) {
159
+ data[0] = last
160
+ let index = 0
161
+ for (; ;) {
162
+ const left = index * 2 + 1
163
+ const right = left + 1
164
+ let smallest = index
165
+ if (left < data.length && this.#compare(data[left], data[smallest]) < 0)
166
+ smallest = left
167
+ if (right < data.length && this.#compare(data[right], data[smallest]) < 0)
168
+ smallest = right
169
+ if (smallest === index) break
170
+ ;[data[index], data[smallest]] = [data[smallest], data[index]]
171
+ index = smallest
172
+ }
173
+ }
174
+ return top
175
+ }
176
+ }
177
+
178
+ /**
179
+ * 规范拓扑序(Kahn + tiebreaker:hlc.wall, hlc.logical, node_id, id;与分布式群聊规范 §4 一致)。
180
+ * @param {Array<{ id: string, prev_event_ids?: unknown, hlc?: { wall: number, logical: number }, sender?: string, node_id?: string }>} metas 事件元数据列表
181
+ * @returns {string[]} 按规范顺序排列的事件 id 列表
182
+ */
183
+ export function topologicalCanonicalOrder(metas) {
184
+ if (!metas.length) return []
185
+ const byId = new Map(metas.map(meta => [meta.id, meta]))
186
+ const parentCount = new Map()
187
+ /** @type {Map<string, string[]>} */
188
+ const children = new Map()
189
+ for (const meta of metas) {
190
+ const parentsInGraph = sortedPrevEventIds(meta.prev_event_ids).filter(parentId => byId.has(parentId))
191
+ parentCount.set(meta.id, parentsInGraph.length)
192
+ for (const parentId of parentsInGraph) {
193
+ const list = children.get(parentId)
194
+ if (list) list.push(meta.id)
195
+ else children.set(parentId, [meta.id])
196
+ }
197
+ }
198
+
199
+ /**
200
+ * @param {string} eventId 事件 id
201
+ * @returns {{ wall: number, logical: number }} wall 与 logical 分量
202
+ */
203
+ const hlcOf = eventId => {
204
+ const hlc = byId.get(eventId)?.hlc
205
+ return { wall: Number(hlc?.wall) || 0, logical: Number(hlc?.logical) || 0 }
206
+ }
207
+
208
+ /**
209
+ * @param {string} leftId 事件 id
210
+ * @param {string} rightId 事件 id
211
+ * @returns {number} 比较结果
212
+ */
213
+ const compareIds = (leftId, rightId) => {
214
+ const leftHlc = hlcOf(leftId)
215
+ const rightHlc = hlcOf(rightId)
216
+ if (leftHlc.wall !== rightHlc.wall) return leftHlc.wall - rightHlc.wall
217
+ if (leftHlc.logical !== rightHlc.logical) return leftHlc.logical - rightHlc.logical
218
+ const leftNode = String(byId.get(leftId)?.node_id || '')
219
+ const rightNode = String(byId.get(rightId)?.node_id || '')
220
+ if (leftNode !== rightNode) return leftNode.localeCompare(rightNode)
221
+ return leftId.localeCompare(rightId)
222
+ }
223
+
224
+ const ordered = []
225
+ const ready = new MinHeap(compareIds)
226
+ for (const meta of metas)
227
+ if (parentCount.get(meta.id) === 0) ready.push(meta.id)
228
+
229
+
230
+ while (ready.size) {
231
+ const next = ready.pop()
232
+ if (next == null) break
233
+ ordered.push(next)
234
+ for (const childId of children.get(next) || []) {
235
+ const remaining = (parentCount.get(childId) || 0) - 1
236
+ parentCount.set(childId, remaining)
237
+ if (remaining === 0) ready.push(childId)
238
+ }
239
+ }
240
+
241
+ return ordered
242
+ }
243
+
244
+ /**
245
+ * @param {string} hex 十六进制串
246
+ * @param {number} expectedByteLength 期望字节长度
247
+ * @returns {Uint8Array | null} 解析后的字节
248
+ */
249
+ function parseHexBytes(hex, expectedByteLength) {
250
+ if (!hex || hex.length !== expectedByteLength * 2 || hex.length % 2 !== 0) return null
251
+ const bytes = hex.match(/.{2}/g)
252
+ if (!bytes?.every(pair => HEX_PAIR.test(pair))) return null
253
+ return new Uint8Array(bytes.map(byte => Number.parseInt(byte, 16)))
254
+ }
255
+
256
+ /**
257
+ * 签名事件(签名为不含 id 的 canonical body)
258
+ * @param {object} event 含完整字段;可含占位 id
259
+ * @param {Uint8Array} privateKey 私钥
260
+ * @returns {Promise<string>} 十六进制签名
261
+ */
262
+ export async function signEvent(event, privateKey) {
263
+ const body = eventBodyForSign(event)
264
+ const signature = await sign(signPayloadBytes(body), privateKey)
265
+ return Array.from(signature).map(byte => byte.toString(16).padStart(2, '0')).join('')
266
+ }
267
+
268
+ /**
269
+ * 验证事件签名
270
+ * @param {object} event 含 signature、sender 与正文字段的完整事件
271
+ * @returns {Promise<boolean>} 验签是否通过
272
+ */
273
+ export async function verifyEventSignature(event) {
274
+ try {
275
+ const signature = parseHexBytes(event?.signature, 64)
276
+ const publicKey = parseHexBytes(event?.sender, 32)
277
+ if (!signature || !publicKey) return false
278
+ return await verify(signature, signPayloadBytes(eventBodyForSign(event)), publicKey)
279
+ }
280
+ catch (error) {
281
+ console.error('Signature verification failed:', error)
282
+ return false
283
+ }
284
+ }
285
+
286
+ /**
287
+ * 创建新事件
288
+ * @param {object} params 事件字段与签名私钥等构造参数
289
+ * @param {string[]} [params.prev_event_ids] 父事件 id;根事件 `[]`
290
+ * @returns {Promise<object>} 含 id、signature 的完整事件对象
291
+ */
292
+ export async function createEvent(params) {
293
+ const {
294
+ type, groupId, channelId, sender, charId, content, prev_event_ids, privateKey, hlc,
295
+ } = params
296
+
297
+ const event = {
298
+ type,
299
+ groupId,
300
+ channelId: channelId || null,
301
+ sender,
302
+ charId: charId || null,
303
+ timestamp: Date.now(),
304
+ hlc: hlc || HLC.now(),
305
+ prev_event_ids: sortedPrevEventIds(prev_event_ids),
306
+ content,
307
+ }
308
+
309
+ event.id = computeEventId(eventBodyForSign(event))
310
+ event.signature = await signEvent(event, privateKey)
311
+ return event
312
+ }
313
+
314
+ /**
315
+ * @param {object[]} events DAG 事件行
316
+ * @returns {Array<{ id: string, prev_event_ids?: unknown, hlc?: object, node_id?: string, sender?: string }>} 拓扑 meta 列表
317
+ */
318
+ export function eventsToMetas(events) {
319
+ return events.map(event => ({
320
+ id: event.id,
321
+ prev_event_ids: event.prev_event_ids,
322
+ hlc: event.hlc,
323
+ node_id: event.node_id,
324
+ sender: event.sender,
325
+ }))
326
+ }
327
+
328
+ /**
329
+ *
330
+ */
331
+ export { canonicalizeRowContent, canonicalizeSignedRow } from './canonicalize_row.mjs'
332
+ /**
333
+ *
334
+ */
335
+ export { stripDagEventLocalExtensions } from './strip_extensions.mjs'