recker 1.0.19 → 1.0.20-next.963881c

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.
@@ -1,225 +1,395 @@
1
1
  import { createWriteStream } from 'node:fs';
2
+ import { mkdir } from 'node:fs/promises';
2
3
  import { dirname, join } from 'node:path';
3
- import { mkdir, readdir, readFile, rm } from 'node:fs/promises';
4
- function parseM3u8(content, baseUrl) {
4
+ function parseAttributes(line, prefix) {
5
+ const attrs = {};
6
+ const content = line.substring(prefix.length);
7
+ const regex = /([A-Z0-9-]+)=(?:"([^"]*)"|([^,]*))/g;
8
+ let match;
9
+ while ((match = regex.exec(content)) !== null) {
10
+ attrs[match[1]] = match[2] ?? match[3];
11
+ }
12
+ return attrs;
13
+ }
14
+ function resolveUrl(url, baseUrl) {
15
+ if (url.startsWith('http://') || url.startsWith('https://')) {
16
+ return url;
17
+ }
18
+ try {
19
+ const base = new URL(baseUrl);
20
+ const basePath = base.pathname.substring(0, base.pathname.lastIndexOf('/') + 1);
21
+ return new URL(url, base.origin + basePath).toString();
22
+ }
23
+ catch {
24
+ return url;
25
+ }
26
+ }
27
+ function parseMasterPlaylist(content, baseUrl) {
28
+ const lines = content.split('\n');
29
+ const variants = [];
30
+ let pendingVariant = {};
31
+ for (const rawLine of lines) {
32
+ const line = rawLine.trim();
33
+ if (!line)
34
+ continue;
35
+ if (line.startsWith('#EXT-X-STREAM-INF:')) {
36
+ const attrs = parseAttributes(line, '#EXT-X-STREAM-INF:');
37
+ pendingVariant = {
38
+ bandwidth: attrs.BANDWIDTH ? parseInt(attrs.BANDWIDTH, 10) : undefined,
39
+ resolution: attrs.RESOLUTION,
40
+ codecs: attrs.CODECS,
41
+ name: attrs.NAME,
42
+ };
43
+ }
44
+ else if (!line.startsWith('#') && pendingVariant.bandwidth !== undefined) {
45
+ variants.push({
46
+ ...pendingVariant,
47
+ url: resolveUrl(line, baseUrl),
48
+ });
49
+ pendingVariant = {};
50
+ }
51
+ }
52
+ return { variants, isMaster: true };
53
+ }
54
+ function parseMediaPlaylist(content, baseUrl) {
5
55
  const lines = content.split('\n');
6
56
  const segments = [];
7
- let currentDuration = 0;
8
- let mediaSequence = 0;
9
57
  let targetDuration = 5;
58
+ let mediaSequence = 0;
59
+ let discontinuitySequence = 0;
10
60
  let endList = false;
61
+ let playlistType;
62
+ let currentDuration = 0;
11
63
  let currentKey;
12
- for (let i = 0; i < lines.length; i++) {
13
- const line = lines[i].trim();
64
+ let currentDiscontinuity = false;
65
+ let currentProgramDateTime;
66
+ let segmentIndex = 0;
67
+ for (const rawLine of lines) {
68
+ const line = rawLine.trim();
14
69
  if (!line)
15
70
  continue;
16
71
  if (line.startsWith('#EXT-X-TARGETDURATION:')) {
17
- targetDuration = parseFloat(line.split(':')[1]);
72
+ targetDuration = parseInt(line.split(':')[1], 10);
18
73
  }
19
74
  else if (line.startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
20
75
  mediaSequence = parseInt(line.split(':')[1], 10);
21
76
  }
22
- else if (line.startsWith('#EXT-X-ENDLIST')) {
77
+ else if (line.startsWith('#EXT-X-DISCONTINUITY-SEQUENCE:')) {
78
+ discontinuitySequence = parseInt(line.split(':')[1], 10);
79
+ }
80
+ else if (line.startsWith('#EXT-X-PLAYLIST-TYPE:')) {
81
+ playlistType = line.split(':')[1];
82
+ }
83
+ else if (line === '#EXT-X-ENDLIST') {
23
84
  endList = true;
24
85
  }
86
+ else if (line === '#EXT-X-DISCONTINUITY') {
87
+ currentDiscontinuity = true;
88
+ }
89
+ else if (line.startsWith('#EXT-X-PROGRAM-DATE-TIME:')) {
90
+ currentProgramDateTime = new Date(line.split(':').slice(1).join(':'));
91
+ }
25
92
  else if (line.startsWith('#EXT-X-KEY:')) {
26
- const attrs = line.substring(11);
27
- const methodMatch = attrs.match(/METHOD=([^,]+)/);
28
- const uriMatch = attrs.match(/URI="([^"]+)"/);
29
- const ivMatch = attrs.match(/IV=([^,]+)/);
30
- if (methodMatch) {
93
+ const attrs = parseAttributes(line, '#EXT-X-KEY:');
94
+ if (attrs.METHOD === 'NONE') {
95
+ currentKey = undefined;
96
+ }
97
+ else {
31
98
  currentKey = {
32
- method: methodMatch[1],
33
- uri: uriMatch ? uriMatch[1] : undefined,
34
- iv: ivMatch ? ivMatch[1] : undefined
99
+ method: attrs.METHOD,
100
+ uri: attrs.URI ? resolveUrl(attrs.URI, baseUrl) : undefined,
101
+ iv: attrs.IV,
35
102
  };
36
103
  }
37
104
  }
38
105
  else if (line.startsWith('#EXTINF:')) {
39
- const durationStr = line.substring(8).split(',', 1)[0];
106
+ const durationStr = line.substring(8).split(',')[0];
40
107
  currentDuration = parseFloat(durationStr);
41
108
  }
42
109
  else if (!line.startsWith('#')) {
43
- let url = line;
44
- if (!url.startsWith('http')) {
45
- try {
46
- const base = new URL(baseUrl);
47
- const basePath = base.pathname.substring(0, base.pathname.lastIndexOf('/') + 1);
48
- url = new URL(url, base.origin + basePath).toString();
49
- }
50
- catch {
51
- }
52
- }
53
110
  segments.push({
54
- url,
111
+ url: resolveUrl(line, baseUrl),
55
112
  duration: currentDuration,
56
- sequence: 0,
57
- key: currentKey
113
+ sequence: mediaSequence + segmentIndex,
114
+ key: currentKey,
115
+ discontinuity: currentDiscontinuity,
116
+ programDateTime: currentProgramDateTime,
58
117
  });
118
+ segmentIndex++;
119
+ currentDiscontinuity = false;
120
+ currentProgramDateTime = undefined;
59
121
  }
60
122
  }
61
- segments.forEach((seg, idx) => {
62
- seg.sequence = mediaSequence + idx;
63
- });
64
- return { segments, targetDuration, endList, mediaSequence };
123
+ return {
124
+ segments,
125
+ targetDuration,
126
+ mediaSequence,
127
+ endList,
128
+ playlistType,
129
+ discontinuitySequence,
130
+ };
65
131
  }
66
- function parseVariants(content, baseUrl) {
67
- const lines = content.split('\n');
68
- const variants = [];
69
- let currentBandwidth;
70
- let currentResolution;
71
- for (let i = 0; i < lines.length; i++) {
72
- const line = lines[i].trim();
73
- if (!line)
74
- continue;
75
- if (line.startsWith('#EXT-X-STREAM-INF:')) {
76
- const bwMatch = line.match(/BANDWIDTH=(\d+)/);
77
- const resMatch = line.match(/RESOLUTION=(\d+x\d+)/);
78
- if (bwMatch)
79
- currentBandwidth = parseInt(bwMatch[1], 10);
80
- if (resMatch)
81
- currentResolution = resMatch[1];
82
- }
83
- else if (!line.startsWith('#')) {
84
- let url = line;
85
- if (!url.startsWith('http')) {
86
- try {
87
- const base = new URL(baseUrl);
88
- const basePath = base.pathname.substring(0, base.pathname.lastIndexOf('/') + 1);
89
- url = new URL(url, base.origin + basePath).toString();
90
- }
91
- catch { }
92
- }
93
- variants.push({
94
- url,
95
- bandwidth: currentBandwidth,
96
- resolution: currentResolution
97
- });
98
- }
132
+ function isMasterPlaylist(content) {
133
+ return content.includes('#EXT-X-STREAM-INF');
134
+ }
135
+ function selectVariant(variants, quality) {
136
+ if (!variants.length) {
137
+ throw new Error('No variants found in master playlist');
138
+ }
139
+ const sorted = [...variants].sort((a, b) => (a.bandwidth ?? 0) - (b.bandwidth ?? 0));
140
+ if (quality === 'lowest') {
141
+ return sorted[0];
99
142
  }
100
- return variants;
143
+ if (quality === 'highest' || quality === undefined) {
144
+ return sorted[sorted.length - 1];
145
+ }
146
+ if (quality.resolution) {
147
+ const match = variants.find((v) => v.resolution === quality.resolution);
148
+ if (match)
149
+ return match;
150
+ }
151
+ if (quality.bandwidth) {
152
+ const target = quality.bandwidth;
153
+ return sorted.reduce((prev, curr) => {
154
+ const prevDiff = Math.abs((prev.bandwidth ?? 0) - target);
155
+ const currDiff = Math.abs((curr.bandwidth ?? 0) - target);
156
+ return currDiff < prevDiff ? curr : prev;
157
+ });
158
+ }
159
+ return sorted[sorted.length - 1];
101
160
  }
102
- export async function downloadHls(client, manifestUrl, outputPath, options = {}) {
103
- const merge = options.merge !== false;
104
- const concurrency = options.concurrency || 5;
105
- const isLive = options.live === true;
106
- const maxDuration = options.duration || (isLive ? Infinity : 0);
107
- const info = options.onInfo || (() => { });
108
- const error = options.onError || ((err) => { throw err; });
109
- const seenSegments = new Set();
110
- const outputDir = merge ? dirname(outputPath) : outputPath;
111
- await mkdir(outputDir, { recursive: true });
112
- const tempDir = join(outputDir, `.tmp_${Date.now()}_${Math.random().toString(36).slice(2)}`);
113
- if (merge) {
114
- await mkdir(tempDir, { recursive: true });
115
- }
116
- let currentManifestUrl = manifestUrl;
117
- try {
118
- let initialManifest = await client.get(currentManifestUrl).text();
119
- if (options.onManifest) {
120
- const modified = options.onManifest(initialManifest, currentManifestUrl);
121
- if (typeof modified === 'string')
122
- initialManifest = modified;
123
- }
124
- if (initialManifest.includes('#EXT-X-STREAM-INF')) {
125
- const variants = parseVariants(initialManifest, currentManifestUrl);
126
- if (variants.length > 0) {
127
- let selected = variants[variants.length - 1];
128
- if (options.onVariantSelected) {
129
- const userSelected = options.onVariantSelected(variants, selected);
130
- if (userSelected)
131
- selected = userSelected;
132
- }
133
- currentManifestUrl = selected.url;
134
- info(`Master playlist detected. Switching to variant: ${currentManifestUrl}`);
135
- }
161
+ export class HlsPromise {
162
+ client;
163
+ manifestUrl;
164
+ options;
165
+ seenSequences = new Set();
166
+ downloadedBytes = 0;
167
+ downloadedSegments = 0;
168
+ startTime = 0;
169
+ aborted = false;
170
+ abortController = new AbortController();
171
+ constructor(client, manifestUrl, options = {}) {
172
+ this.client = client;
173
+ this.manifestUrl = manifestUrl;
174
+ this.options = {
175
+ mode: 'merge',
176
+ format: 'ts',
177
+ concurrency: 5,
178
+ ...options,
179
+ };
180
+ if (this.options.format !== 'ts') {
181
+ throw new Error(`Format '${this.options.format}' requires ffmpeg. Use format: 'ts' or install ffmpeg.`);
136
182
  }
137
183
  }
138
- catch (err) {
139
- error(new Error(`Failed to fetch initial manifest: ${err}`));
140
- return;
184
+ get [Symbol.toStringTag]() {
185
+ return 'HlsPromise';
141
186
  }
142
- const { RequestRunner } = await import('../runner/request-runner.js');
143
- const runner = new RequestRunner({ concurrency });
144
- const startTime = Date.now();
145
- let recording = true;
146
- while (recording) {
147
- try {
148
- let content = await client.get(currentManifestUrl).text();
149
- if (options.onManifest) {
150
- const modified = options.onManifest(content, currentManifestUrl);
151
- if (typeof modified === 'string')
152
- content = modified;
187
+ then(onfulfilled, onrejected) {
188
+ return Promise.reject(new Error('HlsPromise requires .download(), .stream(), or .pipe() to execute')).then(onfulfilled, onrejected);
189
+ }
190
+ catch(onrejected) {
191
+ return this.then(null, onrejected);
192
+ }
193
+ finally(onfinally) {
194
+ return this.then(() => {
195
+ onfinally?.();
196
+ }, () => {
197
+ onfinally?.();
198
+ });
199
+ }
200
+ cancel() {
201
+ this.aborted = true;
202
+ this.abortController.abort();
203
+ }
204
+ async download(dest) {
205
+ this.startTime = Date.now();
206
+ const mediaPlaylistUrl = await this.resolveMediaPlaylist();
207
+ if (this.options.mode === 'chunks') {
208
+ await this.downloadChunks(mediaPlaylistUrl, dest);
209
+ }
210
+ else {
211
+ if (typeof dest !== 'string') {
212
+ throw new Error('Merge mode requires a string path, not a function');
153
213
  }
154
- const playlist = parseM3u8(content, currentManifestUrl);
155
- let newSegments = playlist.segments.filter(s => !seenSegments.has(s.url));
156
- if (options.onSegment) {
157
- const filtered = [];
158
- for (const seg of newSegments) {
159
- if (seg.key && options.onKey) {
160
- const modifiedKey = options.onKey(seg.key);
161
- if (modifiedKey)
162
- seg.key = modifiedKey;
163
- }
164
- const res = options.onSegment(seg);
165
- if (res === null)
166
- continue;
167
- if (res)
168
- filtered.push(res);
169
- else
170
- filtered.push(seg);
214
+ await this.downloadMerged(mediaPlaylistUrl, dest);
215
+ }
216
+ }
217
+ async *stream() {
218
+ this.startTime = Date.now();
219
+ const mediaPlaylistUrl = await this.resolveMediaPlaylist();
220
+ const isLive = this.isLiveMode();
221
+ const maxDuration = this.getMaxDuration();
222
+ while (!this.aborted) {
223
+ const playlist = await this.fetchMediaPlaylist(mediaPlaylistUrl);
224
+ const newSegments = playlist.segments.filter((s) => !this.seenSequences.has(s.sequence));
225
+ for (const segment of newSegments) {
226
+ if (this.aborted)
227
+ break;
228
+ if (maxDuration && Date.now() - this.startTime > maxDuration) {
229
+ return;
171
230
  }
172
- newSegments = filtered;
173
- }
174
- if (newSegments.length > 0) {
175
- info(`Found ${newSegments.length} new segments.`);
176
- await runner.run(newSegments, async (seg) => {
177
- seenSegments.add(seg.url);
178
- const fileDest = merge
179
- ? join(tempDir, `${seg.sequence.toString().padStart(10, '0')}.ts`)
180
- : join(outputDir, `segment_${seg.sequence}.ts`);
181
- await client.get(seg.url).write(fileDest);
182
- });
231
+ this.seenSequences.add(segment.sequence);
232
+ const data = await this.downloadSegment(segment);
233
+ const segmentData = {
234
+ sequence: segment.sequence,
235
+ duration: segment.duration,
236
+ data,
237
+ url: segment.url,
238
+ downloadedAt: new Date(),
239
+ };
240
+ this.downloadedSegments++;
241
+ this.downloadedBytes += data.byteLength;
242
+ this.emitProgress(playlist, isLive);
243
+ if (this.options.onSegment) {
244
+ await this.options.onSegment(segmentData);
245
+ }
246
+ yield segmentData;
183
247
  }
184
248
  if (!isLive || playlist.endList) {
185
- recording = false;
186
249
  break;
187
250
  }
188
- if (maxDuration > 0 && (Date.now() - startTime) > maxDuration) {
189
- info('Max duration reached. Stopping.');
190
- recording = false;
251
+ if (maxDuration && Date.now() - this.startTime > maxDuration) {
191
252
  break;
192
253
  }
193
- const waitTime = Math.max(1000, (playlist.targetDuration * 1000) / 2);
194
- await new Promise(r => setTimeout(r, waitTime));
195
- }
196
- catch (err) {
197
- if (isLive) {
198
- info(`Error fetching live manifest, retrying: ${err.message}`);
199
- const waitTime = 5000;
200
- await new Promise(r => setTimeout(r, waitTime));
254
+ const pollInterval = Math.max(1000, (playlist.targetDuration * 1000) / 2);
255
+ await this.sleep(pollInterval);
256
+ }
257
+ }
258
+ async pipe(writable) {
259
+ try {
260
+ for await (const segment of this.stream()) {
261
+ const canContinue = writable.write(segment.data);
262
+ if (!canContinue) {
263
+ await new Promise((resolve) => writable.once('drain', resolve));
264
+ }
201
265
  }
202
- else {
203
- error(new Error(`HLS download failed: ${err.message}`));
204
- recording = false;
266
+ }
267
+ finally {
268
+ if ('end' in writable && typeof writable.end === 'function') {
269
+ writable.end();
205
270
  }
206
271
  }
207
272
  }
208
- if (merge) {
209
- info('Merging segments...');
210
- const dest = createWriteStream(outputPath);
211
- const files = (await readdir(tempDir)).sort();
212
- for (const file of files) {
213
- if (!file.endsWith('.ts'))
214
- continue;
215
- const chunk = await readFile(join(tempDir, file));
216
- dest.write(chunk);
273
+ async info() {
274
+ const content = await this.client.get(this.manifestUrl).text();
275
+ if (isMasterPlaylist(content)) {
276
+ const master = parseMasterPlaylist(content, this.manifestUrl);
277
+ const selectedVariant = selectVariant(master.variants, this.options.quality);
278
+ const playlistContent = await this.client.get(selectedVariant.url).text();
279
+ const playlist = parseMediaPlaylist(playlistContent, selectedVariant.url);
280
+ const totalDuration = playlist.endList
281
+ ? playlist.segments.reduce((sum, s) => sum + s.duration, 0)
282
+ : undefined;
283
+ return {
284
+ master,
285
+ playlist,
286
+ selectedVariant,
287
+ isLive: !playlist.endList,
288
+ totalDuration,
289
+ };
290
+ }
291
+ const playlist = parseMediaPlaylist(content, this.manifestUrl);
292
+ const totalDuration = playlist.endList
293
+ ? playlist.segments.reduce((sum, s) => sum + s.duration, 0)
294
+ : undefined;
295
+ return {
296
+ playlist,
297
+ isLive: !playlist.endList,
298
+ totalDuration,
299
+ };
300
+ }
301
+ async resolveMediaPlaylist() {
302
+ const content = await this.client.get(this.manifestUrl).text();
303
+ if (!isMasterPlaylist(content)) {
304
+ return this.manifestUrl;
305
+ }
306
+ const master = parseMasterPlaylist(content, this.manifestUrl);
307
+ const variant = selectVariant(master.variants, this.options.quality);
308
+ return variant.url;
309
+ }
310
+ async fetchMediaPlaylist(url) {
311
+ const content = await this.client.get(url).text();
312
+ return parseMediaPlaylist(content, url);
313
+ }
314
+ async downloadSegment(segment) {
315
+ if (segment.key && segment.key.method !== 'NONE') {
316
+ throw new Error(`Encrypted HLS (${segment.key.method}) requires ffmpeg. Use unencrypted streams or install ffmpeg.`);
317
+ }
318
+ const response = await this.client.get(segment.url, {
319
+ headers: this.options.headers,
320
+ signal: this.abortController.signal,
321
+ });
322
+ const blob = await response.blob();
323
+ return new Uint8Array(await blob.arrayBuffer());
324
+ }
325
+ async downloadMerged(playlistUrl, outputPath) {
326
+ await mkdir(dirname(outputPath), { recursive: true });
327
+ const output = createWriteStream(outputPath);
328
+ try {
329
+ await this.pipe(output);
330
+ }
331
+ catch (error) {
332
+ output.destroy();
333
+ throw error;
334
+ }
335
+ }
336
+ async downloadChunks(playlistUrl, dest) {
337
+ const getPath = typeof dest === 'string'
338
+ ? (seg) => join(dest, `segment-${seg.sequence}.ts`)
339
+ : dest;
340
+ const baseDir = typeof dest === 'string' ? dest : dirname(getPath({ sequence: 0, duration: 0, url: '' }));
341
+ await mkdir(baseDir, { recursive: true });
342
+ for await (const segment of this.stream()) {
343
+ const filePath = getPath({
344
+ sequence: segment.sequence,
345
+ duration: segment.duration,
346
+ url: segment.url,
347
+ });
348
+ await mkdir(dirname(filePath), { recursive: true });
349
+ const output = createWriteStream(filePath);
350
+ await new Promise((resolve, reject) => {
351
+ output.write(segment.data, (err) => {
352
+ if (err)
353
+ reject(err);
354
+ else {
355
+ output.end();
356
+ resolve();
357
+ }
358
+ });
359
+ });
217
360
  }
218
- dest.end();
219
- await rm(tempDir, { recursive: true, force: true });
220
- info(`Saved to ${outputPath}`);
221
361
  }
222
- else {
223
- info(`Download complete.`);
362
+ isLiveMode() {
363
+ return this.options.live === true || typeof this.options.live === 'object';
224
364
  }
365
+ getMaxDuration() {
366
+ if (typeof this.options.live === 'object' && this.options.live.duration) {
367
+ return this.options.live.duration;
368
+ }
369
+ return undefined;
370
+ }
371
+ emitProgress(playlist, isLive) {
372
+ if (!this.options.onProgress)
373
+ return;
374
+ this.options.onProgress({
375
+ downloadedSegments: this.downloadedSegments,
376
+ totalSegments: isLive ? undefined : playlist.segments.length,
377
+ downloadedBytes: this.downloadedBytes,
378
+ currentSegment: Math.max(...this.seenSequences),
379
+ isLive,
380
+ elapsed: Date.now() - this.startTime,
381
+ });
382
+ }
383
+ sleep(ms) {
384
+ return new Promise((resolve) => {
385
+ const timeout = setTimeout(resolve, ms);
386
+ this.abortController.signal.addEventListener('abort', () => {
387
+ clearTimeout(timeout);
388
+ resolve();
389
+ }, { once: true });
390
+ });
391
+ }
392
+ }
393
+ export function hls(client, manifestUrl, options = {}) {
394
+ return new HlsPromise(client, manifestUrl, options);
225
395
  }
@@ -83,7 +83,7 @@ export function retry(options = {}) {
83
83
  if (onRetry) {
84
84
  onRetry(attempt, err, delayMs);
85
85
  }
86
- if (client.hooks && client.hooks.onRetry) {
86
+ if (client.hooks?.onRetry) {
87
87
  for (const hook of client.hooks.onRetry) {
88
88
  await hook(err, attempt, delayMs, req);
89
89
  }
@@ -99,7 +99,7 @@ export function retry(options = {}) {
99
99
  if (onRetry) {
100
100
  onRetry(attempt, error, delayMs);
101
101
  }
102
- if (client.hooks && client.hooks.onRetry) {
102
+ if (client.hooks?.onRetry) {
103
103
  for (const hook of client.hooks.onRetry) {
104
104
  await hook(error, attempt, delayMs, req);
105
105
  }
@@ -2,4 +2,12 @@ export { MockClient, MockTransport, createMockClient, installGlobalMock, uninsta
2
2
  export type { MockResponseOptions, MockInterceptOptions, } from './mock.js';
3
3
  export { MockUDPServer, createMockUDPServer, } from './mock-udp-server.js';
4
4
  export type { MockUDPServerOptions, ReceivedMessage, } from './mock-udp-server.js';
5
+ export { MockHlsServer, createMockHlsVod, createMockHlsLive, createMockHlsMultiQuality, } from './mock-hls-server.js';
6
+ export type { MockHlsServerOptions, MockHlsVariant, MockHlsSegment, MockHlsStats, } from './mock-hls-server.js';
7
+ export { MockWebSocketServer, createMockWebSocketServer, } from './mock-websocket-server.js';
8
+ export type { MockWebSocketServerOptions, MockWebSocketClient, MockWebSocketMessage, MockWebSocketStats, } from './mock-websocket-server.js';
9
+ export { MockSSEServer, createMockSSEServer, } from './mock-sse-server.js';
10
+ export type { MockSSEServerOptions, SSEEvent, MockSSEClient, MockSSEStats, } from './mock-sse-server.js';
11
+ export { MockHttpServer, createMockHttpServer, } from './mock-http-server.js';
12
+ export type { MockHttpServerOptions, MockHttpResponse, MockHttpRequest, MockHttpHandler, MockHttpStats, } from './mock-http-server.js';
5
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAqCA,OAAO,EACL,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,SAAS,GACV,MAAM,WAAW,CAAC;AAEnB,YAAY,EACV,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,aAAa,EACb,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,oBAAoB,EACpB,eAAe,GAChB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAqCA,OAAO,EACL,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,SAAS,GACV,MAAM,WAAW,CAAC;AAEnB,YAAY,EACV,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,aAAa,EACb,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,oBAAoB,EACpB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,YAAY,GACb,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AAEpC,YAAY,EACV,0BAA0B,EAC1B,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,aAAa,EACb,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,oBAAoB,EACpB,QAAQ,EACR,aAAa,EACb,YAAY,GACb,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,cAAc,EACd,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,aAAa,GACd,MAAM,uBAAuB,CAAC"}
@@ -1,2 +1,6 @@
1
1
  export { MockClient, MockTransport, createMockClient, installGlobalMock, uninstallGlobalMock, MockAgent, } from './mock.js';
2
2
  export { MockUDPServer, createMockUDPServer, } from './mock-udp-server.js';
3
+ export { MockHlsServer, createMockHlsVod, createMockHlsLive, createMockHlsMultiQuality, } from './mock-hls-server.js';
4
+ export { MockWebSocketServer, createMockWebSocketServer, } from './mock-websocket-server.js';
5
+ export { MockSSEServer, createMockSSEServer, } from './mock-sse-server.js';
6
+ export { MockHttpServer, createMockHttpServer, } from './mock-http-server.js';