@dxos/teleport-extension-replicator 0.8.2-staging.7ac8446 → 0.8.2
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.
- package/dist/lib/browser/index.mjs.map +2 -2
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs.map +2 -2
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/replicator-extension.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +15 -15
- package/src/replicator-extension.ts +8 -8
- package/src/stress.test.ts +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/replicator-extension.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport type { ProtocolStream } from 'hypercore-protocol';\nimport { type Duplex } from 'node:stream';\n\nimport { asyncTimeout, DeferredTask, synchronized } from '@dxos/async';\nimport { Context } from '@dxos/context';\nimport { failUndefined } from '@dxos/debug';\nimport { type FeedWrapper } from '@dxos/feed-store';\nimport { invariant } from '@dxos/invariant';\nimport { PublicKey } from '@dxos/keys';\nimport { log, logInfo } from '@dxos/log';\nimport { RpcClosedError, TimeoutError } from '@dxos/protocols';\nimport { schema } from '@dxos/protocols/proto';\nimport { type FeedInfo, type ReplicatorService } from '@dxos/protocols/proto/dxos/mesh/teleport/replicator';\nimport { createProtoRpcPeer, type ProtoRpcPeer } from '@dxos/rpc';\nimport { type ExtensionContext, type TeleportExtension } from '@dxos/teleport';\nimport { ComplexMap } from '@dxos/util';\n\nexport type ReplicationOptions = {\n upload: boolean;\n};\n\n/**\n * Manages replication between a set of feeds for a single teleport session.\n */\nexport class ReplicatorExtension implements TeleportExtension {\n private readonly _ctx = new Context({\n onError: (err) => {\n this._extensionContext?.close(err);\n },\n });\n\n private readonly _feeds = new ComplexMap<PublicKey, FeedWrapper<any>>(PublicKey.hash);\n private readonly _streams = new ComplexMap<PublicKey, ActiveStream>(PublicKey.hash);\n\n private _rpc?: ProtoRpcPeer<ServiceBundle>;\n private _extensionContext?: ExtensionContext;\n\n private _options: ReplicationOptions = {\n upload: false,\n };\n\n private readonly _updateTask = new DeferredTask(this._ctx, async () => {\n try {\n if (this._extensionContext!.initiator === false) {\n await this._rpc!.rpc.ReplicatorService.updateFeeds({\n feeds: Array.from(this._feeds.values()).map((feed) => ({\n feedKey: feed.key,\n download: true,\n upload: this._options.upload,\n })),\n });\n } else if (this._extensionContext!.initiator === true) {\n await this._reevaluateFeeds();\n }\n } catch (err) {\n if (err instanceof RpcClosedError) {\n return; // Some RPC requests might be pending while closing.\n }\n throw err;\n }\n });\n\n @logInfo\n private get extensionInfo() {\n return {\n initiator: this._extensionContext?.initiator,\n localPeerId: this._extensionContext?.localPeerId,\n remotePeerId: this._extensionContext?.remotePeerId,\n feeds: Array.from(this._feeds.keys()),\n };\n }\n\n setOptions(options: ReplicationOptions): this {\n this._options = options;\n log('setOptions', { options });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n return this;\n }\n\n addFeed(feed: FeedWrapper<any>) {\n this._feeds.set(feed.key, feed);\n log('addFeed', { feedKey: feed.key });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n }\n\n async onOpen(context: ExtensionContext) {\n this._extensionContext = context;\n log('open');\n\n this._rpc = createProtoRpcPeer<ServiceBundle, ServiceBundle>({\n requested: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n exposed: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n handlers: {\n ReplicatorService: {\n updateFeeds: async ({ feeds }) => {\n log('received feed info', { feeds });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n this._updateTask.schedule();\n },\n startReplication: async ({ info }) => {\n log('starting replication...', { info });\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n const streamTag = await this._acceptReplication(info);\n return {\n streamTag,\n };\n },\n stopReplication: async ({ info }) => {\n log('stopping replication...', { info });\n // TODO(dmaretskyi): Make sure any peer can stop replication.\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n await this._stopReplication(info.feedKey);\n },\n },\n },\n port: await context.createPort('rpc', {\n contentType: 'application/x-protobuf; messageType=\"dxos.rpc.Message\"',\n }),\n timeout: 10_000,\n });\n await this._rpc.open();\n\n this._updateTask.schedule();\n }\n\n async onClose(err?: Error | undefined) {\n log('close', { err });\n await this._ctx.dispose();\n await this._rpc?.close();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n async onAbort(err?: Error | undefined) {\n log('abort', { err });\n\n await this._ctx.dispose();\n await this._rpc?.abort();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n @synchronized\n private async _reevaluateFeeds() {\n log('_reevaluateFeeds');\n for (const feedKey of this._feeds.keys()) {\n if (this._ctx.disposed) {\n return;\n }\n if (this._streams.has(feedKey) && this._options.upload !== this._streams.get(feedKey)?.info.upload) {\n try {\n await asyncTimeout(this._stopReplication(feedKey), 1000);\n } catch (err) {\n log.catch(err);\n }\n }\n\n if (this._ctx.disposed) {\n return;\n }\n if (!this._streams.has(feedKey)) {\n await this._initiateReplication({\n feedKey,\n download: true,\n upload: this._options.upload,\n });\n }\n }\n }\n\n /**\n * Try to initiate feed replication.\n */\n private async _initiateReplication(feedInfo: FeedInfo) {\n log('initiating replication', { feedInfo });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n invariant(!this._streams.has(feedInfo.feedKey), `Replication already in progress for feed: ${feedInfo.feedKey}`);\n const { streamTag } = await this._rpc!.rpc.ReplicatorService.startReplication({ info: feedInfo });\n if (!streamTag) {\n return;\n }\n\n await this._replicateFeed(feedInfo, streamTag);\n }\n\n /**\n * Respond to a remote request to replicate a feed.\n * @returns A stream tag for the replication stream or `undefined` if we don't want to replicate.\n */\n @synchronized\n private async _acceptReplication(feedInfo: FeedInfo): Promise<string | undefined> {\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n if (!this._feeds.has(feedInfo.feedKey) || this._streams.has(feedInfo.feedKey)) {\n return undefined; // We don't have the feed or we are already replicating it.\n }\n\n const tag = `feed-${feedInfo.feedKey.toHex()}-${PublicKey.random().toHex().slice(0, 8)}`; // Generate a unique tag for the stream.\n await this._replicateFeed(feedInfo, tag);\n return tag;\n }\n\n private async _replicateFeed(info: FeedInfo, streamTag: string) {\n log('replicate', { info, streamTag });\n invariant(!this._streams.has(info.feedKey), `Replication already in progress for feed: ${info.feedKey}`);\n\n const feed = this._feeds.get(info.feedKey) ?? failUndefined();\n const networkStream = await this._extensionContext!.createStream(streamTag, {\n contentType: 'application/x-hypercore',\n });\n let replicationStreamErrors = 0;\n\n // https://github.com/holepunchto/hypercore/tree/v9.12.0#var-stream--feedreplicateisinitiator-options\n const replicationStream = feed.replicate(true, {\n live: true,\n upload: info.upload,\n download: info.download,\n noise: false,\n encrypted: false,\n maxRequests: 1024,\n });\n\n // Left for testing.\n // const debug = true;\n // if (debug) {\n // feed.on('sync', () => {\n // log.info('sync', { key: feed.key, length: feed.length });\n // });\n // feed.on('download', (index: number, data: any) => {\n // log.info('download', {\n // key: feed.key,\n // index,\n // length: feed.length,\n // data: data.length,\n // });\n // });\n // }\n\n replicationStream.on('error', (err) => {\n if (err instanceof TimeoutError) {\n log.info('replication stream timeout', { err, info });\n return;\n }\n // TODO(nf): use sentinel errors\n if (\n err?.message === 'Writable stream closed prematurely' ||\n err?.message === 'Cannot call write after a stream was destroyed'\n ) {\n log('replication stream closed', { err, info });\n return;\n }\n\n // TODO(nf): WARN on first error? log full info on some subsequent errors?\n if (replicationStreamErrors === 0) {\n log.info('replication stream error', { err, info });\n } else {\n log.info('replication stream error', { err, feedKey: info.feedKey, count: replicationStreamErrors });\n }\n replicationStreamErrors++;\n });\n\n this._streams.set(info.feedKey, {\n streamTag,\n networkStream,\n replicationStream,\n info,\n });\n\n networkStream.pipe(replicationStream as any).pipe(networkStream);\n }\n\n private async _stopReplication(feedKey: PublicKey) {\n const stream = this._streams.get(feedKey);\n if (!stream) {\n return;\n }\n\n stream.networkStream.destroy();\n this._streams.delete(feedKey);\n }\n}\n\ntype ServiceBundle = {\n ReplicatorService: ReplicatorService;\n};\n\ntype ActiveStream = {\n streamTag: string;\n networkStream: Duplex;\n replicationStream: ProtocolStream;\n info: FeedInfo;\n};\n"],
|
|
5
|
-
"mappings": ";;;AAOA,SAASA,cAAcC,cAAcC,oBAAoB;AACzD,SAASC,eAAe;AACxB,SAASC,qBAAqB;AAE9B,SAASC,iBAAiB;AAC1B,SAASC,iBAAiB;AAC1B,SAASC,KAAKC,eAAe;AAC7B,SAASC,gBAAgBC,oBAAoB;AAC7C,SAASC,cAAc;AAEvB,SAASC,0BAA6C;AAEtD,SAASC,kBAAkB;;;;;;;;AASpB,IAAMC,sBAAN,MAAMA;EAAN;AACYC,gBAAO,IAAIZ,QAAQ;MAClCa,SAAS,CAACC,QAAAA;AACR,aAAKC,mBAAmBC,MAAMF,GAAAA;MAChC;IACF,GAAA;;;;AAEiBG,kBAAS,IAAIP,WAAwCP,UAAUe,IAAI;AACnEC,oBAAW,IAAIT,WAAoCP,UAAUe,IAAI;AAK1EE,oBAA+B;MACrCC,QAAQ;IACV;AAEiBC,uBAAc,IAAIxB,aAAa,KAAKc,MAAM,YAAA;AACzD,UAAI;AACF,YAAI,KAAKG,kBAAmBQ,cAAc,OAAO;AAC/C,gBAAM,KAAKC,KAAMC,IAAIC,kBAAkBC,YAAY;YACjDC,OAAOC,MAAMC,KAAK,KAAKb,OAAOc,OAAM,CAAA,EAAIC,IAAI,CAACC,UAAU;cACrDC,SAASD,KAAKE;cACdC,UAAU;cACVf,QAAQ,KAAKD,SAASC;YACxB,EAAA;UACF,CAAA;QACF,WAAW,KAAKN,kBAAmBQ,cAAc,MAAM;AACrD,gBAAM,KAAKc,iBAAgB;QAC7B;MACF,SAASvB,KAAK;AACZ,YAAIA,eAAeR,gBAAgB;AACjC;QACF;AACA,cAAMQ;MACR;IACF,CAAA;;EAEA,IACYwB,gBAAgB;AAC1B,WAAO;MACLf,WAAW,KAAKR,mBAAmBQ;MACnCgB,aAAa,KAAKxB,mBAAmBwB;MACrCC,cAAc,KAAKzB,mBAAmByB;MACtCZ,OAAOC,MAAMC,KAAK,KAAKb,OAAOwB,KAAI,CAAA;IACpC;EACF;EAEAC,WAAWC,SAAmC;AAC5C,SAAKvB,WAAWuB;AAChBvC,QAAI,cAAc;MAAEuC;IAAQ,GAAA;;;;;;AAC5B,QAAI,KAAK5B,mBAAmB;AAC1B,WAAKO,YAAYsB,SAAQ;IAC3B;AACA,WAAO;EACT;EAEAC,QAAQZ,
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport type { ProtocolStream } from 'hypercore-protocol';\nimport { type Duplex } from 'node:stream';\n\nimport { asyncTimeout, DeferredTask, synchronized } from '@dxos/async';\nimport { Context } from '@dxos/context';\nimport { failUndefined } from '@dxos/debug';\nimport { type FeedWrapper } from '@dxos/feed-store';\nimport { invariant } from '@dxos/invariant';\nimport { PublicKey } from '@dxos/keys';\nimport { log, logInfo } from '@dxos/log';\nimport { RpcClosedError, TimeoutError } from '@dxos/protocols';\nimport { schema } from '@dxos/protocols/proto';\nimport { type FeedInfo, type ReplicatorService } from '@dxos/protocols/proto/dxos/mesh/teleport/replicator';\nimport { createProtoRpcPeer, type ProtoRpcPeer } from '@dxos/rpc';\nimport { type ExtensionContext, type TeleportExtension } from '@dxos/teleport';\nimport { ComplexMap } from '@dxos/util';\n\nexport type ReplicationOptions = {\n upload: boolean;\n};\n\n/**\n * Manages replication between a set of feeds for a single teleport session.\n */\nexport class ReplicatorExtension implements TeleportExtension {\n private readonly _ctx = new Context({\n onError: (err) => {\n this._extensionContext?.close(err);\n },\n });\n\n private readonly _feeds = new ComplexMap<PublicKey, FeedWrapper<any>>(PublicKey.hash);\n private readonly _streams = new ComplexMap<PublicKey, ActiveStream>(PublicKey.hash);\n\n private _rpc?: ProtoRpcPeer<ServiceBundle>;\n private _extensionContext?: ExtensionContext;\n\n private _options: ReplicationOptions = {\n upload: false,\n };\n\n private readonly _updateTask = new DeferredTask(this._ctx, async () => {\n try {\n if (this._extensionContext!.initiator === false) {\n await this._rpc!.rpc.ReplicatorService.updateFeeds({\n feeds: Array.from(this._feeds.values()).map((feed) => ({\n feedKey: feed.key,\n download: true,\n upload: this._options.upload,\n })),\n });\n } else if (this._extensionContext!.initiator === true) {\n await this._reevaluateFeeds();\n }\n } catch (err) {\n if (err instanceof RpcClosedError) {\n return; // Some RPC requests might be pending while closing.\n }\n throw err;\n }\n });\n\n @logInfo\n private get extensionInfo() {\n return {\n initiator: this._extensionContext?.initiator,\n localPeerId: this._extensionContext?.localPeerId,\n remotePeerId: this._extensionContext?.remotePeerId,\n feeds: Array.from(this._feeds.keys()),\n };\n }\n\n setOptions(options: ReplicationOptions): this {\n this._options = options;\n log('setOptions', { options });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n return this;\n }\n\n addFeed(feed: FeedWrapper<any>): void {\n this._feeds.set(feed.key, feed);\n log('addFeed', { feedKey: feed.key });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n }\n\n async onOpen(context: ExtensionContext): Promise<void> {\n this._extensionContext = context;\n log('open');\n\n this._rpc = createProtoRpcPeer<ServiceBundle, ServiceBundle>({\n requested: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n exposed: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n handlers: {\n ReplicatorService: {\n updateFeeds: async ({ feeds }) => {\n log('received feed info', { feeds });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n this._updateTask.schedule();\n },\n startReplication: async ({ info }) => {\n log('starting replication...', { info });\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n const streamTag = await this._acceptReplication(info);\n return {\n streamTag,\n };\n },\n stopReplication: async ({ info }) => {\n log('stopping replication...', { info });\n // TODO(dmaretskyi): Make sure any peer can stop replication.\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n await this._stopReplication(info.feedKey);\n },\n },\n },\n port: await context.createPort('rpc', {\n contentType: 'application/x-protobuf; messageType=\"dxos.rpc.Message\"',\n }),\n timeout: 10_000,\n });\n await this._rpc.open();\n\n this._updateTask.schedule();\n }\n\n async onClose(err?: Error | undefined): Promise<void> {\n log('close', { err });\n await this._ctx.dispose();\n await this._rpc?.close();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n async onAbort(err?: Error | undefined): Promise<void> {\n log('abort', { err });\n\n await this._ctx.dispose();\n await this._rpc?.abort();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n @synchronized\n private async _reevaluateFeeds(): Promise<void> {\n log('_reevaluateFeeds');\n for (const feedKey of this._feeds.keys()) {\n if (this._ctx.disposed) {\n return;\n }\n if (this._streams.has(feedKey) && this._options.upload !== this._streams.get(feedKey)?.info.upload) {\n try {\n await asyncTimeout(this._stopReplication(feedKey), 1000);\n } catch (err) {\n log.catch(err);\n }\n }\n\n if (this._ctx.disposed) {\n return;\n }\n if (!this._streams.has(feedKey)) {\n await this._initiateReplication({\n feedKey,\n download: true,\n upload: this._options.upload,\n });\n }\n }\n }\n\n /**\n * Try to initiate feed replication.\n */\n private async _initiateReplication(feedInfo: FeedInfo): Promise<void> {\n log('initiating replication', { feedInfo });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n invariant(!this._streams.has(feedInfo.feedKey), `Replication already in progress for feed: ${feedInfo.feedKey}`);\n const { streamTag } = await this._rpc!.rpc.ReplicatorService.startReplication({ info: feedInfo });\n if (!streamTag) {\n return;\n }\n\n await this._replicateFeed(feedInfo, streamTag);\n }\n\n /**\n * Respond to a remote request to replicate a feed.\n * @returns A stream tag for the replication stream or `undefined` if we don't want to replicate.\n */\n @synchronized\n private async _acceptReplication(feedInfo: FeedInfo): Promise<string | undefined> {\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n if (!this._feeds.has(feedInfo.feedKey) || this._streams.has(feedInfo.feedKey)) {\n return undefined; // We don't have the feed or we are already replicating it.\n }\n\n const tag = `feed-${feedInfo.feedKey.toHex()}-${PublicKey.random().toHex().slice(0, 8)}`; // Generate a unique tag for the stream.\n await this._replicateFeed(feedInfo, tag);\n return tag;\n }\n\n private async _replicateFeed(info: FeedInfo, streamTag: string): Promise<void> {\n log('replicate', { info, streamTag });\n invariant(!this._streams.has(info.feedKey), `Replication already in progress for feed: ${info.feedKey}`);\n\n const feed = this._feeds.get(info.feedKey) ?? failUndefined();\n const networkStream = await this._extensionContext!.createStream(streamTag, {\n contentType: 'application/x-hypercore',\n });\n let replicationStreamErrors = 0;\n\n // https://github.com/holepunchto/hypercore/tree/v9.12.0#var-stream--feedreplicateisinitiator-options\n const replicationStream = feed.replicate(true, {\n live: true,\n upload: info.upload,\n download: info.download,\n noise: false,\n encrypted: false,\n maxRequests: 1024,\n });\n\n // Left for testing.\n // const debug = true;\n // if (debug) {\n // feed.on('sync', () => {\n // log.info('sync', { key: feed.key, length: feed.length });\n // });\n // feed.on('download', (index: number, data: any) => {\n // log.info('download', {\n // key: feed.key,\n // index,\n // length: feed.length,\n // data: data.length,\n // });\n // });\n // }\n\n replicationStream.on('error', (err) => {\n if (err instanceof TimeoutError) {\n log.info('replication stream timeout', { err, info });\n return;\n }\n // TODO(nf): use sentinel errors\n if (\n err?.message === 'Writable stream closed prematurely' ||\n err?.message === 'Cannot call write after a stream was destroyed'\n ) {\n log('replication stream closed', { err, info });\n return;\n }\n\n // TODO(nf): WARN on first error? log full info on some subsequent errors?\n if (replicationStreamErrors === 0) {\n log.info('replication stream error', { err, info });\n } else {\n log.info('replication stream error', { err, feedKey: info.feedKey, count: replicationStreamErrors });\n }\n replicationStreamErrors++;\n });\n\n this._streams.set(info.feedKey, {\n streamTag,\n networkStream,\n replicationStream,\n info,\n });\n\n networkStream.pipe(replicationStream as any).pipe(networkStream);\n }\n\n private async _stopReplication(feedKey: PublicKey): Promise<void> {\n const stream = this._streams.get(feedKey);\n if (!stream) {\n return;\n }\n\n stream.networkStream.destroy();\n this._streams.delete(feedKey);\n }\n}\n\ntype ServiceBundle = {\n ReplicatorService: ReplicatorService;\n};\n\ntype ActiveStream = {\n streamTag: string;\n networkStream: Duplex;\n replicationStream: ProtocolStream;\n info: FeedInfo;\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["asyncTimeout", "DeferredTask", "synchronized", "Context", "failUndefined", "invariant", "PublicKey", "log", "logInfo", "RpcClosedError", "TimeoutError", "schema", "createProtoRpcPeer", "ComplexMap", "ReplicatorExtension", "_ctx", "onError", "err", "_extensionContext", "close", "_feeds", "hash", "_streams", "_options", "upload", "_updateTask", "initiator", "_rpc", "rpc", "ReplicatorService", "updateFeeds", "feeds", "Array", "from", "values", "map", "feed", "feedKey", "key", "download", "_reevaluateFeeds", "extensionInfo", "localPeerId", "remotePeerId", "keys", "setOptions", "options", "schedule", "addFeed", "set", "onOpen", "context", "requested", "getService", "exposed", "handlers", "startReplication", "info", "streamTag", "_acceptReplication", "stopReplication", "_stopReplication", "port", "createPort", "contentType", "timeout", "open", "onClose", "dispose", "onAbort", "abort", "disposed", "has", "get", "catch", "_initiateReplication", "feedInfo", "_replicateFeed", "undefined", "tag", "toHex", "random", "slice", "networkStream", "createStream", "replicationStreamErrors", "replicationStream", "replicate", "live", "noise", "encrypted", "maxRequests", "on", "message", "count", "pipe", "stream", "destroy", "delete"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts":{"bytes":
|
|
1
|
+
{"inputs":{"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts":{"bytes":38844,"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/protocols","kind":"import-statement","external":true},{"path":"@dxos/protocols/proto","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/teleport-extension-replicator/src/index.ts":{"bytes":520,"imports":[{"path":"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts","kind":"import-statement","original":"./replicator-extension"}],"format":"esm"}},"outputs":{"packages/core/mesh/teleport-extension-replicator/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":16530},"packages/core/mesh/teleport-extension-replicator/dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/protocols","kind":"import-statement","external":true},{"path":"@dxos/protocols/proto","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["ReplicatorExtension"],"entryPoint":"packages/core/mesh/teleport-extension-replicator/src/index.ts","inputs":{"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts":{"bytesInOutput":11707},"packages/core/mesh/teleport-extension-replicator/src/index.ts":{"bytesInOutput":0}},"bytes":11890}}}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/replicator-extension.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport type { ProtocolStream } from 'hypercore-protocol';\nimport { type Duplex } from 'node:stream';\n\nimport { asyncTimeout, DeferredTask, synchronized } from '@dxos/async';\nimport { Context } from '@dxos/context';\nimport { failUndefined } from '@dxos/debug';\nimport { type FeedWrapper } from '@dxos/feed-store';\nimport { invariant } from '@dxos/invariant';\nimport { PublicKey } from '@dxos/keys';\nimport { log, logInfo } from '@dxos/log';\nimport { RpcClosedError, TimeoutError } from '@dxos/protocols';\nimport { schema } from '@dxos/protocols/proto';\nimport { type FeedInfo, type ReplicatorService } from '@dxos/protocols/proto/dxos/mesh/teleport/replicator';\nimport { createProtoRpcPeer, type ProtoRpcPeer } from '@dxos/rpc';\nimport { type ExtensionContext, type TeleportExtension } from '@dxos/teleport';\nimport { ComplexMap } from '@dxos/util';\n\nexport type ReplicationOptions = {\n upload: boolean;\n};\n\n/**\n * Manages replication between a set of feeds for a single teleport session.\n */\nexport class ReplicatorExtension implements TeleportExtension {\n private readonly _ctx = new Context({\n onError: (err) => {\n this._extensionContext?.close(err);\n },\n });\n\n private readonly _feeds = new ComplexMap<PublicKey, FeedWrapper<any>>(PublicKey.hash);\n private readonly _streams = new ComplexMap<PublicKey, ActiveStream>(PublicKey.hash);\n\n private _rpc?: ProtoRpcPeer<ServiceBundle>;\n private _extensionContext?: ExtensionContext;\n\n private _options: ReplicationOptions = {\n upload: false,\n };\n\n private readonly _updateTask = new DeferredTask(this._ctx, async () => {\n try {\n if (this._extensionContext!.initiator === false) {\n await this._rpc!.rpc.ReplicatorService.updateFeeds({\n feeds: Array.from(this._feeds.values()).map((feed) => ({\n feedKey: feed.key,\n download: true,\n upload: this._options.upload,\n })),\n });\n } else if (this._extensionContext!.initiator === true) {\n await this._reevaluateFeeds();\n }\n } catch (err) {\n if (err instanceof RpcClosedError) {\n return; // Some RPC requests might be pending while closing.\n }\n throw err;\n }\n });\n\n @logInfo\n private get extensionInfo() {\n return {\n initiator: this._extensionContext?.initiator,\n localPeerId: this._extensionContext?.localPeerId,\n remotePeerId: this._extensionContext?.remotePeerId,\n feeds: Array.from(this._feeds.keys()),\n };\n }\n\n setOptions(options: ReplicationOptions): this {\n this._options = options;\n log('setOptions', { options });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n return this;\n }\n\n addFeed(feed: FeedWrapper<any>) {\n this._feeds.set(feed.key, feed);\n log('addFeed', { feedKey: feed.key });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n }\n\n async onOpen(context: ExtensionContext) {\n this._extensionContext = context;\n log('open');\n\n this._rpc = createProtoRpcPeer<ServiceBundle, ServiceBundle>({\n requested: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n exposed: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n handlers: {\n ReplicatorService: {\n updateFeeds: async ({ feeds }) => {\n log('received feed info', { feeds });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n this._updateTask.schedule();\n },\n startReplication: async ({ info }) => {\n log('starting replication...', { info });\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n const streamTag = await this._acceptReplication(info);\n return {\n streamTag,\n };\n },\n stopReplication: async ({ info }) => {\n log('stopping replication...', { info });\n // TODO(dmaretskyi): Make sure any peer can stop replication.\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n await this._stopReplication(info.feedKey);\n },\n },\n },\n port: await context.createPort('rpc', {\n contentType: 'application/x-protobuf; messageType=\"dxos.rpc.Message\"',\n }),\n timeout: 10_000,\n });\n await this._rpc.open();\n\n this._updateTask.schedule();\n }\n\n async onClose(err?: Error | undefined) {\n log('close', { err });\n await this._ctx.dispose();\n await this._rpc?.close();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n async onAbort(err?: Error | undefined) {\n log('abort', { err });\n\n await this._ctx.dispose();\n await this._rpc?.abort();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n @synchronized\n private async _reevaluateFeeds() {\n log('_reevaluateFeeds');\n for (const feedKey of this._feeds.keys()) {\n if (this._ctx.disposed) {\n return;\n }\n if (this._streams.has(feedKey) && this._options.upload !== this._streams.get(feedKey)?.info.upload) {\n try {\n await asyncTimeout(this._stopReplication(feedKey), 1000);\n } catch (err) {\n log.catch(err);\n }\n }\n\n if (this._ctx.disposed) {\n return;\n }\n if (!this._streams.has(feedKey)) {\n await this._initiateReplication({\n feedKey,\n download: true,\n upload: this._options.upload,\n });\n }\n }\n }\n\n /**\n * Try to initiate feed replication.\n */\n private async _initiateReplication(feedInfo: FeedInfo) {\n log('initiating replication', { feedInfo });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n invariant(!this._streams.has(feedInfo.feedKey), `Replication already in progress for feed: ${feedInfo.feedKey}`);\n const { streamTag } = await this._rpc!.rpc.ReplicatorService.startReplication({ info: feedInfo });\n if (!streamTag) {\n return;\n }\n\n await this._replicateFeed(feedInfo, streamTag);\n }\n\n /**\n * Respond to a remote request to replicate a feed.\n * @returns A stream tag for the replication stream or `undefined` if we don't want to replicate.\n */\n @synchronized\n private async _acceptReplication(feedInfo: FeedInfo): Promise<string | undefined> {\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n if (!this._feeds.has(feedInfo.feedKey) || this._streams.has(feedInfo.feedKey)) {\n return undefined; // We don't have the feed or we are already replicating it.\n }\n\n const tag = `feed-${feedInfo.feedKey.toHex()}-${PublicKey.random().toHex().slice(0, 8)}`; // Generate a unique tag for the stream.\n await this._replicateFeed(feedInfo, tag);\n return tag;\n }\n\n private async _replicateFeed(info: FeedInfo, streamTag: string) {\n log('replicate', { info, streamTag });\n invariant(!this._streams.has(info.feedKey), `Replication already in progress for feed: ${info.feedKey}`);\n\n const feed = this._feeds.get(info.feedKey) ?? failUndefined();\n const networkStream = await this._extensionContext!.createStream(streamTag, {\n contentType: 'application/x-hypercore',\n });\n let replicationStreamErrors = 0;\n\n // https://github.com/holepunchto/hypercore/tree/v9.12.0#var-stream--feedreplicateisinitiator-options\n const replicationStream = feed.replicate(true, {\n live: true,\n upload: info.upload,\n download: info.download,\n noise: false,\n encrypted: false,\n maxRequests: 1024,\n });\n\n // Left for testing.\n // const debug = true;\n // if (debug) {\n // feed.on('sync', () => {\n // log.info('sync', { key: feed.key, length: feed.length });\n // });\n // feed.on('download', (index: number, data: any) => {\n // log.info('download', {\n // key: feed.key,\n // index,\n // length: feed.length,\n // data: data.length,\n // });\n // });\n // }\n\n replicationStream.on('error', (err) => {\n if (err instanceof TimeoutError) {\n log.info('replication stream timeout', { err, info });\n return;\n }\n // TODO(nf): use sentinel errors\n if (\n err?.message === 'Writable stream closed prematurely' ||\n err?.message === 'Cannot call write after a stream was destroyed'\n ) {\n log('replication stream closed', { err, info });\n return;\n }\n\n // TODO(nf): WARN on first error? log full info on some subsequent errors?\n if (replicationStreamErrors === 0) {\n log.info('replication stream error', { err, info });\n } else {\n log.info('replication stream error', { err, feedKey: info.feedKey, count: replicationStreamErrors });\n }\n replicationStreamErrors++;\n });\n\n this._streams.set(info.feedKey, {\n streamTag,\n networkStream,\n replicationStream,\n info,\n });\n\n networkStream.pipe(replicationStream as any).pipe(networkStream);\n }\n\n private async _stopReplication(feedKey: PublicKey) {\n const stream = this._streams.get(feedKey);\n if (!stream) {\n return;\n }\n\n stream.networkStream.destroy();\n this._streams.delete(feedKey);\n }\n}\n\ntype ServiceBundle = {\n ReplicatorService: ReplicatorService;\n};\n\ntype ActiveStream = {\n streamTag: string;\n networkStream: Duplex;\n replicationStream: ProtocolStream;\n info: FeedInfo;\n};\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;AAOA,mBAAyD;AACzD,qBAAwB;AACxB,mBAA8B;AAE9B,uBAA0B;AAC1B,kBAA0B;AAC1B,iBAA6B;AAC7B,uBAA6C;AAC7C,mBAAuB;AAEvB,iBAAsD;AAEtD,kBAA2B;;;;;;;;AASpB,IAAMA,sBAAN,MAAMA;EAAN,cAAA;AACYC,SAAAA,OAAO,IAAIC,uBAAQ;MAClCC,SAAS,CAACC,QAAAA;AACR,aAAKC,mBAAmBC,MAAMF,GAAAA;MAChC;IACF,GAAA;;;;AAEiBG,SAAAA,SAAS,IAAIC,uBAAwCC,sBAAUC,IAAI;AACnEC,SAAAA,WAAW,IAAIH,uBAAoCC,sBAAUC,IAAI;AAK1EE,SAAAA,WAA+B;MACrCC,QAAQ;IACV;AAEiBC,SAAAA,cAAc,IAAIC,0BAAa,KAAKd,MAAM,YAAA;AACzD,UAAI;AACF,YAAI,KAAKI,kBAAmBW,cAAc,OAAO;AAC/C,gBAAM,KAAKC,KAAMC,IAAIC,kBAAkBC,YAAY;YACjDC,OAAOC,MAAMC,KAAK,KAAKhB,OAAOiB,OAAM,CAAA,EAAIC,IAAI,CAACC,UAAU;cACrDC,SAASD,KAAKE;cACdC,UAAU;cACVhB,QAAQ,KAAKD,SAASC;YACxB,EAAA;UACF,CAAA;QACF,WAAW,KAAKR,kBAAmBW,cAAc,MAAM;AACrD,gBAAM,KAAKc,iBAAgB;QAC7B;MACF,SAAS1B,KAAK;AACZ,YAAIA,eAAe2B,iCAAgB;AACjC;QACF;AACA,cAAM3B;MACR;IACF,CAAA;;EAEA,IACY4B,gBAAgB;AAC1B,WAAO;MACLhB,WAAW,KAAKX,mBAAmBW;MACnCiB,aAAa,KAAK5B,mBAAmB4B;MACrCC,cAAc,KAAK7B,mBAAmB6B;MACtCb,OAAOC,MAAMC,KAAK,KAAKhB,OAAO4B,KAAI,CAAA;IACpC;EACF;EAEAC,WAAWC,SAAmC;AAC5C,SAAKzB,WAAWyB;AAChBC,wBAAI,cAAc;MAAED;IAAQ,GAAA;;;;;;AAC5B,QAAI,KAAKhC,mBAAmB;AAC1B,WAAKS,YAAYyB,SAAQ;IAC3B;AACA,WAAO;EACT;EAEAC,QAAQd,
|
|
6
|
-
"names": ["ReplicatorExtension", "_ctx", "Context", "onError", "err", "_extensionContext", "close", "_feeds", "ComplexMap", "PublicKey", "hash", "_streams", "_options", "upload", "_updateTask", "DeferredTask", "initiator", "_rpc", "rpc", "ReplicatorService", "updateFeeds", "feeds", "Array", "from", "values", "map", "feed", "feedKey", "key", "download", "_reevaluateFeeds", "RpcClosedError", "extensionInfo", "localPeerId", "remotePeerId", "keys", "setOptions", "options", "log", "schedule", "addFeed", "set", "onOpen", "context", "createProtoRpcPeer", "requested", "schema", "getService", "exposed", "handlers", "invariant", "startReplication", "info", "streamTag", "_acceptReplication", "stopReplication", "_stopReplication", "port", "createPort", "contentType", "timeout", "open", "onClose", "dispose", "onAbort", "abort", "disposed", "has", "get", "asyncTimeout", "catch", "_initiateReplication", "feedInfo", "_replicateFeed", "undefined", "tag", "toHex", "random", "slice", "failUndefined", "networkStream", "createStream", "replicationStreamErrors", "replicationStream", "replicate", "live", "noise", "encrypted", "maxRequests", "on", "TimeoutError", "message", "count", "pipe", "stream", "destroy", "delete"
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport type { ProtocolStream } from 'hypercore-protocol';\nimport { type Duplex } from 'node:stream';\n\nimport { asyncTimeout, DeferredTask, synchronized } from '@dxos/async';\nimport { Context } from '@dxos/context';\nimport { failUndefined } from '@dxos/debug';\nimport { type FeedWrapper } from '@dxos/feed-store';\nimport { invariant } from '@dxos/invariant';\nimport { PublicKey } from '@dxos/keys';\nimport { log, logInfo } from '@dxos/log';\nimport { RpcClosedError, TimeoutError } from '@dxos/protocols';\nimport { schema } from '@dxos/protocols/proto';\nimport { type FeedInfo, type ReplicatorService } from '@dxos/protocols/proto/dxos/mesh/teleport/replicator';\nimport { createProtoRpcPeer, type ProtoRpcPeer } from '@dxos/rpc';\nimport { type ExtensionContext, type TeleportExtension } from '@dxos/teleport';\nimport { ComplexMap } from '@dxos/util';\n\nexport type ReplicationOptions = {\n upload: boolean;\n};\n\n/**\n * Manages replication between a set of feeds for a single teleport session.\n */\nexport class ReplicatorExtension implements TeleportExtension {\n private readonly _ctx = new Context({\n onError: (err) => {\n this._extensionContext?.close(err);\n },\n });\n\n private readonly _feeds = new ComplexMap<PublicKey, FeedWrapper<any>>(PublicKey.hash);\n private readonly _streams = new ComplexMap<PublicKey, ActiveStream>(PublicKey.hash);\n\n private _rpc?: ProtoRpcPeer<ServiceBundle>;\n private _extensionContext?: ExtensionContext;\n\n private _options: ReplicationOptions = {\n upload: false,\n };\n\n private readonly _updateTask = new DeferredTask(this._ctx, async () => {\n try {\n if (this._extensionContext!.initiator === false) {\n await this._rpc!.rpc.ReplicatorService.updateFeeds({\n feeds: Array.from(this._feeds.values()).map((feed) => ({\n feedKey: feed.key,\n download: true,\n upload: this._options.upload,\n })),\n });\n } else if (this._extensionContext!.initiator === true) {\n await this._reevaluateFeeds();\n }\n } catch (err) {\n if (err instanceof RpcClosedError) {\n return; // Some RPC requests might be pending while closing.\n }\n throw err;\n }\n });\n\n @logInfo\n private get extensionInfo() {\n return {\n initiator: this._extensionContext?.initiator,\n localPeerId: this._extensionContext?.localPeerId,\n remotePeerId: this._extensionContext?.remotePeerId,\n feeds: Array.from(this._feeds.keys()),\n };\n }\n\n setOptions(options: ReplicationOptions): this {\n this._options = options;\n log('setOptions', { options });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n return this;\n }\n\n addFeed(feed: FeedWrapper<any>): void {\n this._feeds.set(feed.key, feed);\n log('addFeed', { feedKey: feed.key });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n }\n\n async onOpen(context: ExtensionContext): Promise<void> {\n this._extensionContext = context;\n log('open');\n\n this._rpc = createProtoRpcPeer<ServiceBundle, ServiceBundle>({\n requested: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n exposed: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n handlers: {\n ReplicatorService: {\n updateFeeds: async ({ feeds }) => {\n log('received feed info', { feeds });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n this._updateTask.schedule();\n },\n startReplication: async ({ info }) => {\n log('starting replication...', { info });\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n const streamTag = await this._acceptReplication(info);\n return {\n streamTag,\n };\n },\n stopReplication: async ({ info }) => {\n log('stopping replication...', { info });\n // TODO(dmaretskyi): Make sure any peer can stop replication.\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n await this._stopReplication(info.feedKey);\n },\n },\n },\n port: await context.createPort('rpc', {\n contentType: 'application/x-protobuf; messageType=\"dxos.rpc.Message\"',\n }),\n timeout: 10_000,\n });\n await this._rpc.open();\n\n this._updateTask.schedule();\n }\n\n async onClose(err?: Error | undefined): Promise<void> {\n log('close', { err });\n await this._ctx.dispose();\n await this._rpc?.close();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n async onAbort(err?: Error | undefined): Promise<void> {\n log('abort', { err });\n\n await this._ctx.dispose();\n await this._rpc?.abort();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n @synchronized\n private async _reevaluateFeeds(): Promise<void> {\n log('_reevaluateFeeds');\n for (const feedKey of this._feeds.keys()) {\n if (this._ctx.disposed) {\n return;\n }\n if (this._streams.has(feedKey) && this._options.upload !== this._streams.get(feedKey)?.info.upload) {\n try {\n await asyncTimeout(this._stopReplication(feedKey), 1000);\n } catch (err) {\n log.catch(err);\n }\n }\n\n if (this._ctx.disposed) {\n return;\n }\n if (!this._streams.has(feedKey)) {\n await this._initiateReplication({\n feedKey,\n download: true,\n upload: this._options.upload,\n });\n }\n }\n }\n\n /**\n * Try to initiate feed replication.\n */\n private async _initiateReplication(feedInfo: FeedInfo): Promise<void> {\n log('initiating replication', { feedInfo });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n invariant(!this._streams.has(feedInfo.feedKey), `Replication already in progress for feed: ${feedInfo.feedKey}`);\n const { streamTag } = await this._rpc!.rpc.ReplicatorService.startReplication({ info: feedInfo });\n if (!streamTag) {\n return;\n }\n\n await this._replicateFeed(feedInfo, streamTag);\n }\n\n /**\n * Respond to a remote request to replicate a feed.\n * @returns A stream tag for the replication stream or `undefined` if we don't want to replicate.\n */\n @synchronized\n private async _acceptReplication(feedInfo: FeedInfo): Promise<string | undefined> {\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n if (!this._feeds.has(feedInfo.feedKey) || this._streams.has(feedInfo.feedKey)) {\n return undefined; // We don't have the feed or we are already replicating it.\n }\n\n const tag = `feed-${feedInfo.feedKey.toHex()}-${PublicKey.random().toHex().slice(0, 8)}`; // Generate a unique tag for the stream.\n await this._replicateFeed(feedInfo, tag);\n return tag;\n }\n\n private async _replicateFeed(info: FeedInfo, streamTag: string): Promise<void> {\n log('replicate', { info, streamTag });\n invariant(!this._streams.has(info.feedKey), `Replication already in progress for feed: ${info.feedKey}`);\n\n const feed = this._feeds.get(info.feedKey) ?? failUndefined();\n const networkStream = await this._extensionContext!.createStream(streamTag, {\n contentType: 'application/x-hypercore',\n });\n let replicationStreamErrors = 0;\n\n // https://github.com/holepunchto/hypercore/tree/v9.12.0#var-stream--feedreplicateisinitiator-options\n const replicationStream = feed.replicate(true, {\n live: true,\n upload: info.upload,\n download: info.download,\n noise: false,\n encrypted: false,\n maxRequests: 1024,\n });\n\n // Left for testing.\n // const debug = true;\n // if (debug) {\n // feed.on('sync', () => {\n // log.info('sync', { key: feed.key, length: feed.length });\n // });\n // feed.on('download', (index: number, data: any) => {\n // log.info('download', {\n // key: feed.key,\n // index,\n // length: feed.length,\n // data: data.length,\n // });\n // });\n // }\n\n replicationStream.on('error', (err) => {\n if (err instanceof TimeoutError) {\n log.info('replication stream timeout', { err, info });\n return;\n }\n // TODO(nf): use sentinel errors\n if (\n err?.message === 'Writable stream closed prematurely' ||\n err?.message === 'Cannot call write after a stream was destroyed'\n ) {\n log('replication stream closed', { err, info });\n return;\n }\n\n // TODO(nf): WARN on first error? log full info on some subsequent errors?\n if (replicationStreamErrors === 0) {\n log.info('replication stream error', { err, info });\n } else {\n log.info('replication stream error', { err, feedKey: info.feedKey, count: replicationStreamErrors });\n }\n replicationStreamErrors++;\n });\n\n this._streams.set(info.feedKey, {\n streamTag,\n networkStream,\n replicationStream,\n info,\n });\n\n networkStream.pipe(replicationStream as any).pipe(networkStream);\n }\n\n private async _stopReplication(feedKey: PublicKey): Promise<void> {\n const stream = this._streams.get(feedKey);\n if (!stream) {\n return;\n }\n\n stream.networkStream.destroy();\n this._streams.delete(feedKey);\n }\n}\n\ntype ServiceBundle = {\n ReplicatorService: ReplicatorService;\n};\n\ntype ActiveStream = {\n streamTag: string;\n networkStream: Duplex;\n replicationStream: ProtocolStream;\n info: FeedInfo;\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["ReplicatorExtension", "_ctx", "Context", "onError", "err", "_extensionContext", "close", "_feeds", "ComplexMap", "PublicKey", "hash", "_streams", "_options", "upload", "_updateTask", "DeferredTask", "initiator", "_rpc", "rpc", "ReplicatorService", "updateFeeds", "feeds", "Array", "from", "values", "map", "feed", "feedKey", "key", "download", "_reevaluateFeeds", "RpcClosedError", "extensionInfo", "localPeerId", "remotePeerId", "keys", "setOptions", "options", "log", "schedule", "addFeed", "set", "onOpen", "context", "createProtoRpcPeer", "requested", "schema", "getService", "exposed", "handlers", "invariant", "startReplication", "info", "streamTag", "_acceptReplication", "stopReplication", "_stopReplication", "port", "createPort", "contentType", "timeout", "open", "onClose", "dispose", "onAbort", "abort", "disposed", "has", "get", "asyncTimeout", "catch", "_initiateReplication", "feedInfo", "_replicateFeed", "undefined", "tag", "toHex", "random", "slice", "failUndefined", "networkStream", "createStream", "replicationStreamErrors", "replicationStream", "replicate", "live", "noise", "encrypted", "maxRequests", "on", "TimeoutError", "message", "count", "pipe", "stream", "destroy", "delete"]
|
|
7
7
|
}
|
package/dist/lib/node/meta.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts":{"bytes":
|
|
1
|
+
{"inputs":{"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts":{"bytes":38844,"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/protocols","kind":"import-statement","external":true},{"path":"@dxos/protocols/proto","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/teleport-extension-replicator/src/index.ts":{"bytes":520,"imports":[{"path":"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts","kind":"import-statement","original":"./replicator-extension"}],"format":"esm"}},"outputs":{"packages/core/mesh/teleport-extension-replicator/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":16528},"packages/core/mesh/teleport-extension-replicator/dist/lib/node/index.cjs":{"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/protocols","kind":"import-statement","external":true},{"path":"@dxos/protocols/proto","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["ReplicatorExtension"],"entryPoint":"packages/core/mesh/teleport-extension-replicator/src/index.ts","inputs":{"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts":{"bytesInOutput":11707},"packages/core/mesh/teleport-extension-replicator/src/index.ts":{"bytesInOutput":0}},"bytes":11856}}}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/replicator-extension.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport type { ProtocolStream } from 'hypercore-protocol';\nimport { type Duplex } from 'node:stream';\n\nimport { asyncTimeout, DeferredTask, synchronized } from '@dxos/async';\nimport { Context } from '@dxos/context';\nimport { failUndefined } from '@dxos/debug';\nimport { type FeedWrapper } from '@dxos/feed-store';\nimport { invariant } from '@dxos/invariant';\nimport { PublicKey } from '@dxos/keys';\nimport { log, logInfo } from '@dxos/log';\nimport { RpcClosedError, TimeoutError } from '@dxos/protocols';\nimport { schema } from '@dxos/protocols/proto';\nimport { type FeedInfo, type ReplicatorService } from '@dxos/protocols/proto/dxos/mesh/teleport/replicator';\nimport { createProtoRpcPeer, type ProtoRpcPeer } from '@dxos/rpc';\nimport { type ExtensionContext, type TeleportExtension } from '@dxos/teleport';\nimport { ComplexMap } from '@dxos/util';\n\nexport type ReplicationOptions = {\n upload: boolean;\n};\n\n/**\n * Manages replication between a set of feeds for a single teleport session.\n */\nexport class ReplicatorExtension implements TeleportExtension {\n private readonly _ctx = new Context({\n onError: (err) => {\n this._extensionContext?.close(err);\n },\n });\n\n private readonly _feeds = new ComplexMap<PublicKey, FeedWrapper<any>>(PublicKey.hash);\n private readonly _streams = new ComplexMap<PublicKey, ActiveStream>(PublicKey.hash);\n\n private _rpc?: ProtoRpcPeer<ServiceBundle>;\n private _extensionContext?: ExtensionContext;\n\n private _options: ReplicationOptions = {\n upload: false,\n };\n\n private readonly _updateTask = new DeferredTask(this._ctx, async () => {\n try {\n if (this._extensionContext!.initiator === false) {\n await this._rpc!.rpc.ReplicatorService.updateFeeds({\n feeds: Array.from(this._feeds.values()).map((feed) => ({\n feedKey: feed.key,\n download: true,\n upload: this._options.upload,\n })),\n });\n } else if (this._extensionContext!.initiator === true) {\n await this._reevaluateFeeds();\n }\n } catch (err) {\n if (err instanceof RpcClosedError) {\n return; // Some RPC requests might be pending while closing.\n }\n throw err;\n }\n });\n\n @logInfo\n private get extensionInfo() {\n return {\n initiator: this._extensionContext?.initiator,\n localPeerId: this._extensionContext?.localPeerId,\n remotePeerId: this._extensionContext?.remotePeerId,\n feeds: Array.from(this._feeds.keys()),\n };\n }\n\n setOptions(options: ReplicationOptions): this {\n this._options = options;\n log('setOptions', { options });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n return this;\n }\n\n addFeed(feed: FeedWrapper<any>) {\n this._feeds.set(feed.key, feed);\n log('addFeed', { feedKey: feed.key });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n }\n\n async onOpen(context: ExtensionContext) {\n this._extensionContext = context;\n log('open');\n\n this._rpc = createProtoRpcPeer<ServiceBundle, ServiceBundle>({\n requested: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n exposed: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n handlers: {\n ReplicatorService: {\n updateFeeds: async ({ feeds }) => {\n log('received feed info', { feeds });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n this._updateTask.schedule();\n },\n startReplication: async ({ info }) => {\n log('starting replication...', { info });\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n const streamTag = await this._acceptReplication(info);\n return {\n streamTag,\n };\n },\n stopReplication: async ({ info }) => {\n log('stopping replication...', { info });\n // TODO(dmaretskyi): Make sure any peer can stop replication.\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n await this._stopReplication(info.feedKey);\n },\n },\n },\n port: await context.createPort('rpc', {\n contentType: 'application/x-protobuf; messageType=\"dxos.rpc.Message\"',\n }),\n timeout: 10_000,\n });\n await this._rpc.open();\n\n this._updateTask.schedule();\n }\n\n async onClose(err?: Error | undefined) {\n log('close', { err });\n await this._ctx.dispose();\n await this._rpc?.close();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n async onAbort(err?: Error | undefined) {\n log('abort', { err });\n\n await this._ctx.dispose();\n await this._rpc?.abort();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n @synchronized\n private async _reevaluateFeeds() {\n log('_reevaluateFeeds');\n for (const feedKey of this._feeds.keys()) {\n if (this._ctx.disposed) {\n return;\n }\n if (this._streams.has(feedKey) && this._options.upload !== this._streams.get(feedKey)?.info.upload) {\n try {\n await asyncTimeout(this._stopReplication(feedKey), 1000);\n } catch (err) {\n log.catch(err);\n }\n }\n\n if (this._ctx.disposed) {\n return;\n }\n if (!this._streams.has(feedKey)) {\n await this._initiateReplication({\n feedKey,\n download: true,\n upload: this._options.upload,\n });\n }\n }\n }\n\n /**\n * Try to initiate feed replication.\n */\n private async _initiateReplication(feedInfo: FeedInfo) {\n log('initiating replication', { feedInfo });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n invariant(!this._streams.has(feedInfo.feedKey), `Replication already in progress for feed: ${feedInfo.feedKey}`);\n const { streamTag } = await this._rpc!.rpc.ReplicatorService.startReplication({ info: feedInfo });\n if (!streamTag) {\n return;\n }\n\n await this._replicateFeed(feedInfo, streamTag);\n }\n\n /**\n * Respond to a remote request to replicate a feed.\n * @returns A stream tag for the replication stream or `undefined` if we don't want to replicate.\n */\n @synchronized\n private async _acceptReplication(feedInfo: FeedInfo): Promise<string | undefined> {\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n if (!this._feeds.has(feedInfo.feedKey) || this._streams.has(feedInfo.feedKey)) {\n return undefined; // We don't have the feed or we are already replicating it.\n }\n\n const tag = `feed-${feedInfo.feedKey.toHex()}-${PublicKey.random().toHex().slice(0, 8)}`; // Generate a unique tag for the stream.\n await this._replicateFeed(feedInfo, tag);\n return tag;\n }\n\n private async _replicateFeed(info: FeedInfo, streamTag: string) {\n log('replicate', { info, streamTag });\n invariant(!this._streams.has(info.feedKey), `Replication already in progress for feed: ${info.feedKey}`);\n\n const feed = this._feeds.get(info.feedKey) ?? failUndefined();\n const networkStream = await this._extensionContext!.createStream(streamTag, {\n contentType: 'application/x-hypercore',\n });\n let replicationStreamErrors = 0;\n\n // https://github.com/holepunchto/hypercore/tree/v9.12.0#var-stream--feedreplicateisinitiator-options\n const replicationStream = feed.replicate(true, {\n live: true,\n upload: info.upload,\n download: info.download,\n noise: false,\n encrypted: false,\n maxRequests: 1024,\n });\n\n // Left for testing.\n // const debug = true;\n // if (debug) {\n // feed.on('sync', () => {\n // log.info('sync', { key: feed.key, length: feed.length });\n // });\n // feed.on('download', (index: number, data: any) => {\n // log.info('download', {\n // key: feed.key,\n // index,\n // length: feed.length,\n // data: data.length,\n // });\n // });\n // }\n\n replicationStream.on('error', (err) => {\n if (err instanceof TimeoutError) {\n log.info('replication stream timeout', { err, info });\n return;\n }\n // TODO(nf): use sentinel errors\n if (\n err?.message === 'Writable stream closed prematurely' ||\n err?.message === 'Cannot call write after a stream was destroyed'\n ) {\n log('replication stream closed', { err, info });\n return;\n }\n\n // TODO(nf): WARN on first error? log full info on some subsequent errors?\n if (replicationStreamErrors === 0) {\n log.info('replication stream error', { err, info });\n } else {\n log.info('replication stream error', { err, feedKey: info.feedKey, count: replicationStreamErrors });\n }\n replicationStreamErrors++;\n });\n\n this._streams.set(info.feedKey, {\n streamTag,\n networkStream,\n replicationStream,\n info,\n });\n\n networkStream.pipe(replicationStream as any).pipe(networkStream);\n }\n\n private async _stopReplication(feedKey: PublicKey) {\n const stream = this._streams.get(feedKey);\n if (!stream) {\n return;\n }\n\n stream.networkStream.destroy();\n this._streams.delete(feedKey);\n }\n}\n\ntype ServiceBundle = {\n ReplicatorService: ReplicatorService;\n};\n\ntype ActiveStream = {\n streamTag: string;\n networkStream: Duplex;\n replicationStream: ProtocolStream;\n info: FeedInfo;\n};\n"],
|
|
5
|
-
"mappings": ";;;AAOA,SAASA,cAAcC,cAAcC,oBAAoB;AACzD,SAASC,eAAe;AACxB,SAASC,qBAAqB;AAE9B,SAASC,iBAAiB;AAC1B,SAASC,iBAAiB;AAC1B,SAASC,KAAKC,eAAe;AAC7B,SAASC,gBAAgBC,oBAAoB;AAC7C,SAASC,cAAc;AAEvB,SAASC,0BAA6C;AAEtD,SAASC,kBAAkB;;;;;;;;AASpB,IAAMC,sBAAN,MAAMA;EAAN;AACYC,gBAAO,IAAIZ,QAAQ;MAClCa,SAAS,CAACC,QAAAA;AACR,aAAKC,mBAAmBC,MAAMF,GAAAA;MAChC;IACF,GAAA;;;;AAEiBG,kBAAS,IAAIP,WAAwCP,UAAUe,IAAI;AACnEC,oBAAW,IAAIT,WAAoCP,UAAUe,IAAI;AAK1EE,oBAA+B;MACrCC,QAAQ;IACV;AAEiBC,uBAAc,IAAIxB,aAAa,KAAKc,MAAM,YAAA;AACzD,UAAI;AACF,YAAI,KAAKG,kBAAmBQ,cAAc,OAAO;AAC/C,gBAAM,KAAKC,KAAMC,IAAIC,kBAAkBC,YAAY;YACjDC,OAAOC,MAAMC,KAAK,KAAKb,OAAOc,OAAM,CAAA,EAAIC,IAAI,CAACC,UAAU;cACrDC,SAASD,KAAKE;cACdC,UAAU;cACVf,QAAQ,KAAKD,SAASC;YACxB,EAAA;UACF,CAAA;QACF,WAAW,KAAKN,kBAAmBQ,cAAc,MAAM;AACrD,gBAAM,KAAKc,iBAAgB;QAC7B;MACF,SAASvB,KAAK;AACZ,YAAIA,eAAeR,gBAAgB;AACjC;QACF;AACA,cAAMQ;MACR;IACF,CAAA;;EAEA,IACYwB,gBAAgB;AAC1B,WAAO;MACLf,WAAW,KAAKR,mBAAmBQ;MACnCgB,aAAa,KAAKxB,mBAAmBwB;MACrCC,cAAc,KAAKzB,mBAAmByB;MACtCZ,OAAOC,MAAMC,KAAK,KAAKb,OAAOwB,KAAI,CAAA;IACpC;EACF;EAEAC,WAAWC,SAAmC;AAC5C,SAAKvB,WAAWuB;AAChBvC,QAAI,cAAc;MAAEuC;IAAQ,GAAA;;;;;;AAC5B,QAAI,KAAK5B,mBAAmB;AAC1B,WAAKO,YAAYsB,SAAQ;IAC3B;AACA,WAAO;EACT;EAEAC,QAAQZ,
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport type { ProtocolStream } from 'hypercore-protocol';\nimport { type Duplex } from 'node:stream';\n\nimport { asyncTimeout, DeferredTask, synchronized } from '@dxos/async';\nimport { Context } from '@dxos/context';\nimport { failUndefined } from '@dxos/debug';\nimport { type FeedWrapper } from '@dxos/feed-store';\nimport { invariant } from '@dxos/invariant';\nimport { PublicKey } from '@dxos/keys';\nimport { log, logInfo } from '@dxos/log';\nimport { RpcClosedError, TimeoutError } from '@dxos/protocols';\nimport { schema } from '@dxos/protocols/proto';\nimport { type FeedInfo, type ReplicatorService } from '@dxos/protocols/proto/dxos/mesh/teleport/replicator';\nimport { createProtoRpcPeer, type ProtoRpcPeer } from '@dxos/rpc';\nimport { type ExtensionContext, type TeleportExtension } from '@dxos/teleport';\nimport { ComplexMap } from '@dxos/util';\n\nexport type ReplicationOptions = {\n upload: boolean;\n};\n\n/**\n * Manages replication between a set of feeds for a single teleport session.\n */\nexport class ReplicatorExtension implements TeleportExtension {\n private readonly _ctx = new Context({\n onError: (err) => {\n this._extensionContext?.close(err);\n },\n });\n\n private readonly _feeds = new ComplexMap<PublicKey, FeedWrapper<any>>(PublicKey.hash);\n private readonly _streams = new ComplexMap<PublicKey, ActiveStream>(PublicKey.hash);\n\n private _rpc?: ProtoRpcPeer<ServiceBundle>;\n private _extensionContext?: ExtensionContext;\n\n private _options: ReplicationOptions = {\n upload: false,\n };\n\n private readonly _updateTask = new DeferredTask(this._ctx, async () => {\n try {\n if (this._extensionContext!.initiator === false) {\n await this._rpc!.rpc.ReplicatorService.updateFeeds({\n feeds: Array.from(this._feeds.values()).map((feed) => ({\n feedKey: feed.key,\n download: true,\n upload: this._options.upload,\n })),\n });\n } else if (this._extensionContext!.initiator === true) {\n await this._reevaluateFeeds();\n }\n } catch (err) {\n if (err instanceof RpcClosedError) {\n return; // Some RPC requests might be pending while closing.\n }\n throw err;\n }\n });\n\n @logInfo\n private get extensionInfo() {\n return {\n initiator: this._extensionContext?.initiator,\n localPeerId: this._extensionContext?.localPeerId,\n remotePeerId: this._extensionContext?.remotePeerId,\n feeds: Array.from(this._feeds.keys()),\n };\n }\n\n setOptions(options: ReplicationOptions): this {\n this._options = options;\n log('setOptions', { options });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n return this;\n }\n\n addFeed(feed: FeedWrapper<any>): void {\n this._feeds.set(feed.key, feed);\n log('addFeed', { feedKey: feed.key });\n if (this._extensionContext) {\n this._updateTask.schedule();\n }\n }\n\n async onOpen(context: ExtensionContext): Promise<void> {\n this._extensionContext = context;\n log('open');\n\n this._rpc = createProtoRpcPeer<ServiceBundle, ServiceBundle>({\n requested: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n exposed: {\n ReplicatorService: schema.getService('dxos.mesh.teleport.replicator.ReplicatorService'),\n },\n handlers: {\n ReplicatorService: {\n updateFeeds: async ({ feeds }) => {\n log('received feed info', { feeds });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n this._updateTask.schedule();\n },\n startReplication: async ({ info }) => {\n log('starting replication...', { info });\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n const streamTag = await this._acceptReplication(info);\n return {\n streamTag,\n };\n },\n stopReplication: async ({ info }) => {\n log('stopping replication...', { info });\n // TODO(dmaretskyi): Make sure any peer can stop replication.\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n await this._stopReplication(info.feedKey);\n },\n },\n },\n port: await context.createPort('rpc', {\n contentType: 'application/x-protobuf; messageType=\"dxos.rpc.Message\"',\n }),\n timeout: 10_000,\n });\n await this._rpc.open();\n\n this._updateTask.schedule();\n }\n\n async onClose(err?: Error | undefined): Promise<void> {\n log('close', { err });\n await this._ctx.dispose();\n await this._rpc?.close();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n async onAbort(err?: Error | undefined): Promise<void> {\n log('abort', { err });\n\n await this._ctx.dispose();\n await this._rpc?.abort();\n for (const feedKey of this._streams.keys()) {\n await this._stopReplication(feedKey);\n }\n }\n\n @synchronized\n private async _reevaluateFeeds(): Promise<void> {\n log('_reevaluateFeeds');\n for (const feedKey of this._feeds.keys()) {\n if (this._ctx.disposed) {\n return;\n }\n if (this._streams.has(feedKey) && this._options.upload !== this._streams.get(feedKey)?.info.upload) {\n try {\n await asyncTimeout(this._stopReplication(feedKey), 1000);\n } catch (err) {\n log.catch(err);\n }\n }\n\n if (this._ctx.disposed) {\n return;\n }\n if (!this._streams.has(feedKey)) {\n await this._initiateReplication({\n feedKey,\n download: true,\n upload: this._options.upload,\n });\n }\n }\n }\n\n /**\n * Try to initiate feed replication.\n */\n private async _initiateReplication(feedInfo: FeedInfo): Promise<void> {\n log('initiating replication', { feedInfo });\n invariant(this._extensionContext!.initiator === true, 'Invalid call');\n invariant(!this._streams.has(feedInfo.feedKey), `Replication already in progress for feed: ${feedInfo.feedKey}`);\n const { streamTag } = await this._rpc!.rpc.ReplicatorService.startReplication({ info: feedInfo });\n if (!streamTag) {\n return;\n }\n\n await this._replicateFeed(feedInfo, streamTag);\n }\n\n /**\n * Respond to a remote request to replicate a feed.\n * @returns A stream tag for the replication stream or `undefined` if we don't want to replicate.\n */\n @synchronized\n private async _acceptReplication(feedInfo: FeedInfo): Promise<string | undefined> {\n invariant(this._extensionContext!.initiator === false, 'Invalid call');\n\n if (!this._feeds.has(feedInfo.feedKey) || this._streams.has(feedInfo.feedKey)) {\n return undefined; // We don't have the feed or we are already replicating it.\n }\n\n const tag = `feed-${feedInfo.feedKey.toHex()}-${PublicKey.random().toHex().slice(0, 8)}`; // Generate a unique tag for the stream.\n await this._replicateFeed(feedInfo, tag);\n return tag;\n }\n\n private async _replicateFeed(info: FeedInfo, streamTag: string): Promise<void> {\n log('replicate', { info, streamTag });\n invariant(!this._streams.has(info.feedKey), `Replication already in progress for feed: ${info.feedKey}`);\n\n const feed = this._feeds.get(info.feedKey) ?? failUndefined();\n const networkStream = await this._extensionContext!.createStream(streamTag, {\n contentType: 'application/x-hypercore',\n });\n let replicationStreamErrors = 0;\n\n // https://github.com/holepunchto/hypercore/tree/v9.12.0#var-stream--feedreplicateisinitiator-options\n const replicationStream = feed.replicate(true, {\n live: true,\n upload: info.upload,\n download: info.download,\n noise: false,\n encrypted: false,\n maxRequests: 1024,\n });\n\n // Left for testing.\n // const debug = true;\n // if (debug) {\n // feed.on('sync', () => {\n // log.info('sync', { key: feed.key, length: feed.length });\n // });\n // feed.on('download', (index: number, data: any) => {\n // log.info('download', {\n // key: feed.key,\n // index,\n // length: feed.length,\n // data: data.length,\n // });\n // });\n // }\n\n replicationStream.on('error', (err) => {\n if (err instanceof TimeoutError) {\n log.info('replication stream timeout', { err, info });\n return;\n }\n // TODO(nf): use sentinel errors\n if (\n err?.message === 'Writable stream closed prematurely' ||\n err?.message === 'Cannot call write after a stream was destroyed'\n ) {\n log('replication stream closed', { err, info });\n return;\n }\n\n // TODO(nf): WARN on first error? log full info on some subsequent errors?\n if (replicationStreamErrors === 0) {\n log.info('replication stream error', { err, info });\n } else {\n log.info('replication stream error', { err, feedKey: info.feedKey, count: replicationStreamErrors });\n }\n replicationStreamErrors++;\n });\n\n this._streams.set(info.feedKey, {\n streamTag,\n networkStream,\n replicationStream,\n info,\n });\n\n networkStream.pipe(replicationStream as any).pipe(networkStream);\n }\n\n private async _stopReplication(feedKey: PublicKey): Promise<void> {\n const stream = this._streams.get(feedKey);\n if (!stream) {\n return;\n }\n\n stream.networkStream.destroy();\n this._streams.delete(feedKey);\n }\n}\n\ntype ServiceBundle = {\n ReplicatorService: ReplicatorService;\n};\n\ntype ActiveStream = {\n streamTag: string;\n networkStream: Duplex;\n replicationStream: ProtocolStream;\n info: FeedInfo;\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["asyncTimeout", "DeferredTask", "synchronized", "Context", "failUndefined", "invariant", "PublicKey", "log", "logInfo", "RpcClosedError", "TimeoutError", "schema", "createProtoRpcPeer", "ComplexMap", "ReplicatorExtension", "_ctx", "onError", "err", "_extensionContext", "close", "_feeds", "hash", "_streams", "_options", "upload", "_updateTask", "initiator", "_rpc", "rpc", "ReplicatorService", "updateFeeds", "feeds", "Array", "from", "values", "map", "feed", "feedKey", "key", "download", "_reevaluateFeeds", "extensionInfo", "localPeerId", "remotePeerId", "keys", "setOptions", "options", "schedule", "addFeed", "set", "onOpen", "context", "requested", "getService", "exposed", "handlers", "startReplication", "info", "streamTag", "_acceptReplication", "stopReplication", "_stopReplication", "port", "createPort", "contentType", "timeout", "open", "onClose", "dispose", "onAbort", "abort", "disposed", "has", "get", "catch", "_initiateReplication", "feedInfo", "_replicateFeed", "undefined", "tag", "toHex", "random", "slice", "networkStream", "createStream", "replicationStreamErrors", "replicationStream", "replicate", "live", "noise", "encrypted", "maxRequests", "on", "message", "count", "pipe", "stream", "destroy", "delete"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts":{"bytes":
|
|
1
|
+
{"inputs":{"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts":{"bytes":38844,"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/protocols","kind":"import-statement","external":true},{"path":"@dxos/protocols/proto","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/teleport-extension-replicator/src/index.ts":{"bytes":520,"imports":[{"path":"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts","kind":"import-statement","original":"./replicator-extension"}],"format":"esm"}},"outputs":{"packages/core/mesh/teleport-extension-replicator/dist/lib/node-esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":16530},"packages/core/mesh/teleport-extension-replicator/dist/lib/node-esm/index.mjs":{"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/debug","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/protocols","kind":"import-statement","external":true},{"path":"@dxos/protocols/proto","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["ReplicatorExtension"],"entryPoint":"packages/core/mesh/teleport-extension-replicator/src/index.ts","inputs":{"packages/core/mesh/teleport-extension-replicator/src/replicator-extension.ts":{"bytesInOutput":11707},"packages/core/mesh/teleport-extension-replicator/src/index.ts":{"bytesInOutput":0}},"bytes":11949}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replicator-extension.d.ts","sourceRoot":"","sources":["../../../src/replicator-extension.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQpD,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAG/E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,qBAAa,mBAAoB,YAAW,iBAAiB;IAC3D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAIlB;IAEH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+D;IACtF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2D;IAEpF,OAAO,CAAC,IAAI,CAAC,CAA8B;IAC3C,OAAO,CAAC,iBAAiB,CAAC,CAAmB;IAE7C,OAAO,CAAC,QAAQ,CAEd;IAEF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAmBzB;IAGH,OAAO,KAAK,aAAa,GAOxB;IAED,UAAU,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAS7C,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"replicator-extension.d.ts","sourceRoot":"","sources":["../../../src/replicator-extension.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQpD,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAG/E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,qBAAa,mBAAoB,YAAW,iBAAiB;IAC3D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAIlB;IAEH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+D;IACtF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2D;IAEpF,OAAO,CAAC,IAAI,CAAC,CAA8B;IAC3C,OAAO,CAAC,iBAAiB,CAAC,CAAmB;IAE7C,OAAO,CAAC,QAAQ,CAEd;IAEF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAmBzB;IAGH,OAAO,KAAK,aAAa,GAOxB;IAED,UAAU,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAS7C,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI;IAQ/B,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA8ChD,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/C,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;YAWvC,gBAAgB;IA2B9B;;OAEG;YACW,oBAAoB;IAYlC;;;OAGG;YAEW,kBAAkB;YAYlB,cAAc;YAqEd,gBAAgB;CAS/B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"5.
|
|
1
|
+
{"version":"5.8.3"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/teleport-extension-replicator",
|
|
3
|
-
"version": "0.8.2
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Replicator extension for teleport.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -24,23 +24,23 @@
|
|
|
24
24
|
"src"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@dxos/async": "0.8.2
|
|
28
|
-
"@dxos/context": "0.8.2
|
|
29
|
-
"@dxos/debug": "0.8.2
|
|
30
|
-
"@dxos/feed-store": "0.8.2
|
|
31
|
-
"@dxos/
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/node-std": "0.8.2
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
27
|
+
"@dxos/async": "0.8.2",
|
|
28
|
+
"@dxos/context": "0.8.2",
|
|
29
|
+
"@dxos/debug": "0.8.2",
|
|
30
|
+
"@dxos/feed-store": "0.8.2",
|
|
31
|
+
"@dxos/invariant": "0.8.2",
|
|
32
|
+
"@dxos/keys": "0.8.2",
|
|
33
|
+
"@dxos/log": "0.8.2",
|
|
34
|
+
"@dxos/node-std": "0.8.2",
|
|
35
|
+
"@dxos/protocols": "0.8.2",
|
|
36
|
+
"@dxos/util": "0.8.2",
|
|
37
|
+
"@dxos/teleport": "0.8.2",
|
|
38
|
+
"@dxos/rpc": "0.8.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"fast-check": "^3.19.0",
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/
|
|
42
|
+
"@dxos/keyring": "0.8.2",
|
|
43
|
+
"@dxos/random-access-storage": "0.8.2"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|
|
@@ -83,7 +83,7 @@ export class ReplicatorExtension implements TeleportExtension {
|
|
|
83
83
|
return this;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
addFeed(feed: FeedWrapper<any>) {
|
|
86
|
+
addFeed(feed: FeedWrapper<any>): void {
|
|
87
87
|
this._feeds.set(feed.key, feed);
|
|
88
88
|
log('addFeed', { feedKey: feed.key });
|
|
89
89
|
if (this._extensionContext) {
|
|
@@ -91,7 +91,7 @@ export class ReplicatorExtension implements TeleportExtension {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
async onOpen(context: ExtensionContext) {
|
|
94
|
+
async onOpen(context: ExtensionContext): Promise<void> {
|
|
95
95
|
this._extensionContext = context;
|
|
96
96
|
log('open');
|
|
97
97
|
|
|
@@ -137,7 +137,7 @@ export class ReplicatorExtension implements TeleportExtension {
|
|
|
137
137
|
this._updateTask.schedule();
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
async onClose(err?: Error | undefined) {
|
|
140
|
+
async onClose(err?: Error | undefined): Promise<void> {
|
|
141
141
|
log('close', { err });
|
|
142
142
|
await this._ctx.dispose();
|
|
143
143
|
await this._rpc?.close();
|
|
@@ -146,7 +146,7 @@ export class ReplicatorExtension implements TeleportExtension {
|
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
async onAbort(err?: Error | undefined) {
|
|
149
|
+
async onAbort(err?: Error | undefined): Promise<void> {
|
|
150
150
|
log('abort', { err });
|
|
151
151
|
|
|
152
152
|
await this._ctx.dispose();
|
|
@@ -157,7 +157,7 @@ export class ReplicatorExtension implements TeleportExtension {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
@synchronized
|
|
160
|
-
private async _reevaluateFeeds() {
|
|
160
|
+
private async _reevaluateFeeds(): Promise<void> {
|
|
161
161
|
log('_reevaluateFeeds');
|
|
162
162
|
for (const feedKey of this._feeds.keys()) {
|
|
163
163
|
if (this._ctx.disposed) {
|
|
@@ -187,7 +187,7 @@ export class ReplicatorExtension implements TeleportExtension {
|
|
|
187
187
|
/**
|
|
188
188
|
* Try to initiate feed replication.
|
|
189
189
|
*/
|
|
190
|
-
private async _initiateReplication(feedInfo: FeedInfo) {
|
|
190
|
+
private async _initiateReplication(feedInfo: FeedInfo): Promise<void> {
|
|
191
191
|
log('initiating replication', { feedInfo });
|
|
192
192
|
invariant(this._extensionContext!.initiator === true, 'Invalid call');
|
|
193
193
|
invariant(!this._streams.has(feedInfo.feedKey), `Replication already in progress for feed: ${feedInfo.feedKey}`);
|
|
@@ -216,7 +216,7 @@ export class ReplicatorExtension implements TeleportExtension {
|
|
|
216
216
|
return tag;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
private async _replicateFeed(info: FeedInfo, streamTag: string) {
|
|
219
|
+
private async _replicateFeed(info: FeedInfo, streamTag: string): Promise<void> {
|
|
220
220
|
log('replicate', { info, streamTag });
|
|
221
221
|
invariant(!this._streams.has(info.feedKey), `Replication already in progress for feed: ${info.feedKey}`);
|
|
222
222
|
|
|
@@ -285,7 +285,7 @@ export class ReplicatorExtension implements TeleportExtension {
|
|
|
285
285
|
networkStream.pipe(replicationStream as any).pipe(networkStream);
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
private async _stopReplication(feedKey: PublicKey) {
|
|
288
|
+
private async _stopReplication(feedKey: PublicKey): Promise<void> {
|
|
289
289
|
const stream = this._streams.get(feedKey);
|
|
290
290
|
if (!stream) {
|
|
291
291
|
return;
|