@dxos/messaging 0.9.1-staging.ee54ba693a → 0.11.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 (88) hide show
  1. package/dist/lib/chunk-memory-signal-manager.mjs +652 -0
  2. package/dist/lib/chunk-memory-signal-manager.mjs.map +1 -0
  3. package/dist/lib/index.mjs +377 -0
  4. package/dist/lib/index.mjs.map +1 -0
  5. package/dist/lib/testing.mjs +288 -0
  6. package/dist/lib/testing.mjs.map +1 -0
  7. package/dist/types/src/index.d.ts +0 -1
  8. package/dist/types/src/index.d.ts.map +1 -1
  9. package/dist/types/src/messenger.blueprint-test.d.ts.map +1 -1
  10. package/dist/types/src/messenger.d.ts +1 -1
  11. package/dist/types/src/messenger.d.ts.map +1 -1
  12. package/dist/types/src/signal-manager/edge-signal-manager.d.ts +21 -4
  13. package/dist/types/src/signal-manager/edge-signal-manager.d.ts.map +1 -1
  14. package/dist/types/src/signal-manager/edge-signal-manager.test.d.ts +2 -0
  15. package/dist/types/src/signal-manager/edge-signal-manager.test.d.ts.map +1 -0
  16. package/dist/types/src/signal-manager/index.d.ts +1 -2
  17. package/dist/types/src/signal-manager/index.d.ts.map +1 -1
  18. package/dist/types/src/signal-manager/memory-signal-manager.d.ts +12 -10
  19. package/dist/types/src/signal-manager/memory-signal-manager.d.ts.map +1 -1
  20. package/dist/types/src/signal-manager/memory-signal-manager.test.d.ts +2 -0
  21. package/dist/types/src/signal-manager/memory-signal-manager.test.d.ts.map +1 -0
  22. package/dist/types/src/signal-manager/signal-manager.d.ts +5 -0
  23. package/dist/types/src/signal-manager/signal-manager.d.ts.map +1 -1
  24. package/dist/types/src/signal-manager/utils.d.ts +6 -4
  25. package/dist/types/src/signal-manager/utils.d.ts.map +1 -1
  26. package/dist/types/src/signal-methods.d.ts +33 -22
  27. package/dist/types/src/signal-methods.d.ts.map +1 -1
  28. package/dist/types/src/testing/index.d.ts +1 -0
  29. package/dist/types/src/testing/index.d.ts.map +1 -1
  30. package/dist/types/src/testing/test-edge-mesh.d.ts +61 -0
  31. package/dist/types/src/testing/test-edge-mesh.d.ts.map +1 -0
  32. package/dist/types/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +19 -19
  34. package/src/index.ts +0 -1
  35. package/src/messenger.blueprint-test.ts +0 -25
  36. package/src/messenger.node.test.ts +1 -1
  37. package/src/messenger.ts +42 -18
  38. package/src/signal-manager/edge-signal-manager.test.ts +193 -0
  39. package/src/signal-manager/edge-signal-manager.ts +145 -12
  40. package/src/signal-manager/index.ts +1 -2
  41. package/src/signal-manager/memory-signal-manager.test.ts +199 -0
  42. package/src/signal-manager/memory-signal-manager.ts +87 -47
  43. package/src/signal-manager/signal-manager.ts +7 -0
  44. package/src/signal-manager/utils.ts +30 -21
  45. package/src/signal-methods.ts +37 -26
  46. package/src/testing/index.ts +1 -0
  47. package/src/testing/test-edge-mesh.ts +196 -0
  48. package/src/testing/utils.ts +2 -2
  49. package/dist/lib/neutral/chunk-EC4H56J5.mjs +0 -497
  50. package/dist/lib/neutral/chunk-EC4H56J5.mjs.map +0 -7
  51. package/dist/lib/neutral/index.mjs +0 -1249
  52. package/dist/lib/neutral/index.mjs.map +0 -7
  53. package/dist/lib/neutral/meta.json +0 -1
  54. package/dist/lib/neutral/testing/index.mjs +0 -155
  55. package/dist/lib/neutral/testing/index.mjs.map +0 -7
  56. package/dist/types/src/signal-client/index.d.ts +0 -2
  57. package/dist/types/src/signal-client/index.d.ts.map +0 -1
  58. package/dist/types/src/signal-client/signal-client-monitor.d.ts +0 -30
  59. package/dist/types/src/signal-client/signal-client-monitor.d.ts.map +0 -1
  60. package/dist/types/src/signal-client/signal-client.d.ts +0 -59
  61. package/dist/types/src/signal-client/signal-client.d.ts.map +0 -1
  62. package/dist/types/src/signal-client/signal-client.node.test.d.ts +0 -2
  63. package/dist/types/src/signal-client/signal-client.node.test.d.ts.map +0 -1
  64. package/dist/types/src/signal-client/signal-local-state.d.ts +0 -38
  65. package/dist/types/src/signal-client/signal-local-state.d.ts.map +0 -1
  66. package/dist/types/src/signal-client/signal-rpc-client-monitor.d.ts +0 -6
  67. package/dist/types/src/signal-client/signal-rpc-client-monitor.d.ts.map +0 -1
  68. package/dist/types/src/signal-client/signal-rpc-client.d.ts +0 -47
  69. package/dist/types/src/signal-client/signal-rpc-client.d.ts.map +0 -1
  70. package/dist/types/src/signal-client/signal-rpc-client.node.test.d.ts +0 -2
  71. package/dist/types/src/signal-client/signal-rpc-client.node.test.d.ts.map +0 -1
  72. package/dist/types/src/signal-manager/websocket-signal-manager-monitor.d.ts +0 -8
  73. package/dist/types/src/signal-manager/websocket-signal-manager-monitor.d.ts.map +0 -1
  74. package/dist/types/src/signal-manager/websocket-signal-manager.d.ts +0 -41
  75. package/dist/types/src/signal-manager/websocket-signal-manager.d.ts.map +0 -1
  76. package/dist/types/src/signal-manager/websocket-signal-manager.node.test.d.ts +0 -2
  77. package/dist/types/src/signal-manager/websocket-signal-manager.node.test.d.ts.map +0 -1
  78. package/src/signal-client/index.ts +0 -5
  79. package/src/signal-client/signal-client-monitor.ts +0 -111
  80. package/src/signal-client/signal-client.node.test.ts +0 -185
  81. package/src/signal-client/signal-client.ts +0 -339
  82. package/src/signal-client/signal-local-state.ts +0 -174
  83. package/src/signal-client/signal-rpc-client-monitor.ts +0 -15
  84. package/src/signal-client/signal-rpc-client.node.test.ts +0 -102
  85. package/src/signal-client/signal-rpc-client.ts +0 -227
  86. package/src/signal-manager/websocket-signal-manager-monitor.ts +0 -20
  87. package/src/signal-manager/websocket-signal-manager.node.test.ts +0 -112
  88. package/src/signal-manager/websocket-signal-manager.ts +0 -188
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-memory-signal-manager.mjs","names":[],"sources":["../../src/messenger-monitor.ts","../../src/timeouts.ts","../../src/messenger.ts","../../src/signal-methods.ts","../../src/signal-manager/memory-signal-manager.ts"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\nimport { trace } from '@dxos/tracing';\n\nexport class MessengerMonitor {\n public recordMessageAckFailed(): void {\n trace.metrics.increment('dxos.mesh.signal.messenger.failed-ack', 1);\n }\n\n public recordReliableMessage(params: { sendAttempts: number; sent: boolean }): void {\n trace.metrics.increment('dxos.mesh.signal.messenger.reliable-send', 1, {\n tags: {\n success: params.sent,\n attempts: params.sendAttempts,\n },\n });\n }\n}\n","//\n// Copyright 2023 DXOS.org\n//\n\n/**\n * Timeout for retrying messages.\n */\nexport const MESSAGE_TIMEOUT = 10_000;\n","//\n// Copyright 2022 DXOS.org\n//\n\nimport { TimeoutError, scheduleExponentialBackoffTaskInterval, scheduleTask, scheduleTaskInterval } from '@dxos/async';\nimport { type Any } from '@dxos/codec-protobuf';\nimport { Context } from '@dxos/context';\nimport { invariant } from '@dxos/invariant';\nimport { PublicKey } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { TimeoutError as ProtocolTimeoutError } from '@dxos/protocols';\nimport { schema } from '@dxos/protocols/proto';\nimport { type ReliablePayload } from '@dxos/protocols/proto/dxos/mesh/messaging';\nimport { ComplexMap, ComplexSet } from '@dxos/util';\n\nimport { MessengerMonitor } from './messenger-monitor';\nimport { type SignalManager } from './signal-manager';\nimport { type Message, type PeerInfo } from './signal-methods';\nimport { MESSAGE_TIMEOUT } from './timeouts';\n\nexport type OnMessage = (params: Message) => Promise<void>;\n\nexport interface MessengerOptions {\n signalManager: SignalManager;\n retryDelay?: number;\n}\n\nconst ReliablePayload = schema.getCodecForType('dxos.mesh.messaging.ReliablePayload');\nconst Acknowledgement = schema.getCodecForType('dxos.mesh.messaging.Acknowledgement');\n\nconst RECEIVED_MESSAGES_GC_INTERVAL = 120_000;\n\n/**\n * Reliable messenger that works trough signal network.\n */\nexport class Messenger {\n private readonly _monitor = new MessengerMonitor();\n private readonly _signalManager: SignalManager;\n // { peerId, payloadType } => listeners set\n private readonly _listeners = new ComplexMap<{ peerId: string; payloadType: string }, Set<OnMessage>>(\n ({ peerId, payloadType }) => peerId + payloadType,\n );\n\n // peerId => listeners set\n private readonly _defaultListeners = new Map<string, Set<OnMessage>>();\n\n private readonly _onAckCallbacks = new ComplexMap<PublicKey, () => void>(PublicKey.hash);\n\n private readonly _receivedMessages = new ComplexSet<PublicKey>(PublicKey.hash);\n\n /**\n * Keys scheduled to be cleared from _receivedMessages on the next iteration.\n */\n private readonly _toClear = new ComplexSet<PublicKey>(PublicKey.hash);\n\n private _ctx!: Context;\n private _closed = true;\n private readonly _retryDelay: number;\n\n constructor({ signalManager, retryDelay = 1000 }: MessengerOptions) {\n this._signalManager = signalManager;\n this._retryDelay = retryDelay;\n\n this.open();\n }\n\n open(): void {\n if (!this._closed) {\n return;\n }\n log('opening messenger');\n this._ctx = new Context({\n onError: (err) => log.catch(err),\n });\n\n // Clear the map periodically.\n scheduleTaskInterval(\n this._ctx,\n async () => {\n this._performGc();\n },\n RECEIVED_MESSAGES_GC_INTERVAL,\n );\n\n this._closed = false;\n log('opened messenger');\n }\n\n async close(): Promise<void> {\n if (this._closed) {\n return;\n }\n this._closed = true;\n // Disposing the context tears down every still-active subscription — each `listen` registers its\n // transport unsubscribe via `onDispose`, and a handle unsubscribed manually has already cleared\n // its registration. The offline/online cycle keeps the messenger open (only signaling toggles),\n // so subscriptions are torn down only on a real close.\n await this._ctx.dispose();\n }\n\n async sendMessage(ctx: Context, message: Message): Promise<void> {\n invariant(!this._closed, 'Closed');\n const { author, recipient, payload } = message;\n // Messenger provides reliable point-to-point delivery; broadcasts are not routed here.\n invariant(recipient, 'Recipient is required');\n const messageContext = this._ctx.derive();\n\n const reliablePayload: ReliablePayload = {\n messageId: PublicKey.random(),\n payload,\n };\n invariant(!this._onAckCallbacks.has(reliablePayload.messageId!));\n log('send message', { messageId: reliablePayload.messageId, author, recipient });\n\n let messageReceived: () => void;\n let timeoutHit: (err: Error) => void;\n let sendAttempts = 0;\n\n const promise = new Promise<void>((resolve, reject) => {\n messageReceived = resolve;\n timeoutHit = reject;\n });\n\n // Setting retry interval if signal was not acknowledged.\n scheduleExponentialBackoffTaskInterval(\n messageContext,\n async () => {\n log('retrying message', { messageId: reliablePayload.messageId });\n sendAttempts++;\n await this._encodeAndSend(ctx, { author, recipient, reliablePayload }).catch((err) =>\n log('failed to send message', { err }),\n );\n },\n this._retryDelay,\n );\n\n scheduleTask(\n messageContext,\n () => {\n log('message not delivered', { messageId: reliablePayload.messageId });\n this._onAckCallbacks.delete(reliablePayload.messageId!);\n timeoutHit(\n new ProtocolTimeoutError({\n message: 'signaling message not delivered',\n cause: new TimeoutError(MESSAGE_TIMEOUT, 'Message not delivered'),\n }),\n );\n void messageContext.dispose();\n this._monitor.recordReliableMessage({ sendAttempts, sent: false });\n },\n MESSAGE_TIMEOUT,\n );\n\n this._onAckCallbacks.set(reliablePayload.messageId, () => {\n messageReceived();\n this._onAckCallbacks.delete(reliablePayload.messageId!);\n void messageContext.dispose();\n this._monitor.recordReliableMessage({ sendAttempts, sent: true });\n });\n\n await this._encodeAndSend(ctx, { author, recipient, reliablePayload });\n return promise;\n }\n\n /**\n * Subscribes onMessage function to messages that contains payload with payloadType.\n * @param payloadType if not specified, onMessage will be subscribed to all types of messages.\n */\n async listen({\n peer,\n payloadType,\n onMessage,\n }: {\n peer: PeerInfo;\n payloadType?: string;\n onMessage: OnMessage;\n }): Promise<ListeningHandle> {\n invariant(!this._closed, 'Closed');\n invariant(peer.peerKey, 'Peer key is required');\n const peerKey = peer.peerKey;\n\n // Multiplexing is owned by the signal manager. The messenger keeps only reliable delivery\n // (ACK/dedup) and payloadType routing, and passes the transport unsubscribe back through the\n // handle. The unsubscribe is also registered on the messenger context so `close` tears every\n // subscription down; a handle unsubscribed manually clears that registration first.\n const unsubscribe = await this._signalManager.subscribeMessages({\n peer,\n onMessage: (message) => {\n // Subscriptions can outlive a close (they are torn down on dispose, and survive offline/online\n // cycles), so ignore late deliveries and never let a handler rejection escape as unhandled.\n if (this._closed) {\n return;\n }\n log('received message', { from: message.author });\n void this._handleMessage(message).catch((err) => log.catch(err));\n },\n });\n const clearDispose = this._ctx.onDispose(unsubscribe);\n\n let listeners: Set<OnMessage> | undefined;\n if (!payloadType) {\n listeners = this._defaultListeners.get(peerKey);\n if (!listeners) {\n listeners = new Set();\n this._defaultListeners.set(peerKey, listeners);\n }\n } else {\n listeners = this._listeners.get({ peerId: peerKey, payloadType });\n if (!listeners) {\n listeners = new Set();\n this._listeners.set({ peerId: peerKey, payloadType }, listeners);\n }\n }\n\n listeners.add(onMessage);\n\n return {\n unsubscribe: async () => {\n clearDispose();\n listeners!.delete(onMessage);\n await unsubscribe();\n },\n };\n }\n\n private async _encodeAndSend(\n ctx: Context,\n {\n author,\n recipient,\n reliablePayload,\n }: {\n author: PeerInfo;\n recipient: PeerInfo;\n reliablePayload: ReliablePayload;\n },\n ): Promise<void> {\n await this._signalManager.sendMessage(ctx, {\n author,\n recipient,\n payload: {\n type_url: 'dxos.mesh.messaging.ReliablePayload',\n value: ReliablePayload.encode(reliablePayload, { preserveAny: true }),\n },\n });\n }\n\n private async _handleMessage(message: Message): Promise<void> {\n switch (message.payload.type_url) {\n case 'dxos.mesh.messaging.ReliablePayload': {\n await this._handleReliablePayload(message);\n break;\n }\n case 'dxos.mesh.messaging.Acknowledgement': {\n await this._handleAcknowledgement({ payload: message.payload });\n break;\n }\n }\n }\n\n private async _handleReliablePayload(message: Message): Promise<void> {\n const { author, recipient, payload } = message;\n invariant(payload.type_url === 'dxos.mesh.messaging.ReliablePayload');\n invariant(recipient, 'Recipient is required');\n const reliablePayload: ReliablePayload = ReliablePayload.decode(payload.value, { preserveAny: true });\n\n log('handling message', { messageId: reliablePayload.messageId });\n\n try {\n await this._sendAcknowledgement(this._ctx, {\n author,\n recipient,\n messageId: reliablePayload.messageId,\n });\n } catch (err) {\n this._monitor.recordMessageAckFailed();\n throw err;\n }\n\n // Ignore message if it was already received, i.e. from multiple signal servers.\n if (this._receivedMessages.has(reliablePayload.messageId!)) {\n return;\n }\n\n this._receivedMessages.add(reliablePayload.messageId!);\n\n await this._callListeners({\n author,\n recipient,\n payload: reliablePayload.payload,\n });\n }\n\n private async _handleAcknowledgement({ payload }: { payload: Any }): Promise<void> {\n invariant(payload.type_url === 'dxos.mesh.messaging.Acknowledgement');\n this._onAckCallbacks.get(Acknowledgement.decode(payload.value).messageId)?.();\n }\n\n private async _sendAcknowledgement(\n ctx: Context,\n {\n author,\n recipient,\n messageId,\n }: {\n author: PeerInfo;\n recipient: PeerInfo;\n messageId: PublicKey;\n },\n ): Promise<void> {\n log('sending ACK', { messageId, from: recipient, to: author });\n\n await this._signalManager.sendMessage(ctx, {\n author: recipient,\n recipient: author,\n payload: {\n type_url: 'dxos.mesh.messaging.Acknowledgement',\n value: Acknowledgement.encode({ messageId }),\n },\n });\n }\n\n private async _callListeners(message: Message): Promise<void> {\n const { recipient } = message;\n invariant(recipient?.peerKey, 'Peer key is required');\n const peerKey = recipient.peerKey;\n {\n const defaultListenerMap = this._defaultListeners.get(peerKey);\n if (defaultListenerMap) {\n for (const listener of defaultListenerMap) {\n await listener(message);\n }\n }\n }\n\n {\n const listenerMap = this._listeners.get({\n peerId: peerKey,\n payloadType: message.payload.type_url,\n });\n if (listenerMap) {\n for (const listener of listenerMap) {\n await listener(message);\n }\n }\n }\n }\n\n private _performGc(): void {\n const start = performance.now();\n\n for (const key of this._toClear.keys()) {\n this._receivedMessages.delete(key);\n }\n this._toClear.clear();\n for (const key of this._receivedMessages.keys()) {\n this._toClear.add(key);\n }\n\n const elapsed = performance.now() - start;\n if (elapsed > 100) {\n log.warn('GC took too long', { elapsed });\n }\n }\n}\n\nexport interface ListeningHandle {\n unsubscribe: () => Promise<void>;\n}\n","//\n// Copyright 2022 DXOS.org\n//\n\nimport { type Event } from '@dxos/async';\nimport { type Context } from '@dxos/context';\nimport { type Peer, type SwarmResponse } from '@dxos/protocols/proto/dxos/edge/messenger';\nimport {\n type JoinRequest,\n type LeaveRequest,\n type Message,\n type QueryRequest,\n type SwarmEvent,\n} from '@dxos/protocols/proto/dxos/edge/signal';\nimport { type SignalState } from '@dxos/protocols/proto/dxos/mesh/signal';\n\nexport type { Message, SwarmEvent };\nexport type PeerInfo = Peer;\nexport const PeerInfoHash = ({ peerKey }: PeerInfo) => peerKey;\n\nexport type SignalStatus = {\n host: string;\n state: SignalState;\n error?: string;\n reconnectIn: number;\n connectionStarted: Date;\n lastStateChange: Date;\n};\n\n/**\n * Parameters for {@link SignalMethods.subscribeMessages}.\n */\nexport type SubscribeMessagesParams = {\n /**\n * The subscribing peer. Point-to-point messages addressed to this `peerKey` are delivered.\n */\n peer: PeerInfo;\n\n /**\n * OR-subscription tags (DX-1125). When provided, swarm broadcasts whose tags intersect this set are\n * also delivered. Only meaningful for edge signaling; other transports ignore them.\n */\n tags?: string[];\n\n /**\n * Invoked for every message delivered to this subscription (point-to-point or matching broadcast).\n */\n onMessage: (message: Message) => void;\n};\n\n/**\n * Tears down a subscription created by {@link SignalMethods.subscribeMessages} and releases its\n * transport resources (edge tag registration / receive stream). The subscriber owns this lifecycle.\n */\nexport type UnsubscribeCallback = () => Promise<void>;\n\n/**\n * Message routing interface.\n */\nexport interface SignalMethods {\n /**\n * Emits when other peers join or leave the swarm.\n * @deprecated\n * TODO(mykola): Use swarmState in network-manager instead.\n */\n swarmEvent: Event<SwarmEvent>;\n\n /**\n * Emits when the swarm state changes.\n */\n swarmState?: Event<SwarmResponse>;\n\n /**\n * Join topic on signal network, to be discoverable by other peers.\n */\n join: (ctx: Context, params: JoinRequest) => Promise<void>;\n\n /**\n * Leave topic on signal network, to stop being discoverable by other peers.\n */\n leave: (ctx: Context, params: LeaveRequest) => Promise<void>;\n\n /**\n * Query peers in the swarm without joining it.\n */\n query: (ctx: Context, params: QueryRequest) => Promise<SwarmResponse>;\n\n /**\n * Send a message. Point-to-point when `recipient` is set; a swarm broadcast (DX-1125) when `tags`\n * are set — fanned out to every peer whose tag subscription intersects, with the target swarm taken\n * from `author.swarmKey`. Exactly one of `recipient` / `tags` must be present. Broadcasts are only\n * supported by edge signaling.\n */\n sendMessage: (ctx: Context, message: Message) => Promise<void>;\n\n /**\n * Start receiving messages for `peer`: its point-to-point messages, plus — when `tags` are provided\n * (DX-1125) — swarm broadcasts whose tags intersect. Matching messages are routed to\n * `params.onMessage`; routing and the transport stream are owned here. Returns a callback that tears\n * the subscription down; the subscriber owns that lifecycle.\n */\n subscribeMessages: (params: SubscribeMessagesParams) => Promise<UnsubscribeCallback>;\n}\n","//\n// Copyright 2020 DXOS.org\n//\n\nimport { Event, Trigger } from '@dxos/async';\nimport { type Any } from '@dxos/codec-protobuf';\nimport { Context } from '@dxos/context';\nimport { invariant } from '@dxos/invariant';\nimport { PublicKey } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { schema } from '@dxos/protocols/proto';\nimport { type SwarmResponse } from '@dxos/protocols/proto/dxos/edge/messenger';\nimport { type QueryRequest } from '@dxos/protocols/proto/dxos/edge/signal';\nimport { ComplexMap, ComplexSet } from '@dxos/util';\n\nimport {\n type Message,\n type PeerInfo,\n PeerInfoHash,\n type SignalStatus,\n type SubscribeMessagesParams,\n type SwarmEvent,\n type UnsubscribeCallback,\n} from '../signal-methods';\nimport { type SignalManager } from './signal-manager';\n\n/**\n * A single message subscription registered on a {@link MemorySignalManager} (DX-1125). Point-to-point\n * delivery matches `peerKey`; broadcast delivery matches any intersection with `tags`.\n */\ntype MessageSubscription = {\n peerKey: string;\n tags: Set<string>;\n onMessage: (message: Message) => void;\n};\n\n/**\n * Common signaling context that connects multiple MemorySignalManager instances.\n */\nexport class MemorySignalManagerContext {\n // Swarm messages.\n readonly swarmEvent = new Event<SwarmEvent>();\n\n // Mapping from topic to set of peers.\n readonly swarms = new ComplexMap<PublicKey, ComplexSet<PeerInfo>>(PublicKey.hash);\n\n // Map of connections for each peer for signaling.\n readonly connections = new ComplexMap<PeerInfo, MemorySignalManager>(PeerInfoHash);\n}\n\n/**\n * In memory signal manager for testing.\n */\nexport class MemorySignalManager implements SignalManager {\n readonly statusChanged = new Event<SignalStatus[]>();\n readonly swarmEvent = new Event<SwarmEvent>();\n\n /**\n * Active message subscriptions on this manager. Routing is encapsulated here (DX-1125): a delivered\n * message is dispatched to every subscription it matches.\n */\n private readonly _subscriptions = new Set<MessageSubscription>();\n\n /** Will be used to emit SwarmEvents on .open() and .close() */\n private _joinedSwarms = new ComplexSet<{ topic: PublicKey; peer: PeerInfo }>(\n ({ topic, peer }) => topic.toHex() + peer.peerKey,\n );\n\n private _ctx!: Context;\n\n // TODO(dmaretskyi): Replace with callback.\n private readonly _freezeTrigger = new Trigger().wake();\n\n constructor(private readonly _context: MemorySignalManagerContext) {\n this._ctx = new Context();\n\n this._ctx.onDispose(this._context.swarmEvent.on((data) => this.swarmEvent.emit(data)));\n }\n\n async open(): Promise<void> {\n if (!this._ctx.disposed) {\n return;\n }\n this._ctx = new Context();\n this._ctx.onDispose(this._context.swarmEvent.on((data) => this.swarmEvent.emit(data)));\n\n await Promise.all([...this._joinedSwarms.values()].map((value) => this.join(this._ctx, value)));\n }\n\n async close(): Promise<void> {\n if (this._ctx.disposed) {\n return;\n }\n // save copy of joined swarms.\n const joinedSwarmsCopy = new ComplexSet<{ topic: PublicKey; peer: PeerInfo }>(\n ({ topic, peer }) => topic.toHex() + peer.peerKey,\n [...this._joinedSwarms.values()],\n );\n\n await Promise.all([...this._joinedSwarms.values()].map((value) => this.leave(this._ctx, value)));\n\n // assign joined swarms back because .leave() deletes it.\n this._joinedSwarms = joinedSwarmsCopy;\n\n await this._ctx.dispose();\n }\n\n getStatus(): SignalStatus[] {\n return [];\n }\n\n async join(_ctx: Context, { topic, peer }: { topic: PublicKey; peer: PeerInfo }): Promise<void> {\n invariant(!this._ctx.disposed, 'Closed');\n\n this._joinedSwarms.add({ topic, peer });\n\n if (!this._context.swarms.has(topic)) {\n this._context.swarms.set(topic, new ComplexSet(PeerInfoHash));\n }\n\n this._context.swarms.get(topic)!.add(peer);\n this._context.swarmEvent.emit({\n topic,\n peerAvailable: {\n peer,\n since: new Date(),\n },\n });\n\n // Emitting swarm events for each peer.\n for (const [topic, peers] of this._context.swarms) {\n Array.from(peers).forEach((peer) => {\n this.swarmEvent.emit({\n topic,\n peerAvailable: {\n peer,\n since: new Date(),\n },\n });\n });\n }\n }\n\n async leave(_ctx: Context, { topic, peer }: { topic: PublicKey; peer: PeerInfo }): Promise<void> {\n invariant(!this._ctx.disposed, 'Closed');\n\n this._joinedSwarms.delete({ topic, peer });\n\n if (!this._context.swarms.has(topic)) {\n this._context.swarms.set(topic, new ComplexSet(PeerInfoHash));\n }\n\n this._context.swarms.get(topic)!.delete(peer);\n\n const swarmEvent: SwarmEvent = {\n topic,\n peerLeft: {\n peer,\n },\n };\n\n this._context.swarmEvent.emit(swarmEvent);\n }\n\n async query(_ctx: Context, request: QueryRequest): Promise<SwarmResponse> {\n throw new Error('Not implemented');\n }\n\n async sendMessage(_ctx: Context, message: Message): Promise<void> {\n invariant(!this._ctx.disposed, 'Closed');\n const { author, recipient, tags, payload } = message;\n // Exactly one of point-to-point (`recipient`) or broadcast (`tags`) delivery (DX-1125).\n invariant((recipient == null) !== !tags?.length, 'Exactly one of `recipient` or `tags` must be set');\n\n await this._freezeTrigger.wait();\n\n if (recipient != null) {\n log('send message', { author, recipient, ...dec(payload) });\n const remote = this._context.connections.get(recipient);\n if (!remote) {\n log.warn('recipient is not subscribed for messages', { author, recipient });\n return;\n }\n remote._deliver(message);\n } else {\n // Broadcast: fan out to every subscriber in the shared context whose tags intersect.\n log('broadcast message', { author, tags, ...dec(payload) });\n for (const manager of new Set(this._context.connections.values())) {\n manager._deliver(message);\n }\n }\n }\n\n async subscribeMessages({ peer, tags = [], onMessage }: SubscribeMessagesParams): Promise<UnsubscribeCallback> {\n invariant(!this._ctx.disposed, 'Closed');\n log('subscribing', { peer, tags });\n const subscription: MessageSubscription = { peerKey: peer.peerKey, tags: new Set(tags), onMessage };\n this._subscriptions.add(subscription);\n this._context.connections.set(peer, this);\n\n return async () => {\n log('unsubscribing', { peer, tags });\n this._subscriptions.delete(subscription);\n // Drop the shared-context connection entry only once no subscription for this peer remains.\n if (![...this._subscriptions].some((sub) => sub.peerKey === peer.peerKey)) {\n this._context.connections.delete(peer);\n }\n };\n }\n\n freeze(): void {\n this._freezeTrigger.reset();\n }\n\n unfreeze(): void {\n this._freezeTrigger.wake();\n }\n\n /**\n * Route a delivered message to this manager's matching subscriptions once it is unfrozen and open.\n */\n private _deliver(message: Message): void {\n if (this._ctx.disposed) {\n log.warn('recipient is disposed', { message });\n return;\n }\n\n this._freezeTrigger\n .wait()\n .then(() => {\n if (this._ctx.disposed) {\n log.warn('recipient is disposed', { message });\n return;\n }\n\n log('receive message', { author: message.author, recipient: message.recipient, ...dec(message.payload) });\n for (const subscription of this._subscriptions) {\n if (message.recipient != null) {\n if (subscription.peerKey === message.recipient.peerKey) {\n subscription.onMessage(message);\n }\n } else if (message.tags?.some((tag) => subscription.tags.has(tag))) {\n subscription.onMessage(message);\n }\n }\n })\n .catch((err) => {\n log.error('error while waiting for freeze', { err });\n });\n }\n}\nconst dec = (payload: Any) => {\n if (!payload.type_url.endsWith('ReliablePayload')) {\n return {};\n }\n\n const relPayload = schema.getCodecForType('dxos.mesh.messaging.ReliablePayload').decode(payload.value);\n\n if (typeof relPayload?.payload?.data === 'object') {\n return { payload: Object.keys(relPayload?.payload?.data)[0], sessionId: relPayload?.payload?.sessionId };\n }\n\n return {};\n};\n"],"mappings":"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"}
@@ -0,0 +1,377 @@
1
+ import { i as Messenger, n as MemorySignalManagerContext, r as PeerInfoHash, t as MemorySignalManager } from "./chunk-memory-signal-manager.mjs";
2
+ import { Event, scheduleMicroTask } from "@dxos/async";
3
+ import { Resource, cancelWithContext } from "@dxos/context";
4
+ import { invariant } from "@dxos/invariant";
5
+ import { PublicKey } from "@dxos/keys";
6
+ import { log } from "@dxos/log";
7
+ import { EdgeService, subscribeStream } from "@dxos/protocols";
8
+ import { ComplexMap, ComplexSet } from "@dxos/util";
9
+ import * as Context$1 from "effect/Context";
10
+ import { EdgeIdentityChangedError, protocol } from "@dxos/edge-client";
11
+ import { bufWkt } from "@dxos/protocols/buf";
12
+ import { SwarmRequestSchema, SwarmRequest_Action, SwarmResponseSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
13
+ import * as Runtime from "effect/Runtime";
14
+ import { DeviceKind } from "@dxos/protocols/proto/dxos/client/services";
15
+ //#region src/signal-manager/signal-manager.ts
16
+ var SignalManagerService = class extends Context$1.Tag("@dxos/messaging/SignalManager")() {};
17
+ //#endregion
18
+ //#region src/signal-manager/edge-signal-manager.ts
19
+ var __dxlog_file$1 = "/__w/dxos/dxos/packages/core/mesh/messaging/src/signal-manager/edge-signal-manager.ts";
20
+ var EdgeSignalManager = class extends Resource {
21
+ /**
22
+ * @deprecated
23
+ */
24
+ swarmEvent = new Event();
25
+ swarmState = new Event();
26
+ /**
27
+ * Active message subscriptions. Routing is encapsulated here (DX-1125): each incoming message is
28
+ * dispatched to every subscription it matches, and each subscription owns its own teardown.
29
+ */
30
+ _subscriptions = /* @__PURE__ */ new Set();
31
+ /**
32
+ * Swarm key -> { peer: <own state payload>, joinedPeers: <state of swarm> }.
33
+ */
34
+ _swarmPeers = new ComplexMap(PublicKey.hash);
35
+ /**
36
+ * OR-subscription tag refcounts for broadcast messages (DX-1125). One count per distinct tag across
37
+ * all subscribers, so one consumer's unsubscribe releases only its own registration rather than
38
+ * clobbering another consumer's identical subscription. The effective tag set (the keys) is shared
39
+ * across all joined swarms; the edge fans out any broadcast whose tags intersect it. Re-sent on
40
+ * reconnect and whenever a swarm is (re-)joined.
41
+ */
42
+ _subscribedTags = /* @__PURE__ */ new Map();
43
+ _edgeConnection;
44
+ constructor({ edgeConnection }) {
45
+ super();
46
+ this._edgeConnection = edgeConnection;
47
+ }
48
+ async _open() {
49
+ this._ctx.onDispose(this._edgeConnection.onMessage((message) => this._onMessage(message)));
50
+ this._ctx.onDispose(this._edgeConnection.onReconnected(() => {
51
+ scheduleMicroTask(this._ctx, () => this._rejoinAllSwarms());
52
+ }));
53
+ }
54
+ /**
55
+ * Warning: PeerInfo is inferred from edgeConnection.
56
+ */
57
+ async join(ctx, { topic, peer }) {
58
+ if (!this._matchSelfPeerInfo(peer)) {
59
+ log.warn("ignoring peer info on join request", {
60
+ peer,
61
+ expected: {
62
+ peerKey: this._edgeConnection.peerKey,
63
+ identityDid: this._edgeConnection.identityDid
64
+ }
65
+ }, {
66
+ "~LogMeta": "~LogMeta",
67
+ F: __dxlog_file$1,
68
+ L: 100,
69
+ S: this
70
+ });
71
+ peer.identityDid = this._edgeConnection.identityDid;
72
+ peer.peerKey = this._edgeConnection.peerKey;
73
+ }
74
+ this._swarmPeers.set(topic, {
75
+ lastState: peer.state,
76
+ joinedPeers: new ComplexSet(PeerInfoHash)
77
+ });
78
+ await this._edgeConnection.send(ctx, protocol.createMessage(SwarmRequestSchema, {
79
+ serviceId: EdgeService.SWARM,
80
+ source: createMessageSource(topic, peer),
81
+ payload: {
82
+ action: SwarmRequest_Action.JOIN,
83
+ swarmKeys: [topic.toHex()]
84
+ }
85
+ }));
86
+ if (this._subscribedTags.size > 0) await this._sendSubscription(ctx);
87
+ }
88
+ async leave(ctx, { topic, peer }) {
89
+ this._swarmPeers.delete(topic);
90
+ try {
91
+ await this._edgeConnection.send(ctx, protocol.createMessage(SwarmRequestSchema, {
92
+ serviceId: EdgeService.SWARM,
93
+ source: createMessageSource(topic, peer),
94
+ payload: {
95
+ action: SwarmRequest_Action.LEAVE,
96
+ swarmKeys: [topic.toHex()]
97
+ }
98
+ }));
99
+ } catch (err) {
100
+ if (err instanceof EdgeIdentityChangedError) return;
101
+ throw err;
102
+ }
103
+ }
104
+ async query(ctx, { topic }) {
105
+ const response = cancelWithContext(this._ctx, this.swarmState.waitFor((state) => state.swarmKey === topic.toHex()));
106
+ await this._edgeConnection.send(ctx, protocol.createMessage(SwarmRequestSchema, {
107
+ serviceId: EdgeService.SWARM,
108
+ source: createMessageSource(topic, {
109
+ peerKey: this._edgeConnection.peerKey,
110
+ identityDid: this._edgeConnection.identityDid
111
+ }),
112
+ payload: {
113
+ action: SwarmRequest_Action.INFO,
114
+ swarmKeys: [topic.toHex()]
115
+ }
116
+ }));
117
+ return response;
118
+ }
119
+ async sendMessage(ctx, message) {
120
+ const { author, recipient, tags, payload } = message;
121
+ invariant(recipient == null !== !tags?.length, "Exactly one of `recipient` or `tags` must be set", {
122
+ "~LogMeta": "~LogMeta",
123
+ F: __dxlog_file$1,
124
+ L: 177,
125
+ S: this,
126
+ A: ["(recipient == null) !== !tags?.length", "'Exactly one of `recipient` or `tags` must be set'"]
127
+ });
128
+ if (!this._matchSelfPeerInfo(author)) log.warn("ignoring author on send request", {
129
+ author,
130
+ expected: {
131
+ peerKey: this._edgeConnection.peerKey,
132
+ identityDid: this._edgeConnection.identityDid
133
+ }
134
+ }, {
135
+ "~LogMeta": "~LogMeta",
136
+ F: __dxlog_file$1,
137
+ L: 181,
138
+ S: this
139
+ });
140
+ await this._edgeConnection.send(ctx, protocol.createMessage(bufWkt.AnySchema, {
141
+ serviceId: EdgeService.SIGNAL,
142
+ source: author,
143
+ target: recipient != null ? [recipient] : void 0,
144
+ tags,
145
+ payload: {
146
+ typeUrl: payload.type_url,
147
+ value: payload.value
148
+ }
149
+ }));
150
+ }
151
+ async subscribeMessages({ peer, tags = [], onMessage }) {
152
+ const subscription = {
153
+ peerKey: peer.peerKey,
154
+ tags: new Set(tags),
155
+ onMessage
156
+ };
157
+ this._subscriptions.add(subscription);
158
+ let changed = false;
159
+ for (const tag of subscription.tags) {
160
+ const count = this._subscribedTags.get(tag) ?? 0;
161
+ this._subscribedTags.set(tag, count + 1);
162
+ if (count === 0) changed = true;
163
+ }
164
+ if (changed) await this._sendSubscription(this._ctx);
165
+ return async () => {
166
+ this._subscriptions.delete(subscription);
167
+ let released = false;
168
+ for (const tag of subscription.tags) {
169
+ const count = this._subscribedTags.get(tag);
170
+ if (count === void 0) continue;
171
+ if (count > 1) this._subscribedTags.set(tag, count - 1);
172
+ else {
173
+ this._subscribedTags.delete(tag);
174
+ released = true;
175
+ }
176
+ }
177
+ if (released) await this._sendSubscription(this._ctx);
178
+ };
179
+ }
180
+ /**
181
+ * Send the current broadcast tag subscription to every joined swarm (DX-1125). An empty tag set
182
+ * clears the subscription on the edge.
183
+ */
184
+ async _sendSubscription(ctx) {
185
+ const swarmKeys = Array.from(this._swarmPeers.keys()).map((topic) => topic.toHex());
186
+ if (swarmKeys.length === 0) return;
187
+ await this._edgeConnection.send(ctx, protocol.createMessage(SwarmRequestSchema, {
188
+ serviceId: EdgeService.SWARM,
189
+ source: {
190
+ peerKey: this._edgeConnection.peerKey,
191
+ identityDid: this._edgeConnection.identityDid
192
+ },
193
+ payload: {
194
+ action: SwarmRequest_Action.SUBSCRIBE,
195
+ swarmKeys,
196
+ subscribeTags: Array.from(this._subscribedTags.keys())
197
+ }
198
+ }));
199
+ }
200
+ _onMessage(message) {
201
+ switch (message.serviceId) {
202
+ case EdgeService.SWARM:
203
+ this._processSwarmResponse(message);
204
+ break;
205
+ case EdgeService.SIGNAL: this._processMessage(message);
206
+ }
207
+ }
208
+ _processSwarmResponse(message) {
209
+ invariant(protocol.getPayloadType(message) === SwarmResponseSchema.typeName, "Wrong payload type", {
210
+ "~LogMeta": "~LogMeta",
211
+ F: __dxlog_file$1,
212
+ L: 279,
213
+ S: this,
214
+ A: ["protocol.getPayloadType(message) === SwarmResponseSchema.typeName", "'Wrong payload type'"]
215
+ });
216
+ const payload = protocol.getPayload(message, SwarmResponseSchema);
217
+ this.swarmState.emit(payload);
218
+ const topic = PublicKey.from(payload.swarmKey);
219
+ if (!this._swarmPeers.has(topic)) return;
220
+ const { joinedPeers: oldPeers } = this._swarmPeers.get(topic);
221
+ const timestamp = message.timestamp ? new Date(Date.parse(message.timestamp)) : /* @__PURE__ */ new Date();
222
+ const newPeers = new ComplexSet(PeerInfoHash, payload.peers);
223
+ for (const peer of newPeers) {
224
+ if (oldPeers.has(peer)) continue;
225
+ this.swarmEvent.emit({
226
+ topic,
227
+ peerAvailable: {
228
+ peer,
229
+ since: timestamp
230
+ }
231
+ });
232
+ }
233
+ for (const peer of oldPeers) {
234
+ if (newPeers.has(peer)) continue;
235
+ this.swarmEvent.emit({
236
+ topic,
237
+ peerLeft: { peer }
238
+ });
239
+ }
240
+ this._swarmPeers.get(topic).joinedPeers = newPeers;
241
+ }
242
+ _processMessage(message) {
243
+ invariant(protocol.getPayloadType(message) === bufWkt.AnySchema.typeName, "Wrong payload type", {
244
+ "~LogMeta": "~LogMeta",
245
+ F: __dxlog_file$1,
246
+ L: 317,
247
+ S: this,
248
+ A: ["protocol.getPayloadType(message) === bufWkt.AnySchema.typeName", "'Wrong payload type'"]
249
+ });
250
+ const payload = protocol.getPayload(message, bufWkt.AnySchema);
251
+ invariant(message.source, "source is missing", {
252
+ "~LogMeta": "~LogMeta",
253
+ F: __dxlog_file$1,
254
+ L: 319,
255
+ S: this,
256
+ A: ["message.source", "'source is missing'"]
257
+ });
258
+ if ((message.tags?.length ?? 0) > 0 && (message.target?.length ?? 0) === 0) {
259
+ this._deliver({
260
+ author: message.source,
261
+ tags: message.tags ?? [],
262
+ payload: {
263
+ type_url: payload.typeUrl,
264
+ value: payload.value
265
+ }
266
+ });
267
+ return;
268
+ }
269
+ invariant(message.target, "target is missing", {
270
+ "~LogMeta": "~LogMeta",
271
+ F: __dxlog_file$1,
272
+ L: 331,
273
+ S: this,
274
+ A: ["message.target", "'target is missing'"]
275
+ });
276
+ invariant(message.target.length === 1, "target should have exactly one item", {
277
+ "~LogMeta": "~LogMeta",
278
+ F: __dxlog_file$1,
279
+ L: 332,
280
+ S: this,
281
+ A: ["message.target.length === 1", "'target should have exactly one item'"]
282
+ });
283
+ this._deliver({
284
+ author: message.source,
285
+ recipient: message.target[0],
286
+ payload: {
287
+ type_url: payload.typeUrl,
288
+ value: payload.value
289
+ }
290
+ });
291
+ }
292
+ /**
293
+ * Route an incoming message to matching subscriptions (DX-1125): point-to-point by recipient
294
+ * `peerKey`, broadcasts by tag intersection.
295
+ */
296
+ _deliver(message) {
297
+ for (const subscription of this._subscriptions) if (message.recipient != null) {
298
+ if (subscription.peerKey === message.recipient.peerKey) subscription.onMessage(message);
299
+ } else if (message.tags?.some((tag) => subscription.tags.has(tag))) subscription.onMessage(message);
300
+ }
301
+ _matchSelfPeerInfo(peer) {
302
+ return Boolean(peer && (peer.peerKey === this._edgeConnection.peerKey || peer.identityDid === this._edgeConnection.identityDid));
303
+ }
304
+ async _rejoinAllSwarms() {
305
+ log("rejoin swarms", { swarms: Array.from(this._swarmPeers.keys()) }, {
306
+ "~LogMeta": "~LogMeta",
307
+ F: __dxlog_file$1,
308
+ L: 367,
309
+ S: this
310
+ });
311
+ for (const [topic, { lastState }] of this._swarmPeers.entries()) await this.join(this._ctx, {
312
+ topic,
313
+ peer: {
314
+ peerKey: this._edgeConnection.peerKey,
315
+ identityDid: this._edgeConnection.identityDid,
316
+ state: lastState
317
+ }
318
+ });
319
+ if (this._subscribedTags.size > 0) await this._sendSubscription(this._ctx);
320
+ }
321
+ };
322
+ var createMessageSource = (topic, peerInfo) => {
323
+ return {
324
+ swarmKey: topic.toHex(),
325
+ ...peerInfo
326
+ };
327
+ };
328
+ //#endregion
329
+ //#region src/signal-manager/utils.ts
330
+ var __dxlog_file = "/__w/dxos/dxos/packages/core/mesh/messaging/src/signal-manager/utils.ts";
331
+ var setIdentityTags = ({ identityService, devicesService, runtime = Runtime.defaultRuntime, setTag }) => {
332
+ subscribeStream(runtime, identityService.IdentityService.queryIdentity(void 0), { onData: (idqr) => {
333
+ if (!idqr?.identity?.identityKey) {
334
+ log("empty response from identity service", { idqr }, {
335
+ "~LogMeta": "~LogMeta",
336
+ F: __dxlog_file,
337
+ L: 27,
338
+ S: void 0
339
+ });
340
+ return;
341
+ }
342
+ setTag("identityKey", idqr.identity.identityKey.truncate());
343
+ } });
344
+ subscribeStream(runtime, devicesService.DevicesService.queryDevices(void 0), { onData: (dqr) => {
345
+ if (!dqr || !dqr.devices || dqr.devices.length === 0) {
346
+ log("empty response from device service", { device: dqr }, {
347
+ "~LogMeta": "~LogMeta",
348
+ F: __dxlog_file,
349
+ L: 38,
350
+ S: void 0
351
+ });
352
+ return;
353
+ }
354
+ invariant(dqr, "empty response from device service", {
355
+ "~LogMeta": "~LogMeta",
356
+ F: __dxlog_file,
357
+ L: 41,
358
+ S: void 0,
359
+ A: ["dqr", "'empty response from device service'"]
360
+ });
361
+ const thisDevice = dqr.devices.find((device) => device.kind === DeviceKind.CURRENT);
362
+ if (!thisDevice) {
363
+ log("no current device", { device: dqr }, {
364
+ "~LogMeta": "~LogMeta",
365
+ F: __dxlog_file,
366
+ L: 45,
367
+ S: void 0
368
+ });
369
+ return;
370
+ }
371
+ setTag("deviceKey", thisDevice.deviceKey.truncate());
372
+ } });
373
+ };
374
+ //#endregion
375
+ export { EdgeSignalManager, MemorySignalManager, MemorySignalManagerContext, Messenger, PeerInfoHash, SignalManagerService, setIdentityTags };
376
+
377
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/signal-manager/signal-manager.ts","../../src/signal-manager/edge-signal-manager.ts","../../src/signal-manager/utils.ts"],"sourcesContent":["//\n// Copyright 2020 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\n\nimport { type Event } from '@dxos/async';\nimport { type Lifecycle } from '@dxos/context';\n\nimport { type SignalMethods, type SignalStatus } from '../signal-methods';\n\n/**\n * Manages a collection of signaling clients.\n */\nexport interface SignalManager extends SignalMethods, Required<Lifecycle> {\n statusChanged?: Event<SignalStatus[]>;\n getStatus?: () => SignalStatus[];\n}\n\nexport class SignalManagerService extends Context.Tag('@dxos/messaging/SignalManager')<\n SignalManagerService,\n SignalManager\n>() {}\n","//\n// Copyright 2024 DXOS.org\n//\n\n// DX-1059: this client is already DID-only — it never writes the deprecated `identity_key`\n// (proto field 2). The dual-read fallback that still gates removing that field lives on the\n// edge side (which relays peers from not-yet-migrated senders); nothing here needs it.\n\nimport { Event, scheduleMicroTask } from '@dxos/async';\nimport { type Context, Resource, cancelWithContext } from '@dxos/context';\nimport { type EdgeConnection, EdgeIdentityChangedError, protocol } from '@dxos/edge-client';\nimport { invariant } from '@dxos/invariant';\nimport { PublicKey } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { EdgeService } from '@dxos/protocols';\nimport { type buf, bufWkt } from '@dxos/protocols/buf';\nimport {\n type Message as EdgeMessage,\n type PeerSchema,\n SwarmRequest_Action as SwarmRequestAction,\n SwarmRequestSchema,\n SwarmResponseSchema,\n} from '@dxos/protocols/buf/dxos/edge/messenger_pb';\nimport { type SwarmResponse } from '@dxos/protocols/proto/dxos/edge/messenger';\nimport { ComplexMap, ComplexSet } from '@dxos/util';\n\nimport {\n type Message,\n type PeerInfo,\n PeerInfoHash,\n type SubscribeMessagesParams,\n type SwarmEvent,\n type UnsubscribeCallback,\n} from '../signal-methods';\nimport { type SignalManager } from './signal-manager';\n\n/**\n * A single message subscription registered on an {@link EdgeSignalManager} (DX-1125). Point-to-point\n * delivery matches `peerKey`; broadcast delivery matches any intersection with `tags`.\n */\ntype MessageSubscription = {\n peerKey: string;\n tags: Set<string>;\n onMessage: (message: Message) => void;\n};\n\nexport class EdgeSignalManager extends Resource implements SignalManager {\n /**\n * @deprecated\n */\n public swarmEvent = new Event<SwarmEvent>();\n public swarmState = new Event<SwarmResponse>();\n\n /**\n * Active message subscriptions. Routing is encapsulated here (DX-1125): each incoming message is\n * dispatched to every subscription it matches, and each subscription owns its own teardown.\n */\n private readonly _subscriptions = new Set<MessageSubscription>();\n\n /**\n * Swarm key -> { peer: <own state payload>, joinedPeers: <state of swarm> }.\n */\n // TODO(mykola): This class should not contain swarm state joinedPeers. Temporary before network-manager API changes to accept list of peers.\n private readonly _swarmPeers = new ComplexMap<\n PublicKey,\n { lastState?: Uint8Array; joinedPeers: ComplexSet<PeerInfo> }\n >(PublicKey.hash);\n\n /**\n * OR-subscription tag refcounts for broadcast messages (DX-1125). One count per distinct tag across\n * all subscribers, so one consumer's unsubscribe releases only its own registration rather than\n * clobbering another consumer's identical subscription. The effective tag set (the keys) is shared\n * across all joined swarms; the edge fans out any broadcast whose tags intersect it. Re-sent on\n * reconnect and whenever a swarm is (re-)joined.\n */\n private readonly _subscribedTags = new Map<string, number>();\n\n private readonly _edgeConnection: EdgeConnection;\n\n constructor({ edgeConnection }: { edgeConnection: EdgeConnection }) {\n super();\n this._edgeConnection = edgeConnection;\n }\n\n protected override async _open(): Promise<void> {\n this._ctx.onDispose(this._edgeConnection.onMessage((message) => this._onMessage(message)));\n this._ctx.onDispose(\n this._edgeConnection.onReconnected(() => {\n scheduleMicroTask(this._ctx, () => this._rejoinAllSwarms());\n }),\n );\n }\n\n /**\n * Warning: PeerInfo is inferred from edgeConnection.\n */\n async join(ctx: Context, { topic, peer }: { topic: PublicKey; peer: PeerInfo }): Promise<void> {\n if (!this._matchSelfPeerInfo(peer)) {\n // NOTE: Could only join swarm with the same peer info as the edge connection.\n log.warn('ignoring peer info on join request', {\n peer,\n expected: {\n peerKey: this._edgeConnection.peerKey,\n identityDid: this._edgeConnection.identityDid,\n },\n });\n\n // DX-1059: advertise only the identity DID; the client no longer sends the hex `identityKey`\n // (edge derives the connection's identity from auth, not from this message body).\n peer.identityDid = this._edgeConnection.identityDid;\n peer.peerKey = this._edgeConnection.peerKey;\n }\n\n this._swarmPeers.set(topic, { lastState: peer.state, joinedPeers: new ComplexSet<PeerInfo>(PeerInfoHash) });\n await this._edgeConnection.send(\n ctx,\n protocol.createMessage(SwarmRequestSchema, {\n serviceId: EdgeService.SWARM,\n source: createMessageSource(topic, peer),\n payload: { action: SwarmRequestAction.JOIN, swarmKeys: [topic.toHex()] },\n }),\n );\n\n // Re-establish any broadcast subscription on the newly-joined swarm (DX-1125).\n if (this._subscribedTags.size > 0) {\n await this._sendSubscription(ctx);\n }\n }\n\n async leave(ctx: Context, { topic, peer }: { topic: PublicKey; peer: PeerInfo }): Promise<void> {\n this._swarmPeers.delete(topic);\n try {\n await this._edgeConnection.send(\n ctx,\n protocol.createMessage(SwarmRequestSchema, {\n serviceId: EdgeService.SWARM,\n source: createMessageSource(topic, peer),\n payload: { action: SwarmRequestAction.LEAVE, swarmKeys: [topic.toHex()] },\n }),\n );\n } catch (err) {\n if (err instanceof EdgeIdentityChangedError) {\n // Note: On edge identity change, the connection is closed and EDGE will remove us from the swarm.\n // So we should just delete the swarm from _swarmPeers.\n return;\n }\n throw err;\n }\n }\n\n async query(ctx: Context, { topic }: { topic: PublicKey }): Promise<SwarmResponse> {\n const response = cancelWithContext(\n this._ctx,\n this.swarmState.waitFor((state) => state.swarmKey === topic.toHex()),\n );\n\n await this._edgeConnection.send(\n ctx,\n protocol.createMessage(SwarmRequestSchema, {\n serviceId: EdgeService.SWARM,\n source: createMessageSource(topic, {\n peerKey: this._edgeConnection.peerKey,\n identityDid: this._edgeConnection.identityDid,\n }),\n payload: { action: SwarmRequestAction.INFO, swarmKeys: [topic.toHex()] },\n }),\n );\n\n return response;\n }\n\n async sendMessage(ctx: Context, message: Message): Promise<void> {\n const { author, recipient, tags, payload } = message;\n // Exactly one of point-to-point (`recipient`) or broadcast (`tags`) delivery (DX-1125). A broadcast\n // carries its target swarm in `author.swarmKey` and is published with no `target`; the edge fans it\n // out to every peer whose subscription tags intersect.\n invariant((recipient == null) !== !tags?.length, 'Exactly one of `recipient` or `tags` must be set');\n\n if (!this._matchSelfPeerInfo(author)) {\n // NOTE: Could only join swarm with the same peer info as the edge connection.\n log.warn('ignoring author on send request', {\n author,\n expected: { peerKey: this._edgeConnection.peerKey, identityDid: this._edgeConnection.identityDid },\n });\n }\n\n await this._edgeConnection.send(\n ctx,\n protocol.createMessage(bufWkt.AnySchema, {\n serviceId: EdgeService.SIGNAL,\n source: author,\n target: recipient != null ? [recipient] : undefined,\n tags,\n payload: { typeUrl: payload.type_url, value: payload.value },\n }),\n );\n }\n\n async subscribeMessages({ peer, tags = [], onMessage }: SubscribeMessagesParams): Promise<UnsubscribeCallback> {\n const subscription: MessageSubscription = { peerKey: peer.peerKey, tags: new Set(tags), onMessage };\n this._subscriptions.add(subscription);\n\n // Point-to-point delivery needs no edge registration (the edge relays targeted messages to this\n // peer's socket). Only tag broadcasts require an OR-subscription registered on the swarm (DX-1125),\n // refcounted so one subscriber's teardown does not clobber another's identical tags.\n let changed = false;\n for (const tag of subscription.tags) {\n const count = this._subscribedTags.get(tag) ?? 0;\n this._subscribedTags.set(tag, count + 1);\n if (count === 0) {\n changed = true;\n }\n }\n if (changed) {\n await this._sendSubscription(this._ctx);\n }\n\n return async () => {\n this._subscriptions.delete(subscription);\n // Release only this subscription's tag registrations; a tag stays live while another holds it.\n let released = false;\n for (const tag of subscription.tags) {\n const count = this._subscribedTags.get(tag);\n if (count === undefined) {\n continue;\n }\n if (count > 1) {\n this._subscribedTags.set(tag, count - 1);\n } else {\n this._subscribedTags.delete(tag);\n released = true;\n }\n }\n if (released) {\n await this._sendSubscription(this._ctx);\n }\n };\n }\n\n /**\n * Send the current broadcast tag subscription to every joined swarm (DX-1125). An empty tag set\n * clears the subscription on the edge.\n */\n private async _sendSubscription(ctx: Context): Promise<void> {\n const swarmKeys = Array.from(this._swarmPeers.keys()).map((topic) => topic.toHex());\n if (swarmKeys.length === 0) {\n return;\n }\n await this._edgeConnection.send(\n ctx,\n protocol.createMessage(SwarmRequestSchema, {\n serviceId: EdgeService.SWARM,\n source: {\n peerKey: this._edgeConnection.peerKey,\n identityDid: this._edgeConnection.identityDid,\n },\n payload: {\n action: SwarmRequestAction.SUBSCRIBE,\n swarmKeys,\n subscribeTags: Array.from(this._subscribedTags.keys()),\n },\n }),\n );\n }\n\n private _onMessage(message: EdgeMessage): void {\n switch (message.serviceId) {\n case EdgeService.SWARM: {\n this._processSwarmResponse(message);\n break;\n }\n case EdgeService.SIGNAL: {\n this._processMessage(message);\n }\n }\n }\n\n private _processSwarmResponse(message: EdgeMessage): void {\n invariant(protocol.getPayloadType(message) === SwarmResponseSchema.typeName, 'Wrong payload type');\n const payload = protocol.getPayload(message, SwarmResponseSchema);\n this.swarmState.emit(payload);\n const topic = PublicKey.from(payload.swarmKey);\n if (!this._swarmPeers.has(topic)) {\n return;\n }\n\n const { joinedPeers: oldPeers } = this._swarmPeers.get(topic)!;\n const timestamp = message.timestamp ? new Date(Date.parse(message.timestamp)) : new Date();\n const newPeers = new ComplexSet<PeerInfo>(PeerInfoHash, payload.peers);\n\n // Emit new available peers in the swarm.\n for (const peer of newPeers) {\n if (oldPeers.has(peer)) {\n continue;\n }\n this.swarmEvent.emit({\n topic,\n peerAvailable: { peer, since: timestamp },\n });\n }\n\n // Emit peer that left the swarm.\n for (const peer of oldPeers) {\n if (newPeers.has(peer)) {\n continue;\n }\n this.swarmEvent.emit({\n topic,\n peerLeft: { peer },\n });\n }\n\n this._swarmPeers.get(topic)!.joinedPeers = newPeers;\n }\n\n private _processMessage(message: EdgeMessage): void {\n invariant(protocol.getPayloadType(message) === bufWkt.AnySchema.typeName, 'Wrong payload type');\n const payload = protocol.getPayload(message, bufWkt.AnySchema);\n invariant(message.source, 'source is missing');\n\n // Broadcasts (DX-1125) carry tags and no target; point-to-point messages carry exactly one target.\n if ((message.tags?.length ?? 0) > 0 && (message.target?.length ?? 0) === 0) {\n this._deliver({\n author: message.source,\n tags: message.tags ?? [],\n payload: { type_url: payload.typeUrl, value: payload.value },\n });\n return;\n }\n\n invariant(message.target, 'target is missing');\n invariant(message.target.length === 1, 'target should have exactly one item');\n\n this._deliver({\n author: message.source,\n recipient: message.target[0],\n payload: {\n type_url: payload.typeUrl,\n value: payload.value,\n },\n });\n }\n\n /**\n * Route an incoming message to matching subscriptions (DX-1125): point-to-point by recipient\n * `peerKey`, broadcasts by tag intersection.\n */\n private _deliver(message: Message): void {\n for (const subscription of this._subscriptions) {\n if (message.recipient != null) {\n if (subscription.peerKey === message.recipient.peerKey) {\n subscription.onMessage(message);\n }\n } else if (message.tags?.some((tag) => subscription.tags.has(tag))) {\n subscription.onMessage(message);\n }\n }\n }\n\n private _matchSelfPeerInfo(peer: PeerInfo): boolean {\n return Boolean(\n peer && (peer.peerKey === this._edgeConnection.peerKey || peer.identityDid === this._edgeConnection.identityDid),\n );\n }\n\n private async _rejoinAllSwarms(): Promise<void> {\n log('rejoin swarms', { swarms: Array.from(this._swarmPeers.keys()) });\n for (const [topic, { lastState }] of this._swarmPeers.entries()) {\n await this.join(this._ctx, {\n topic,\n peer: {\n peerKey: this._edgeConnection.peerKey,\n identityDid: this._edgeConnection.identityDid,\n state: lastState,\n },\n });\n }\n // Re-establish the broadcast subscription across the rejoined swarms (DX-1125).\n if (this._subscribedTags.size > 0) {\n await this._sendSubscription(this._ctx);\n }\n }\n}\n\nconst createMessageSource = (topic: PublicKey, peerInfo: PeerInfo): buf.MessageInitShape<typeof PeerSchema> => {\n return {\n swarmKey: topic.toHex(),\n ...peerInfo,\n };\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Runtime from 'effect/Runtime';\n\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { subscribeStream } from '@dxos/protocols';\nimport { DeviceKind } from '@dxos/protocols/proto/dxos/client/services';\nimport { type DevicesService, type IdentityService } from '@dxos/protocols/rpc';\n\nexport const setIdentityTags = ({\n identityService,\n devicesService,\n runtime = Runtime.defaultRuntime,\n setTag,\n}: {\n identityService: IdentityService.Client;\n devicesService: DevicesService.Client;\n runtime?: Runtime.Runtime<never>;\n setTag: (k: string, v: string) => void;\n}) => {\n subscribeStream(runtime, identityService.IdentityService.queryIdentity(undefined), {\n onData: (idqr) => {\n if (!idqr?.identity?.identityKey) {\n log('empty response from identity service', { idqr });\n return;\n }\n\n setTag('identityKey', idqr.identity.identityKey.truncate());\n },\n });\n\n subscribeStream(runtime, devicesService.DevicesService.queryDevices(undefined), {\n onData: (dqr) => {\n if (!dqr || !dqr.devices || dqr.devices.length === 0) {\n log('empty response from device service', { device: dqr });\n return;\n }\n invariant(dqr, 'empty response from device service');\n\n const thisDevice = dqr.devices.find((device) => device.kind === DeviceKind.CURRENT);\n if (!thisDevice) {\n log('no current device', { device: dqr });\n return;\n }\n setTag('deviceKey', thisDevice.deviceKey.truncate());\n },\n });\n};\n"],"mappings":"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"}