web-streams-shim 1.0.4 → 1.0.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-streams-shim",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "",
5
5
  "main": "web-streams-core.js",
6
6
  "scripts": {
@@ -13,7 +13,28 @@
13
13
  "Request",
14
14
  "Response",
15
15
  "fetch",
16
- "Blob"
16
+ "Blob",
17
+ "async",
18
+ "iterator",
19
+ "iterable",
20
+ "asyncIterator",
21
+ "body",
22
+ "ReadableStreamDefaultReader",
23
+ "ReadableStreamDefaultController",
24
+ "duplex",
25
+ "half",
26
+ "construct",
27
+ "constructor",
28
+ "reader",
29
+ "read",
30
+ "getReader",
31
+ "from",
32
+ "controller",
33
+ "prototype",
34
+ "web-streams",
35
+ "shim",
36
+ "polyfill",
37
+ "ponyfill"
17
38
  ],
18
39
  "license": "ISC",
19
40
  "dependencies": {
package/test/test.html ADDED
@@ -0,0 +1,550 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Stream Polyfill Tests</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
10
+ max-width: 1200px;
11
+ margin: 0 auto;
12
+ padding: 20px;
13
+ background: #f5f5f5;
14
+ }
15
+ h1 {
16
+ color: #333;
17
+ border-bottom: 3px solid #4CAF50;
18
+ padding-bottom: 10px;
19
+ }
20
+ .test-section {
21
+ background: white;
22
+ border-radius: 8px;
23
+ padding: 20px;
24
+ margin: 20px 0;
25
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
26
+ }
27
+ .test-section h2 {
28
+ color: #555;
29
+ margin-top: 0;
30
+ }
31
+ .test-case {
32
+ margin: 15px 0;
33
+ padding: 15px;
34
+ background: #f9f9f9;
35
+ border-left: 4px solid #ddd;
36
+ border-radius: 4px;
37
+ }
38
+ .test-case.pass {
39
+ border-left-color: #4CAF50;
40
+ background: #f1f8f4;
41
+ }
42
+ .test-case.fail {
43
+ border-left-color: #f44336;
44
+ background: #fef1f0;
45
+ }
46
+ .test-case.running {
47
+ border-left-color: #2196F3;
48
+ background: #e3f2fd;
49
+ }
50
+ .test-name {
51
+ font-weight: bold;
52
+ margin-bottom: 8px;
53
+ }
54
+ .test-result {
55
+ font-family: 'Courier New', monospace;
56
+ font-size: 14px;
57
+ color: #666;
58
+ }
59
+ .test-error {
60
+ color: #f44336;
61
+ margin-top: 8px;
62
+ padding: 8px;
63
+ background: #ffebee;
64
+ border-radius: 4px;
65
+ font-size: 12px;
66
+ }
67
+ .summary {
68
+ background: #e3f2fd;
69
+ border-radius: 8px;
70
+ padding: 20px;
71
+ margin: 20px 0;
72
+ display: flex;
73
+ justify-content: space-around;
74
+ text-align: center;
75
+ }
76
+ .summary div {
77
+ flex: 1;
78
+ }
79
+ .summary .count {
80
+ font-size: 32px;
81
+ font-weight: bold;
82
+ margin: 10px 0;
83
+ }
84
+ .summary .pass .count { color: #4CAF50; }
85
+ .summary .fail .count { color: #f44336; }
86
+ .summary .total .count { color: #2196F3; }
87
+ button {
88
+ background: #4CAF50;
89
+ color: white;
90
+ border: none;
91
+ padding: 12px 24px;
92
+ border-radius: 4px;
93
+ cursor: pointer;
94
+ font-size: 16px;
95
+ margin-right: 10px;
96
+ }
97
+ button:hover {
98
+ background: #45a049;
99
+ }
100
+ button:disabled {
101
+ background: #ccc;
102
+ cursor: not-allowed;
103
+ }
104
+ .controls {
105
+ margin: 20px 0;
106
+ }
107
+ </style>
108
+ </head>
109
+ <body>
110
+ <h1>🧪 Stream Polyfill Test Suite</h1>
111
+
112
+ <div class="controls">
113
+ <button id="runTests">Run All Tests</button>
114
+ <button id="clearResults">Clear Results</button>
115
+ </div>
116
+
117
+ <div class="summary" id="summary" style="display: none;">
118
+ <div class="total">
119
+ <div>Total Tests</div>
120
+ <div class="count" id="totalCount">0</div>
121
+ </div>
122
+ <div class="pass">
123
+ <div>Passed</div>
124
+ <div class="count" id="passCount">0</div>
125
+ </div>
126
+ <div class="fail">
127
+ <div>Failed</div>
128
+ <div class="count" id="failCount">0</div>
129
+ </div>
130
+ </div>
131
+
132
+ <div id="results"></div>
133
+ <script src="https://patrick-ring-motive.github.io/web-streams-shim/web-streams-core.js"></script>
134
+ <script>
135
+ // Test framework
136
+ class TestRunner {
137
+ constructor() {
138
+ this.tests = [];
139
+ this.results = [];
140
+ }
141
+
142
+ test(name, fn) {
143
+ this.tests.push({ name, fn });
144
+ }
145
+
146
+ async runAll() {
147
+ this.results = [];
148
+ const resultsDiv = document.getElementById('results');
149
+ resultsDiv.innerHTML = '';
150
+
151
+ for (const test of this.tests) {
152
+ const testDiv = this.createTestElement(test.name, 'running');
153
+ resultsDiv.appendChild(testDiv);
154
+
155
+ try {
156
+ await test.fn();
157
+ this.updateTestElement(testDiv, 'pass', 'Passed ✓');
158
+ this.results.push({ name: test.name, passed: true });
159
+ } catch (error) {
160
+ this.updateTestElement(testDiv, 'fail', 'Failed ✗', error.message);
161
+ this.results.push({ name: test.name, passed: false, error: error.message });
162
+ }
163
+ }
164
+
165
+ this.updateSummary();
166
+ }
167
+
168
+ createTestElement(name, status) {
169
+ const section = document.querySelector(`#section-${name.split(':')[0].replace(/\s+/g, '-')}`) ||
170
+ this.createSection(name.split(':')[0]);
171
+
172
+ const div = document.createElement('div');
173
+ div.className = `test-case ${status}`;
174
+ div.innerHTML = `
175
+ <div class="test-name">${name}</div>
176
+ <div class="test-result">Running...</div>
177
+ `;
178
+ section.appendChild(div);
179
+ return div;
180
+ }
181
+
182
+ createSection(title) {
183
+ const section = document.createElement('div');
184
+ section.className = 'test-section';
185
+ section.id = `section-${title.replace(/\s+/g, '-')}`;
186
+ section.innerHTML = `<h2>${title}</h2>`;
187
+ document.getElementById('results').appendChild(section);
188
+ return section;
189
+ }
190
+
191
+ updateTestElement(div, status, result, error = null) {
192
+ div.className = `test-case ${status}`;
193
+ div.querySelector('.test-result').textContent = result;
194
+ if (error) {
195
+ const errorDiv = document.createElement('div');
196
+ errorDiv.className = 'test-error';
197
+ errorDiv.textContent = error;
198
+ div.appendChild(errorDiv);
199
+ }
200
+ }
201
+
202
+ updateSummary() {
203
+ const total = this.results.length;
204
+ const passed = this.results.filter(r => r.passed).length;
205
+ const failed = total - passed;
206
+
207
+ document.getElementById('summary').style.display = 'flex';
208
+ document.getElementById('totalCount').textContent = total;
209
+ document.getElementById('passCount').textContent = passed;
210
+ document.getElementById('failCount').textContent = failed;
211
+ }
212
+ }
213
+
214
+ const runner = new TestRunner();
215
+
216
+ // Helper functions
217
+ function assert(condition, message) {
218
+ if (!condition) {
219
+ throw new Error(message || 'Assertion failed');
220
+ }
221
+ }
222
+
223
+ function assertEqual(actual, expected, message) {
224
+ if (actual !== expected) {
225
+ throw new Error(message || `Expected ${expected}, got ${actual}`);
226
+ }
227
+ }
228
+
229
+ async function delay(ms) {
230
+ return new Promise(resolve => setTimeout(resolve, ms));
231
+ }
232
+
233
+ // ReadableStream Iterator Tests
234
+ runner.test('ReadableStream Iterator: next() method exists', async () => {
235
+ const stream = new ReadableStream({
236
+ start(controller) {
237
+ controller.enqueue('test');
238
+ controller.close();
239
+ }
240
+ });
241
+ const reader = stream.getReader();
242
+ assert(typeof reader.next === 'function', 'next() method should exist');
243
+ });
244
+
245
+ runner.test('ReadableStream Iterator: next() reads chunks', async () => {
246
+ const stream = new ReadableStream({
247
+ start(controller) {
248
+ controller.enqueue('chunk1');
249
+ controller.enqueue('chunk2');
250
+ controller.close();
251
+ }
252
+ });
253
+ const reader = stream.getReader();
254
+ const result1 = await reader.next();
255
+ assertEqual(result1.value, 'chunk1', 'First chunk should be chunk1');
256
+ assertEqual(result1.done, false, 'Should not be done');
257
+ const result2 = await reader.next();
258
+ assertEqual(result2.value, 'chunk2', 'Second chunk should be chunk2');
259
+ });
260
+
261
+ runner.test('ReadableStream Iterator: Symbol.asyncIterator on reader', async () => {
262
+ const stream = new ReadableStream({
263
+ start(controller) {
264
+ controller.enqueue(1);
265
+ controller.enqueue(2);
266
+ controller.close();
267
+ }
268
+ });
269
+ const reader = stream.getReader();
270
+ assert(typeof reader[Symbol.asyncIterator] === 'function', 'Symbol.asyncIterator should exist');
271
+ assertEqual(reader[Symbol.asyncIterator](), reader, 'Should return itself');
272
+ });
273
+
274
+ runner.test('ReadableStream Iterator: for-await-of on reader', async () => {
275
+ const stream = new ReadableStream({
276
+ start(controller) {
277
+ controller.enqueue(1);
278
+ controller.enqueue(2);
279
+ controller.enqueue(3);
280
+ controller.close();
281
+ }
282
+ });
283
+ const reader = stream.getReader();
284
+ const values = [];
285
+ for await (const value of reader) {
286
+ values.push(value);
287
+ }
288
+ assertEqual(values.length, 3, 'Should read 3 values');
289
+ assertEqual(values[0], 1, 'First value should be 1');
290
+ assertEqual(values[2], 3, 'Third value should be 3');
291
+ });
292
+
293
+ runner.test('ReadableStream Iterator: return() method', async () => {
294
+ const stream = new ReadableStream({
295
+ start(controller) {
296
+ controller.enqueue(1);
297
+ controller.enqueue(2);
298
+ controller.enqueue(3);
299
+ controller.close();
300
+ }
301
+ });
302
+ const reader = stream.getReader();
303
+ assert(typeof reader.return === 'function', 'return() method should exist');
304
+ await reader.next();
305
+ const result = await reader.return('stopped');
306
+ assert(result.done, 'Should be done after return');
307
+ });
308
+
309
+ runner.test('ReadableStream Iterator: throw() method', async () => {
310
+ const stream = new ReadableStream({
311
+ start(controller) {
312
+ controller.enqueue(1);
313
+ controller.close();
314
+ }
315
+ });
316
+ const reader = stream.getReader();
317
+ assert(typeof reader.throw === 'function', 'throw() method should exist');
318
+ const result = await reader.throw(new Error('test error'));
319
+ assert(result.done, 'Should be done after throw');
320
+ });
321
+
322
+ // ReadableStream Direct Iterator Tests
323
+ runner.test('ReadableStream Direct: Symbol.asyncIterator on stream', async () => {
324
+ const stream = new ReadableStream({
325
+ start(controller) {
326
+ controller.enqueue(1);
327
+ controller.close();
328
+ }
329
+ });
330
+ assert(typeof stream[Symbol.asyncIterator] === 'function', 'Symbol.asyncIterator should exist on stream');
331
+ });
332
+
333
+ runner.test('ReadableStream Direct: for-await-of on stream', async () => {
334
+ const stream = new ReadableStream({
335
+ start(controller) {
336
+ controller.enqueue('a');
337
+ controller.enqueue('b');
338
+ controller.enqueue('c');
339
+ controller.close();
340
+ }
341
+ });
342
+ const values = [];
343
+ for await (const value of stream) {
344
+ values.push(value);
345
+ }
346
+ assertEqual(values.join(''), 'abc', 'Should read all values');
347
+ });
348
+
349
+ runner.test('ReadableStream Direct: values() method', async () => {
350
+ const stream = new ReadableStream({
351
+ start(controller) {
352
+ controller.enqueue(10);
353
+ controller.enqueue(20);
354
+ controller.close();
355
+ }
356
+ });
357
+ assert(typeof stream.values === 'function', 'values() method should exist');
358
+ const values = [];
359
+ for await (const value of stream.values()) {
360
+ values.push(value);
361
+ }
362
+ assertEqual(values.length, 2, 'Should read 2 values');
363
+ });
364
+
365
+ // ReadableStream.from() Tests
366
+ runner.test('ReadableStream.from: exists as static method', async () => {
367
+ assert(typeof ReadableStream.from === 'function', 'ReadableStream.from should exist');
368
+ });
369
+
370
+ runner.test('ReadableStream.from: creates stream from array', async () => {
371
+ const stream = ReadableStream.from([1, 2, 3]);
372
+ const values = [];
373
+ for await (const value of stream) {
374
+ values.push(value);
375
+ }
376
+ assertEqual(values.length, 3, 'Should have 3 values');
377
+ assertEqual(values[1], 2, 'Second value should be 2');
378
+ });
379
+
380
+ runner.test('ReadableStream.from: creates stream from generator', async () => {
381
+ function* gen() {
382
+ yield 'a';
383
+ yield 'b';
384
+ yield 'c';
385
+ }
386
+ const stream = ReadableStream.from(gen());
387
+ const values = [];
388
+ for await (const value of stream) {
389
+ values.push(value);
390
+ }
391
+ assertEqual(values.join(''), 'abc', 'Should read all generated values');
392
+ });
393
+
394
+ runner.test('ReadableStream.from: creates stream from async generator', async () => {
395
+ async function* asyncGen() {
396
+ yield Promise.resolve(1);
397
+ yield Promise.resolve(2);
398
+ }
399
+ const stream = ReadableStream.from(asyncGen());
400
+ const values = [];
401
+ for await (const value of stream) {
402
+ values.push(value);
403
+ }
404
+ assertEqual(values.length, 2, 'Should have 2 values from async generator');
405
+ });
406
+
407
+ // Request/Response Body Tests
408
+ runner.test('Request/Response: Request has body property', async () => {
409
+ const req = new Request('https://example.com', {
410
+ method: 'POST',
411
+ body: 'test data'
412
+ });
413
+ assert(req.body !== undefined, 'Request should have body property');
414
+ });
415
+
416
+ runner.test('Request/Response: Response has body property', async () => {
417
+ const res = new Response('test data');
418
+ assert(res.body !== undefined, 'Response should have body property');
419
+ });
420
+
421
+ runner.test('Request/Response: body is ReadableStream', async () => {
422
+ const res = new Response('test data');
423
+ assert(res.body instanceof ReadableStream, 'body should be a ReadableStream');
424
+ });
425
+
426
+ runner.test('Request/Response: can read body chunks', async () => {
427
+ const res = new Response('hello world');
428
+ const reader = res.body.getReader();
429
+ const { value, done } = await reader.read();
430
+ assert(value instanceof Uint8Array, 'Chunk should be Uint8Array');
431
+ assert(!done, 'Should not be done after first read');
432
+ });
433
+
434
+ runner.test('Request/Response: bodyUsed property', async () => {
435
+ const res = new Response('test');
436
+ assert(typeof res.bodyUsed === 'boolean', 'bodyUsed should be boolean');
437
+ });
438
+
439
+ // bytes() Method Tests
440
+ runner.test('bytes(): Response.bytes() exists', async () => {
441
+ const res = new Response('test');
442
+ assert(typeof res.bytes === 'function', 'bytes() should exist on Response');
443
+ });
444
+
445
+ runner.test('bytes(): returns Uint8Array', async () => {
446
+ const res = new Response('hello');
447
+ const bytes = await res.bytes();
448
+ assert(bytes instanceof Uint8Array, 'bytes() should return Uint8Array');
449
+ });
450
+
451
+ runner.test('bytes(): correct data', async () => {
452
+ const res = new Response('ABC');
453
+ const bytes = await res.bytes();
454
+ assert(bytes.length > 0, 'Should have bytes');
455
+ // Check if it contains expected byte values
456
+ assertEqual(bytes[0], 65, 'First byte should be 65 (A)');
457
+ });
458
+
459
+ runner.test('bytes(): Blob.bytes() exists', async () => {
460
+ const blob = new Blob(['test']);
461
+ assert(typeof blob.bytes === 'function', 'bytes() should exist on Blob');
462
+ });
463
+
464
+ runner.test('bytes(): Blob.bytes() returns data', async () => {
465
+ const blob = new Blob(['hello']);
466
+ const bytes = await blob.bytes();
467
+ assert(bytes instanceof Uint8Array, 'Blob bytes() should return Uint8Array');
468
+ assert(bytes.length > 0, 'Should have bytes');
469
+ });
470
+
471
+ // ReadableStreamDefaultReader Constructor Tests
472
+ runner.test('ReadableStreamDefaultReader: constructor works', async () => {
473
+ const stream = new ReadableStream({
474
+ start(controller) {
475
+ controller.enqueue('test');
476
+ controller.close();
477
+ }
478
+ });
479
+ const reader = new ReadableStreamDefaultReader(stream);
480
+ assert(reader instanceof ReadableStreamDefaultReader, 'Should be instance of ReadableStreamDefaultReader');
481
+ });
482
+
483
+ runner.test('ReadableStreamDefaultReader: can read after construction', async () => {
484
+ const stream = new ReadableStream({
485
+ start(controller) {
486
+ controller.enqueue('data');
487
+ controller.close();
488
+ }
489
+ });
490
+ const reader = new ReadableStreamDefaultReader(stream);
491
+ const result = await reader.read();
492
+ assertEqual(result.value, 'data', 'Should read correct value');
493
+ });
494
+
495
+ // Integration Tests
496
+ runner.test('Integration: Stream pipeline', async () => {
497
+ const data = [1, 2, 3, 4, 5];
498
+ const stream = ReadableStream.from(data);
499
+
500
+ let sum = 0;
501
+ for await (const num of stream) {
502
+ sum += num;
503
+ }
504
+
505
+ assertEqual(sum, 15, 'Sum should be 15');
506
+ });
507
+
508
+ runner.test('Integration: Early termination with break', async () => {
509
+ const stream = new ReadableStream({
510
+ start(controller) {
511
+ for (let i = 0; i < 100; i++) {
512
+ controller.enqueue(i);
513
+ }
514
+ controller.close();
515
+ }
516
+ });
517
+
518
+ const values = [];
519
+ for await (const value of stream) {
520
+ values.push(value);
521
+ if (value >= 4) break;
522
+ }
523
+
524
+ assert(values.length <= 5, 'Should stop early');
525
+ });
526
+
527
+ // Event listeners
528
+ document.getElementById('runTests').addEventListener('click', async () => {
529
+ const button = document.getElementById('runTests');
530
+ button.disabled = true;
531
+ button.textContent = 'Running...';
532
+
533
+ await runner.runAll();
534
+
535
+ button.disabled = false;
536
+ button.textContent = 'Run All Tests';
537
+ });
538
+
539
+ document.getElementById('clearResults').addEventListener('click', () => {
540
+ document.getElementById('results').innerHTML = '';
541
+ document.getElementById('summary').style.display = 'none';
542
+ });
543
+
544
+ // Auto-run on load
545
+ window.addEventListener('load', () => {
546
+ console.log('Test suite loaded. Click "Run All Tests" to begin.');
547
+ });
548
+ </script>
549
+ </body>
550
+ </html>