teyyare 0.2.2 → 0.2.3

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/bin/teyyare.js CHANGED
@@ -50,6 +50,11 @@ async function main() {
50
50
  process.exit(0);
51
51
  }
52
52
 
53
+ if (command === '--version' || command === '-v' || command === 'version') {
54
+ console.log('0.2.3');
55
+ process.exit(0);
56
+ }
57
+
53
58
  if (command === 'serve') {
54
59
  let port = 7421;
55
60
  let dir = process.cwd();
@@ -105,6 +110,7 @@ Backpressure: ${snap.backpressureCount}
105
110
  Protocol overhead:${snap.protocolOverheadPercent.toFixed(2)}%
106
111
  Peak RSS: ${(snap.peakRSS / (1024 * 1024)).toFixed(2)} MB
107
112
  `);
113
+ process.exit(0);
108
114
  } else if (command === 'pull') {
109
115
  const source = args[1];
110
116
  const target = args[2] || './';
@@ -135,8 +141,9 @@ Backpressure: ${snap.backpressureCount}
135
141
  Protocol overhead:${snap.protocolOverheadPercent.toFixed(2)}%
136
142
  Peak RSS: ${(snap.peakRSS / (1024 * 1024)).toFixed(2)} MB
137
143
  `);
144
+ process.exit(0);
138
145
  } else if (command === 'status') {
139
- console.log('\x1b[1m\x1b[36mTeyyare\x1b[0m v0.2.2 status: ready');
146
+ console.log('\x1b[1m\x1b[36mTeyyare\x1b[0m v0.2.3 status: ready');
140
147
  } else {
141
148
  console.error(`Unknown command: ${command}`);
142
149
  printUsage();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teyyare",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "High-performance resumable file transfer engine and CLI built on muttafa and raptiye",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -5,6 +5,7 @@
5
5
  import path from 'node:path';
6
6
  import { existsSync } from 'node:fs';
7
7
  import { TCPTransport, ReplicationPipeline } from 'raptiye';
8
+ import { readHeader, decodeMessage, HEADER_SIZE } from 'raptiye/src/protocol/wire.js';
8
9
  import { SenderPipeline } from './sender/pipeline.js';
9
10
  import { ReceiverPipeline } from './receiver/pipeline.js';
10
11
  import { TransferManifest } from './manifest/manifest.js';
@@ -22,6 +23,116 @@ import {
22
23
  unpackPullError
23
24
  } from './core/protocol.js';
24
25
 
26
+ // High-performance streaming TCP framer patch for TCPTransport
27
+ // Eliminates O(N^2) Buffer.concat and ensures clean socket lifecycle management
28
+ TCPTransport.prototype._attachSocketReader = function(socket) {
29
+ socket.setNoDelay(true);
30
+ const chunks = [];
31
+ let bufferedLength = 0;
32
+
33
+ socket.on('data', (chunk) => {
34
+ chunks.push(chunk);
35
+ bufferedLength += chunk.length;
36
+
37
+ while (bufferedLength >= HEADER_SIZE) {
38
+ let headerBuf = chunks[0];
39
+ if (headerBuf.length < HEADER_SIZE) {
40
+ const tempHeader = Buffer.allocUnsafe(HEADER_SIZE);
41
+ let off = 0;
42
+ for (let i = 0; off < HEADER_SIZE && i < chunks.length; i++) {
43
+ const c = chunks[i];
44
+ const toCopy = Math.min(c.length, HEADER_SIZE - off);
45
+ tempHeader.set(c.subarray(0, toCopy), off);
46
+ off += toCopy;
47
+ }
48
+ headerBuf = tempHeader;
49
+ }
50
+
51
+ let header;
52
+ try {
53
+ header = readHeader(headerBuf, 0);
54
+ } catch (err) {
55
+ socket.destroy(err);
56
+ return;
57
+ }
58
+
59
+ const totalLength = HEADER_SIZE + header.payloadLength;
60
+ if (bufferedLength < totalLength) {
61
+ break;
62
+ }
63
+
64
+ let rawFrame;
65
+ if (chunks[0].length >= totalLength) {
66
+ rawFrame = chunks[0].subarray(0, totalLength);
67
+ if (chunks[0].length === totalLength) {
68
+ chunks.shift();
69
+ } else {
70
+ chunks[0] = chunks[0].subarray(totalLength);
71
+ }
72
+ bufferedLength -= totalLength;
73
+ } else {
74
+ rawFrame = Buffer.allocUnsafe(totalLength);
75
+ let dstOff = 0;
76
+ let remaining = totalLength;
77
+ while (remaining > 0) {
78
+ const head = chunks[0];
79
+ if (head.length <= remaining) {
80
+ rawFrame.set(head, dstOff);
81
+ dstOff += head.length;
82
+ remaining -= head.length;
83
+ chunks.shift();
84
+ } else {
85
+ rawFrame.set(head.subarray(0, remaining), dstOff);
86
+ dstOff += remaining;
87
+ chunks[0] = head.subarray(remaining);
88
+ remaining = 0;
89
+ }
90
+ }
91
+ bufferedLength -= totalLength;
92
+ }
93
+
94
+ try {
95
+ const frame = new Uint8Array(rawFrame.buffer, rawFrame.byteOffset, rawFrame.byteLength);
96
+ const msg = decodeMessage(frame, 0, true);
97
+ if (msg.sourceNode) {
98
+ this._outboundSockets.set(msg.sourceNode, socket);
99
+ }
100
+ if (this._messageHandler) {
101
+ this._messageHandler(msg.sourceNode, msg);
102
+ }
103
+ } catch (err) {
104
+ console.error(`[TCPTransport ${this.id}] Error decoding frame:`, err.message);
105
+ }
106
+ }
107
+ });
108
+
109
+ socket.on('close', () => {
110
+ this._inboundSockets.delete(socket);
111
+ for (const [peer, s] of this._outboundSockets.entries()) {
112
+ if (s === socket) {
113
+ this._outboundSockets.delete(peer);
114
+ }
115
+ }
116
+ });
117
+
118
+ socket.on('error', () => {
119
+ socket.destroy();
120
+ });
121
+ };
122
+
123
+ // Reset stalled inflight receipts on connection drop or before starting new transfer
124
+ ReplicationPipeline.prototype.reset = function(err = new Error('Replication pipeline reset')) {
125
+ for (const pending of this._pendingReceipts.values()) {
126
+ pending.reject(err);
127
+ }
128
+ this._pendingReceipts.clear();
129
+ this.inflightBatches = 0;
130
+ this.inflightBytes = 0;
131
+ this.stats.inflightBatches = 0;
132
+ this.stats.inflightBytes = 0;
133
+ this._notifyCapacity();
134
+ };
135
+
25
136
  export {
26
137
  SenderPipeline,
27
138
  ReceiverPipeline,
@@ -108,6 +219,7 @@ export async function serve({
108
219
  }
109
220
 
110
221
  const manifest = await TransferManifest.build(resolved);
222
+ replication.reset();
111
223
  const sender = new SenderPipeline({
112
224
  replication,
113
225
  manifest,
@@ -102,10 +102,10 @@ export class SenderPipeline {
102
102
  this.replication = replication;
103
103
  this.manifest = manifest;
104
104
  this.chunkSize = options.chunkSize || 1024 * 1024;
105
- this.maxEntries = options.maxEntries || 8;
106
- this.maxBytes = options.maxBytes || 8 * 1024 * 1024;
105
+ this.maxEntries = options.maxEntries || 4;
106
+ this.maxBytes = options.maxBytes || 4 * 1024 * 1024;
107
107
  this.maxImmutableBuffers = options.maxImmutableBuffers || 8;
108
- this.maxConcurrentFlushes = options.maxConcurrentFlushes || 4;
108
+ this.maxConcurrentFlushes = options.maxConcurrentFlushes || Math.min(this.maxImmutableBuffers, 4);
109
109
  this.metrics = metrics;
110
110
  this.progress = progress;
111
111
  this.knownResumeState = knownResumeState;