| … | … | @@ -16,8 +16,21 @@ module.exports = class HyperSync extends ReadyResource { |
| 16 | 16 | super(); |
| 17 | 17 | |
| 18 | 18 | this._reposDir = opts.repos || path.join(require('os').homedir(), 'git-repos'); |
| 19 | | this._swarm = opts.swarm || null; |
| 20 | | this._ownSwarm = !this._swarm; |
| 19 | |
| 20 | // OFFLINE mode: `swarm: false` means NO NETWORK — do not inject one, do |
| 21 | // not create one. Distinct from `swarm: null/undefined`, which still means |
| 22 | // "make your own". Without this distinction a caller had no way to say |
| 23 | // "scan and serve locally, but never touch the DHT": `_open()` below |
| 24 | // creates a Hyperswarm whenever none was injected, so passing null (as |
| 25 | // pear-git-v3 did when its own swarm was disabled) silently produced a |
| 26 | // live DHT-connected swarm that pulled from real peers. |
| 27 | // |
| 28 | // Offline keeps everything local intact — scan(), the repos map, info(), |
| 29 | // _repoPath, handleInfoRefs — so HTTP git serving still works. Only |
| 30 | // announce/join/connect is suppressed. |
| 31 | this._offline = opts.swarm === false; |
| 32 | this._swarm = this._offline ? null : (opts.swarm || null); |
| 33 | this._ownSwarm = !this._offline && !this._swarm; |
| 21 | 34 | this._keypair = opts.keypair || null; |
| 22 | 35 | |
| 23 | 36 | // Optional push authorization hook. git-upload-pack (read) is always |
| … | … | @@ -46,12 +59,14 @@ module.exports = class HyperSync extends ReadyResource { |
| 46 | 59 | this.repos = scanRepos(this._reposDir); |
| 47 | 60 | this.emit('scan', this.repos); |
| 48 | 61 | |
| 49 | | // Create swarm if not injected |
| 50 | | if (!this._swarm) { |
| 62 | // Create swarm if not injected — unless explicitly offline. |
| 63 | if (!this._swarm && !this._offline) { |
| 51 | 64 | this._swarm = new Hyperswarm({ keyPair: this._keypair }); |
| 52 | 65 | } |
| 53 | 66 | |
| 54 | | this._swarm.on('connection', (conn, info) => this._onConnection(conn, info)); |
| 67 | if (this._swarm) { |
| 68 | this._swarm.on('connection', (conn, info) => this._onConnection(conn, info)); |
| 69 | } |
| 55 | 70 | } |
| 56 | 71 | |
| 57 | 72 | async _close () { |
| … | … | @@ -138,7 +153,7 @@ module.exports = class HyperSync extends ReadyResource { |
| 138 | 153 | } |
| 139 | 154 | |
| 140 | 155 | // Single flush for all topics |
| 141 | | await this._swarm.flush(); |
| 156 | if (this._swarm) await this._swarm.flush(); |
| 142 | 157 | this._serving = true; |
| 143 | 158 | this.emit('serving', this._topics.size); |
| 144 | 159 | } |
| … | … | @@ -150,17 +165,24 @@ module.exports = class HyperSync extends ReadyResource { |
| 150 | 165 | if (!repo) throw new Error('Repo not found: ' + name); |
| 151 | 166 | |
| 152 | 167 | const topic = deriveTopic(this._keypair.publicKey, name); |
| 153 | | const discovery = this._swarm.join(topic, { server: true, client: false }); |
| 168 | |
| 169 | // Offline: derive + record the topic (callers read it, and `serve` events |
| 170 | // drive downstream indexes) but never ANNOUNCE it. No discovery handle. |
| 171 | const discovery = this._offline |
| 172 | ? null |
| 173 | : this._swarm.join(topic, { server: true, client: false }); |
| 154 | 174 | |
| 155 | 175 | this._topics.set(name, { topic, discovery, name }); |
| 156 | 176 | this.emit('serve', name, topic.toString('hex')); |
| 157 | 177 | } |
| 158 | 178 | |
| 159 | 179 | async unserve (name) { |
| 180 | // `discovery` is null in offline mode (nothing was announced, so there is |
| 181 | // nothing to unannounce) — guard rather than relying on a thrown TypeError. |
| 160 | 182 | if (name) { |
| 161 | 183 | const entry = this._topics.get(name); |
| 162 | 184 | if (entry) { |
| 163 | | await entry.discovery.destroy(); |
| 185 | if (entry.discovery) await entry.discovery.destroy(); |
| 164 | 186 | this._topics.delete(name); |
| 165 | 187 | } |
| 166 | 188 | return; |
| … | … | @@ -168,7 +190,7 @@ module.exports = class HyperSync extends ReadyResource { |
| 168 | 190 | |
| 169 | 191 | // Unserve all |
| 170 | 192 | for (const [n, entry] of this._topics) { |
| 171 | | await entry.discovery.destroy(); |
| 193 | if (entry.discovery) await entry.discovery.destroy(); |
| 172 | 194 | } |
| 173 | 195 | this._topics.clear(); |
| 174 | 196 | this._serving = false; |