@trap_stevo/filetide 0.0.84 → 0.0.86
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/SCL-1.0-Universal.md +228 -0
- package/dist/cjs/FileMessagerClient.js +138 -71
- package/dist/cjs/FileTide.js +165 -83
- package/dist/cjs/{FileMessager.js → FileTideAnchor.js} +444 -264
- package/dist/cjs/HUDComponents/FileTidePathsConfigurations.js +57 -0
- package/dist/cjs/HUDManagers/EndpointManager.js +157 -0
- package/dist/cjs/HUDManagers/FileMessagerConfigManager.js +2 -0
- package/dist/cjs/HUDManagers/{FileNetClientManager.js → FileTideAnchorClientManager.js} +3 -2
- package/dist/cjs/HUDManagers/{FileNetUtilityManager.js → FileTideAnchorUtilityManager.js} +2 -2
- package/dist/cjs/HUDManagers/FileTideDebugUtilityManager.js +6 -0
- package/dist/cjs/HUDManagers/FileTideNetworkTrafficManager.js +419 -0
- package/dist/cjs/HUDManagers/FileTideUniversalPathsUtilityManager.js +14 -0
- package/dist/cjs/HUDManagers/FileTransferManager.js +90 -157
- package/dist/cjs/HUDManagers/FileTransferReceiverManager.js +169 -9
- package/dist/cjs/HUDManagers/FileTransferStatsManager.js +138 -0
- package/dist/cjs/HUDManagers/FileTransferTrackingManager.js +22 -0
- package/dist/cjs/HUDManagers/FileUtilityManager.js +2 -1
- package/dist/cjs/HUDManagers/TidalyticsInstanceManager.js +2 -1
- package/dist/cjs/HUDManagers/TideSentinelManager.js +8 -7
- package/dist/cjs/HUDManagers/TideTokenEnforcementManager.js +2 -1
- package/package.json +4 -5
- package/LICENSE.md +0 -79
- package/dist/cjs/HUDComponents/ConsoleProgressBarManager.js +0 -288
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var EventEmitter = require('events').EventEmitter;
|
|
4
|
+
var EVENTS = Object.freeze({
|
|
5
|
+
NETWORK_AVAILABLE: 'network:available',
|
|
6
|
+
NETWORK_CONGESTED: 'network:congested',
|
|
7
|
+
NETWORK_UNSTABLE: 'network:unstable',
|
|
8
|
+
TRANSFER_PAUSE: 'transfer:pause',
|
|
9
|
+
TRANSFER_RESUME: 'transfer:resume',
|
|
10
|
+
CHUNK_SEND: 'chunk:send',
|
|
11
|
+
CHUNK_DELAY: 'chunk:delay',
|
|
12
|
+
CHUNK_RESIZE: 'chunk:resize',
|
|
13
|
+
BANDWIDTH_UPDATED: 'bandwidth:updated'
|
|
14
|
+
});
|
|
15
|
+
var STATES = Object.freeze({
|
|
16
|
+
UNKNOWN: 'unknown',
|
|
17
|
+
AVAILABLE: 'available',
|
|
18
|
+
CONGESTED: 'congested',
|
|
19
|
+
UNSTABLE: 'unstable',
|
|
20
|
+
OFFLINE: 'offline'
|
|
21
|
+
});
|
|
22
|
+
var DEFAULTS = Object.freeze({
|
|
23
|
+
defaultBandwidthBps: 1024 * 1024,
|
|
24
|
+
minimumBandwidthBps: 32 * 1024,
|
|
25
|
+
maximumBandwidthBps: 1024 * 1024 * 1024,
|
|
26
|
+
targetUtilization: 0.9,
|
|
27
|
+
congestedUtilizationFactor: 0.65,
|
|
28
|
+
targetChunkDurationMs: 250,
|
|
29
|
+
minChunkSize: 16 * 1024,
|
|
30
|
+
maxChunkSize: 1024 * 1024,
|
|
31
|
+
burstSeconds: 0.25,
|
|
32
|
+
congestedLatencyMs: 250,
|
|
33
|
+
unstableLatencyMs: 1000,
|
|
34
|
+
congestedFailureRate: 0.1,
|
|
35
|
+
unstableFailureRate: 0.3,
|
|
36
|
+
sampleWindowSize: 20,
|
|
37
|
+
smoothingFactor: 0.25,
|
|
38
|
+
throughputProbeIntervalMs: 2000,
|
|
39
|
+
throughputProbeGrowthFactor: 1.10,
|
|
40
|
+
healthyThroughputRatio: 0.90,
|
|
41
|
+
degradedThroughputRatio: 0.65
|
|
42
|
+
});
|
|
43
|
+
function clamp(value, minimum, maximum) {
|
|
44
|
+
return Math.min(Math.max(value, minimum), maximum);
|
|
45
|
+
}
|
|
46
|
+
function assertPositiveNumber(value, name) {
|
|
47
|
+
if (typeof value !== 'number' || !isFinite(value) || value <= 0) {
|
|
48
|
+
throw new TypeError(name + " expects a positive number");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function mergeOptions(options) {
|
|
52
|
+
var merged = {};
|
|
53
|
+
var key;
|
|
54
|
+
for (key in DEFAULTS) {
|
|
55
|
+
if (Object.prototype.hasOwnProperty.call(DEFAULTS, key)) {
|
|
56
|
+
merged[key] = DEFAULTS[key];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
options = options || {};
|
|
60
|
+
for (key in options) {
|
|
61
|
+
if (key !== 'clock' && Object.prototype.hasOwnProperty.call(options, key)) {
|
|
62
|
+
merged[key] = options[key];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return merged;
|
|
66
|
+
}
|
|
67
|
+
function FileTideNetworkTrafficManager(options) {
|
|
68
|
+
EventEmitter.call(this);
|
|
69
|
+
options = options || {};
|
|
70
|
+
this._config = mergeOptions(options);
|
|
71
|
+
this._clock = typeof options.clock === 'function' ? options.clock : Date.now;
|
|
72
|
+
this._state = STATES.UNKNOWN;
|
|
73
|
+
this._paused = false;
|
|
74
|
+
this._manualPause = false;
|
|
75
|
+
this._autoPaused = false;
|
|
76
|
+
this._pauseReason = null;
|
|
77
|
+
this._bandwidthBps = null;
|
|
78
|
+
this._deliveredThroughputBps = null;
|
|
79
|
+
this._latencyMs = null;
|
|
80
|
+
this._samples = [];
|
|
81
|
+
this._lastThroughputProbeAt = 0;
|
|
82
|
+
this._tokens = this._getBucketCapacity();
|
|
83
|
+
this._lastRefillAt = this._clock();
|
|
84
|
+
this._validateConfiguration();
|
|
85
|
+
}
|
|
86
|
+
FileTideNetworkTrafficManager.prototype = Object.create(EventEmitter.prototype);
|
|
87
|
+
FileTideNetworkTrafficManager.prototype.constructor = FileTideNetworkTrafficManager;
|
|
88
|
+
FileTideNetworkTrafficManager.prototype._validateConfiguration = function () {
|
|
89
|
+
assertPositiveNumber(this._config.defaultBandwidthBps, "defaultBandwidthBps");
|
|
90
|
+
assertPositiveNumber(this._config.minimumBandwidthBps, "minimumBandwidthBps");
|
|
91
|
+
assertPositiveNumber(this._config.targetChunkDurationMs, "targetChunkDurationMs");
|
|
92
|
+
assertPositiveNumber(this._config.minChunkSize, 'minChunkSize');
|
|
93
|
+
assertPositiveNumber(this._config.maxChunkSize, 'maxChunkSize');
|
|
94
|
+
assertPositiveNumber(this._config.burstSeconds, 'burstSeconds');
|
|
95
|
+
assertPositiveNumber(this._config.sampleWindowSize, 'sampleWindowSize');
|
|
96
|
+
if (this._config.targetUtilization <= 0 || this._config.targetUtilization > 1) {
|
|
97
|
+
throw new RangeError("targetUtilization expects greater than 0 and at most 1");
|
|
98
|
+
}
|
|
99
|
+
if (this._config.congestedUtilizationFactor <= 0 || this._config.congestedUtilizationFactor > 1) {
|
|
100
|
+
throw new RangeError("congestedUtilizationFactor expects greater than 0 and at most 1");
|
|
101
|
+
}
|
|
102
|
+
if (this._config.smoothingFactor <= 0 || this._config.smoothingFactor > 1) {
|
|
103
|
+
throw new RangeError("smoothingFactor expects greater than 0 and at most 1");
|
|
104
|
+
}
|
|
105
|
+
if (this._config.minChunkSize > this._config.maxChunkSize) {
|
|
106
|
+
throw new RangeError("minChunkSize cannot exceed maxChunkSize");
|
|
107
|
+
}
|
|
108
|
+
assertPositiveNumber(this._config.maximumBandwidthBps, "maximumBandwidthBps");
|
|
109
|
+
assertPositiveNumber(this._config.throughputProbeIntervalMs, "throughputProbeIntervalMs");
|
|
110
|
+
if (this._config.throughputProbeGrowthFactor <= 1) {
|
|
111
|
+
throw new RangeError("throughputProbeGrowthFactor expects greater than 1");
|
|
112
|
+
}
|
|
113
|
+
if (this._config.healthyThroughputRatio <= 0 || this._config.healthyThroughputRatio > 1) {
|
|
114
|
+
throw new RangeError("healthyThroughputRatio expects greater than 0 and at most 1");
|
|
115
|
+
}
|
|
116
|
+
if (this._config.degradedThroughputRatio <= 0 || this._config.degradedThroughputRatio >= this._config.healthyThroughputRatio) {
|
|
117
|
+
throw new RangeError("degradedThroughputRatio expects greater than 0 and less than healthyThroughputRatio");
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
FileTideNetworkTrafficManager.prototype._smooth = function (current, sample) {
|
|
121
|
+
if (current === null) {
|
|
122
|
+
return sample;
|
|
123
|
+
}
|
|
124
|
+
return this._config.smoothingFactor * sample + (1 - this._config.smoothingFactor) * current;
|
|
125
|
+
};
|
|
126
|
+
FileTideNetworkTrafficManager.prototype._getFailureRate = function () {
|
|
127
|
+
if (this._samples.length === 0) {
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
var failures = this._samples.reduce(function (total, sample) {
|
|
131
|
+
return total + (sample.success ? 0 : 1);
|
|
132
|
+
}, 0);
|
|
133
|
+
return failures / this._samples.length;
|
|
134
|
+
};
|
|
135
|
+
FileTideNetworkTrafficManager.prototype._getRetryRate = function () {
|
|
136
|
+
if (this._samples.length === 0) {
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
var retries = this._samples.reduce(function (total, sample) {
|
|
140
|
+
return total + (sample.retried ? 1 : 0);
|
|
141
|
+
}, 0);
|
|
142
|
+
return retries / this._samples.length;
|
|
143
|
+
};
|
|
144
|
+
FileTideNetworkTrafficManager.prototype._evaluateState = function (online) {
|
|
145
|
+
var failureRate = this._getFailureRate();
|
|
146
|
+
var retryRate = this._getRetryRate();
|
|
147
|
+
if (online === false) {
|
|
148
|
+
return STATES.OFFLINE;
|
|
149
|
+
}
|
|
150
|
+
if (this._latencyMs !== null && this._latencyMs >= this._config.unstableLatencyMs) {
|
|
151
|
+
return STATES.UNSTABLE;
|
|
152
|
+
}
|
|
153
|
+
if (failureRate >= this._config.unstableFailureRate) {
|
|
154
|
+
return STATES.UNSTABLE;
|
|
155
|
+
}
|
|
156
|
+
if (this._latencyMs !== null && this._latencyMs >= this._config.congestedLatencyMs) {
|
|
157
|
+
return STATES.CONGESTED;
|
|
158
|
+
}
|
|
159
|
+
if (failureRate >= this._config.congestedFailureRate || retryRate >= this._config.congestedFailureRate) {
|
|
160
|
+
return STATES.CONGESTED;
|
|
161
|
+
}
|
|
162
|
+
return STATES.AVAILABLE;
|
|
163
|
+
};
|
|
164
|
+
FileTideNetworkTrafficManager.prototype._setState = function (nextState) {
|
|
165
|
+
if (nextState === this._state) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
var previousState = this._state;
|
|
169
|
+
this._state = nextState;
|
|
170
|
+
var eventPayload = {
|
|
171
|
+
state: nextState,
|
|
172
|
+
previousState: previousState,
|
|
173
|
+
status: this.getStatus()
|
|
174
|
+
};
|
|
175
|
+
if (nextState === STATES.AVAILABLE) {
|
|
176
|
+
this.emit(EVENTS.NETWORK_AVAILABLE, eventPayload);
|
|
177
|
+
if (this._autoPaused && !this._manualPause) {
|
|
178
|
+
this.resume('network-recovered', true);
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (nextState === STATES.CONGESTED) {
|
|
183
|
+
this.emit(EVENTS.NETWORK_CONGESTED, eventPayload);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
this.emit(EVENTS.NETWORK_UNSTABLE, eventPayload);
|
|
187
|
+
this.pause(nextState === STATES.OFFLINE ? 'network-offline' : 'network-unstable', true);
|
|
188
|
+
};
|
|
189
|
+
FileTideNetworkTrafficManager.prototype.updateNetworkSample = function (sample) {
|
|
190
|
+
sample = sample || {};
|
|
191
|
+
if (sample.bandwidthBps !== undefined) {
|
|
192
|
+
assertPositiveNumber(sample.bandwidthBps, 'bandwidthBps');
|
|
193
|
+
this._refillTokens();
|
|
194
|
+
var previousCapacity = this._getBucketCapacity();
|
|
195
|
+
var fillRatio = clamp(this._tokens / previousCapacity, 0, 1);
|
|
196
|
+
this._bandwidthBps = this._smooth(this._bandwidthBps, sample.bandwidthBps);
|
|
197
|
+
this._tokens = fillRatio * this._getBucketCapacity();
|
|
198
|
+
this.emit(EVENTS.BANDWIDTH_UPDATED, {
|
|
199
|
+
bandwidthBps: this._bandwidthBps,
|
|
200
|
+
status: this.getStatus()
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (sample.latencyMs !== undefined) {
|
|
204
|
+
if (typeof sample.latencyMs !== 'number' || !isFinite(sample.latencyMs) || sample.latencyMs < 0) {
|
|
205
|
+
throw new TypeError("latencyMs expects a non-negative number");
|
|
206
|
+
}
|
|
207
|
+
this._latencyMs = this._smooth(this._latencyMs, sample.latencyMs);
|
|
208
|
+
}
|
|
209
|
+
if (sample.success !== undefined || sample.retried !== undefined) {
|
|
210
|
+
this._samples.push({
|
|
211
|
+
success: sample.success !== false,
|
|
212
|
+
retried: sample.retried === true,
|
|
213
|
+
timestamp: this._clock()
|
|
214
|
+
});
|
|
215
|
+
if (this._samples.length > this._config.sampleWindowSize) {
|
|
216
|
+
this._samples.shift();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
this._setState(this._evaluateState(sample.online));
|
|
220
|
+
return this.getStatus();
|
|
221
|
+
};
|
|
222
|
+
FileTideNetworkTrafficManager.prototype.recordTransfer = function (result) {
|
|
223
|
+
result = result || {};
|
|
224
|
+
assertPositiveNumber(result.bytes, 'bytes');
|
|
225
|
+
assertPositiveNumber(result.durationMs, 'durationMs');
|
|
226
|
+
return this.updateNetworkSample({
|
|
227
|
+
bandwidthBps: result.bytes / (result.durationMs / 1000),
|
|
228
|
+
latencyMs: result.latencyMs,
|
|
229
|
+
online: result.online,
|
|
230
|
+
success: result.success,
|
|
231
|
+
retried: result.retried
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
FileTideNetworkTrafficManager.prototype.recordDeliveredThroughput = function (throughputBps) {
|
|
235
|
+
assertPositiveNumber(throughputBps, "throughputBps");
|
|
236
|
+
var now = this._clock();
|
|
237
|
+
this._deliveredThroughputBps = this._smooth(this._deliveredThroughputBps, throughputBps);
|
|
238
|
+
this._setState(this._evaluateState(true));
|
|
239
|
+
var currentBandwidth = this._bandwidthBps || this._config.defaultBandwidthBps;
|
|
240
|
+
var currentSendRate = this._getSendRate();
|
|
241
|
+
var throughputRatio = currentSendRate > 0 ? this._deliveredThroughputBps / currentSendRate : 0;
|
|
242
|
+
if (now - this._lastThroughputProbeAt < this._config.throughputProbeIntervalMs) {
|
|
243
|
+
return this.getStatus();
|
|
244
|
+
}
|
|
245
|
+
this._lastThroughputProbeAt = now;
|
|
246
|
+
var nextBandwidth = currentBandwidth;
|
|
247
|
+
if (this._state === STATES.AVAILABLE && throughputRatio >= this._config.healthyThroughputRatio) {
|
|
248
|
+
nextBandwidth = Math.min(currentBandwidth * this._config.throughputProbeGrowthFactor, this._config.maximumBandwidthBps);
|
|
249
|
+
} else if (throughputRatio < this._config.degradedThroughputRatio) {
|
|
250
|
+
var observedCapacity = this._deliveredThroughputBps / this._config.targetUtilization;
|
|
251
|
+
nextBandwidth = this._smooth(currentBandwidth, observedCapacity);
|
|
252
|
+
nextBandwidth = Math.max(nextBandwidth, this._config.minimumBandwidthBps);
|
|
253
|
+
}
|
|
254
|
+
nextBandwidth = Math.min(nextBandwidth, this._config.maximumBandwidthBps);
|
|
255
|
+
if (nextBandwidth !== currentBandwidth) {
|
|
256
|
+
this._refillTokens();
|
|
257
|
+
var previousCapacity = this._getBucketCapacity();
|
|
258
|
+
var fillRatio = clamp(this._tokens / previousCapacity, 0, 1);
|
|
259
|
+
this._bandwidthBps = nextBandwidth;
|
|
260
|
+
this._tokens = fillRatio * this._getBucketCapacity();
|
|
261
|
+
this.emit(EVENTS.BANDWIDTH_UPDATED, {
|
|
262
|
+
bandwidthBps: this._bandwidthBps,
|
|
263
|
+
deliveredThroughputBps: this._deliveredThroughputBps,
|
|
264
|
+
throughputRatio,
|
|
265
|
+
status: this.getStatus()
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return this.getStatus();
|
|
269
|
+
};
|
|
270
|
+
FileTideNetworkTrafficManager.prototype.waitForChunkAdmission = function (requestedBytes) {
|
|
271
|
+
var self = this;
|
|
272
|
+
assertPositiveNumber(requestedBytes, "requestedBytes");
|
|
273
|
+
return new Promise(function (resolve) {
|
|
274
|
+
function evaluate() {
|
|
275
|
+
var decision = self.getChunkDecision(requestedBytes);
|
|
276
|
+
if (decision.action === "send") {
|
|
277
|
+
resolve(decision);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (decision.action === "pause") {
|
|
281
|
+
self.once(EVENTS.TRANSFER_RESUME, evaluate);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
setTimeout(evaluate, Math.max(decision.delayMs || 1, 1));
|
|
285
|
+
}
|
|
286
|
+
evaluate();
|
|
287
|
+
});
|
|
288
|
+
};
|
|
289
|
+
FileTideNetworkTrafficManager.prototype.pause = function (reason, automatic) {
|
|
290
|
+
if (automatic !== true) {
|
|
291
|
+
this._manualPause = true;
|
|
292
|
+
} else {
|
|
293
|
+
this._autoPaused = true;
|
|
294
|
+
}
|
|
295
|
+
this._pauseReason = reason || 'requested';
|
|
296
|
+
if (this._paused) {
|
|
297
|
+
return this.getStatus();
|
|
298
|
+
}
|
|
299
|
+
this._paused = true;
|
|
300
|
+
this.emit(EVENTS.TRANSFER_PAUSE, {
|
|
301
|
+
reason: this._pauseReason,
|
|
302
|
+
automatic: automatic === true,
|
|
303
|
+
status: this.getStatus()
|
|
304
|
+
});
|
|
305
|
+
return this.getStatus();
|
|
306
|
+
};
|
|
307
|
+
FileTideNetworkTrafficManager.prototype.resume = function (reason, automatic) {
|
|
308
|
+
if (automatic === true && this._manualPause) {
|
|
309
|
+
return this.getStatus();
|
|
310
|
+
}
|
|
311
|
+
if (automatic !== true) {
|
|
312
|
+
this._manualPause = false;
|
|
313
|
+
}
|
|
314
|
+
this._autoPaused = false;
|
|
315
|
+
this._pauseReason = null;
|
|
316
|
+
if (!this._paused) {
|
|
317
|
+
return this.getStatus();
|
|
318
|
+
}
|
|
319
|
+
this._paused = false;
|
|
320
|
+
this._lastRefillAt = this._clock();
|
|
321
|
+
this.emit(EVENTS.TRANSFER_RESUME, {
|
|
322
|
+
reason: reason || 'requested',
|
|
323
|
+
automatic: automatic === true,
|
|
324
|
+
status: this.getStatus()
|
|
325
|
+
});
|
|
326
|
+
return this.getStatus();
|
|
327
|
+
};
|
|
328
|
+
FileTideNetworkTrafficManager.prototype._getSendRate = function () {
|
|
329
|
+
var bandwidth = this._bandwidthBps || this._config.defaultBandwidthBps;
|
|
330
|
+
var rate = Math.max(bandwidth * this._config.targetUtilization, this._config.minimumBandwidthBps);
|
|
331
|
+
if (this._state === STATES.CONGESTED) {
|
|
332
|
+
rate *= this._config.congestedUtilizationFactor;
|
|
333
|
+
}
|
|
334
|
+
return rate;
|
|
335
|
+
};
|
|
336
|
+
FileTideNetworkTrafficManager.prototype._getBucketCapacity = function () {
|
|
337
|
+
var bandwidth = this._bandwidthBps || this._config.defaultBandwidthBps;
|
|
338
|
+
return Math.max(bandwidth * this._config.burstSeconds, this._config.maxChunkSize);
|
|
339
|
+
};
|
|
340
|
+
FileTideNetworkTrafficManager.prototype._refillTokens = function () {
|
|
341
|
+
var now = this._clock();
|
|
342
|
+
var elapsedMs = Math.max(now - this._lastRefillAt, 0);
|
|
343
|
+
var addedTokens = this._getSendRate() * (elapsedMs / 1000);
|
|
344
|
+
this._tokens = Math.min(this._tokens + addedTokens, this._getBucketCapacity());
|
|
345
|
+
this._lastRefillAt = now;
|
|
346
|
+
};
|
|
347
|
+
FileTideNetworkTrafficManager.prototype.getRecommendedChunkSize = function () {
|
|
348
|
+
var targetSize = this._getSendRate() * (this._config.targetChunkDurationMs / 1000);
|
|
349
|
+
return Math.round(clamp(targetSize, this._config.minChunkSize, this._config.maxChunkSize));
|
|
350
|
+
};
|
|
351
|
+
FileTideNetworkTrafficManager.prototype.getChunkDecision = function (requestedBytes) {
|
|
352
|
+
assertPositiveNumber(requestedBytes, 'requestedBytes');
|
|
353
|
+
if (this._paused) {
|
|
354
|
+
return {
|
|
355
|
+
action: 'pause',
|
|
356
|
+
allowed: false,
|
|
357
|
+
reason: this._pauseReason,
|
|
358
|
+
delayMs: null,
|
|
359
|
+
chunkSize: 0
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
this._refillTokens();
|
|
363
|
+
var recommendedSize = this.getRecommendedChunkSize();
|
|
364
|
+
var chunkSize = Math.min(requestedBytes, recommendedSize);
|
|
365
|
+
if (chunkSize !== requestedBytes) {
|
|
366
|
+
this.emit(EVENTS.CHUNK_RESIZE, {
|
|
367
|
+
requestedBytes: requestedBytes,
|
|
368
|
+
chunkSize: chunkSize,
|
|
369
|
+
status: this.getStatus()
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
if (this._tokens >= chunkSize) {
|
|
373
|
+
this._tokens -= chunkSize;
|
|
374
|
+
var sendDecision = {
|
|
375
|
+
action: 'send',
|
|
376
|
+
allowed: true,
|
|
377
|
+
reason: 'capacity-available',
|
|
378
|
+
delayMs: 0,
|
|
379
|
+
chunkSize: chunkSize
|
|
380
|
+
};
|
|
381
|
+
this.emit(EVENTS.CHUNK_SEND, sendDecision);
|
|
382
|
+
return sendDecision;
|
|
383
|
+
}
|
|
384
|
+
var delayMs = Math.ceil((chunkSize - this._tokens) / this._getSendRate() * 1000);
|
|
385
|
+
var delayDecision = {
|
|
386
|
+
action: 'delay',
|
|
387
|
+
allowed: false,
|
|
388
|
+
reason: 'bandwidth-throttled',
|
|
389
|
+
delayMs: Math.max(delayMs, 1),
|
|
390
|
+
chunkSize: chunkSize
|
|
391
|
+
};
|
|
392
|
+
this.emit(EVENTS.CHUNK_DELAY, delayDecision);
|
|
393
|
+
return delayDecision;
|
|
394
|
+
};
|
|
395
|
+
FileTideNetworkTrafficManager.prototype.canSendChunk = function (requestedBytes) {
|
|
396
|
+
return this.getChunkDecision(requestedBytes).allowed;
|
|
397
|
+
};
|
|
398
|
+
FileTideNetworkTrafficManager.prototype.getStatus = function () {
|
|
399
|
+
var sendRateBps = this._getSendRate();
|
|
400
|
+
return {
|
|
401
|
+
state: this._state,
|
|
402
|
+
paused: this._paused,
|
|
403
|
+
pauseReason: this._pauseReason,
|
|
404
|
+
bandwidthBps: this._bandwidthBps,
|
|
405
|
+
deliveredThroughputBps: this._deliveredThroughputBps,
|
|
406
|
+
latencyMs: this._latencyMs,
|
|
407
|
+
failureRate: this._getFailureRate(),
|
|
408
|
+
retryRate: this._getRetryRate(),
|
|
409
|
+
recommendedChunkSize: this.getRecommendedChunkSize(),
|
|
410
|
+
sendRateBps,
|
|
411
|
+
throughputRatio: this._deliveredThroughputBps !== null && sendRateBps > 0 ? this._deliveredThroughputBps / sendRateBps : null,
|
|
412
|
+
sampleCount: this._samples.length
|
|
413
|
+
};
|
|
414
|
+
};
|
|
415
|
+
var singleton = new FileTideNetworkTrafficManager();
|
|
416
|
+
module.exports = singleton;
|
|
417
|
+
module.exports.FileTideNetworkTrafficManager = FileTideNetworkTrafficManager;
|
|
418
|
+
module.exports.EVENTS = EVENTS;
|
|
419
|
+
module.exports.STATES = STATES;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require("path");
|
|
4
|
+
function getUniversalPrefix() {
|
|
5
|
+
if (process.platform === "win32") {
|
|
6
|
+
const winAppDataRoot = process.env.PROGRAMDATA || process.env.APPDATA || path.join(process.env.SYSTEMDRIVE || "C:", "ProgramData");
|
|
7
|
+
return path.join(winAppDataRoot);
|
|
8
|
+
}
|
|
9
|
+
return path.join("/var", "lib");
|
|
10
|
+
}
|
|
11
|
+
;
|
|
12
|
+
module.exports = {
|
|
13
|
+
getUniversalPrefix
|
|
14
|
+
};
|