web-streams-shim 1.0.6 → 1.0.8
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/.github/workflows/npm_publish.yml +1 -1
- package/CONTRIBUTING.md +247 -0
- package/README.md +12 -10
- package/Record-body.js +0 -12
- package/extensions/README.md +318 -0
- package/extensions/Record-stream.js +100 -0
- package/extensions/file.js +31 -3
- package/extensions/location.js +21 -0
- package/extensions/type-extensions.js +121 -16
- package/extensions/web-streams-extensions.d.ts +57 -0
- package/extensions/web-streams-extensions.js +298 -9
- package/package.json +20 -4
- package/test/test.html +2 -416
- package/test/test.js +897 -0
- package/web-streams-core.d.ts +228 -0
- package/web-streams-core.js +155 -69
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Record Stream Extensions - Minimal stream() method polyfills
|
|
3
|
+
*
|
|
4
|
+
* Adds stream() method to Request, Response, and Blob objects as a convenient
|
|
5
|
+
* alias for the .body property. This is a lightweight alternative to the full
|
|
6
|
+
* web-streams-extensions.js that only adds the stream() method.
|
|
7
|
+
*
|
|
8
|
+
* Also adds Blob.body property for API consistency with Request/Response.
|
|
9
|
+
*
|
|
10
|
+
* Use this instead of web-streams-extensions.js when you only need the
|
|
11
|
+
* stream() alias and don't want the other non-standard extensions.
|
|
12
|
+
*
|
|
13
|
+
* @note Non-standard convenience method
|
|
14
|
+
*/
|
|
15
|
+
(() => {
|
|
16
|
+
const Q = fn => {
|
|
17
|
+
try {
|
|
18
|
+
return fn?.()
|
|
19
|
+
} catch {}
|
|
20
|
+
};
|
|
21
|
+
const constructPrototype = newClass => {
|
|
22
|
+
try {
|
|
23
|
+
if (newClass?.prototype) return newClass;
|
|
24
|
+
const constProto = newClass?.constructor?.prototype;
|
|
25
|
+
if (constProto) {
|
|
26
|
+
newClass.prototype = Q(() => constProto?.bind?.(constProto)) ?? Object.create(Object(constProto));
|
|
27
|
+
return newClass;
|
|
28
|
+
}
|
|
29
|
+
newClass.prototype = Q(() => newClass?.bind?.(newClass)) ?? Object.create(Object(newClass));
|
|
30
|
+
} catch (e) {
|
|
31
|
+
console.warn(e, newClass);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const extend = (thisClass, superClass) => {
|
|
35
|
+
try {
|
|
36
|
+
constructPrototype(thisClass);
|
|
37
|
+
constructPrototype(superClass);
|
|
38
|
+
Object.setPrototypeOf(
|
|
39
|
+
thisClass.prototype,
|
|
40
|
+
superClass?.prototype ??
|
|
41
|
+
superClass?.constructor?.prototype ??
|
|
42
|
+
superClass
|
|
43
|
+
);
|
|
44
|
+
Object.setPrototypeOf(thisClass, superClass);
|
|
45
|
+
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.warn(e, {
|
|
48
|
+
thisClass,
|
|
49
|
+
superClass
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return thisClass;
|
|
53
|
+
};
|
|
54
|
+
const makeStringer = str => {
|
|
55
|
+
const stringer = () => str;
|
|
56
|
+
['valueOf', 'toString', 'toLocaleString', Symbol.toPrimitive].forEach(x => {
|
|
57
|
+
stringer[x] = stringer;
|
|
58
|
+
});
|
|
59
|
+
stringer[Symbol.toStringTag] = str;
|
|
60
|
+
return stringer;
|
|
61
|
+
};
|
|
62
|
+
const setStrings = (obj) => {
|
|
63
|
+
let type = 'function';
|
|
64
|
+
if(String(obj).trim().startsWith('class')||/^[A-Z]|^.[A-Z]/.test(obj?.name)){
|
|
65
|
+
type = 'class';
|
|
66
|
+
}
|
|
67
|
+
if(String(obj).trim().startsWith('async')||/async/i.test(obj?.name)){
|
|
68
|
+
type = 'async function';
|
|
69
|
+
}
|
|
70
|
+
for (const str of ['toString', 'toLocaleString', Symbol.toStringTag]) {
|
|
71
|
+
Object.defineProperty(obj, str, {
|
|
72
|
+
value: makeStringer(`${type} ${obj.name} { [polyfill code] }`),
|
|
73
|
+
configurable: true,
|
|
74
|
+
writable: true,
|
|
75
|
+
enumerable: false,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return obj;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
for (const record of [Q(() => Request), Q(() => Response)]) {
|
|
82
|
+
(() => {
|
|
83
|
+
let currentRecord = record;
|
|
84
|
+
while(currentRecord.__proto__.name === currentRecord.name) {
|
|
85
|
+
currentRecord = currentRecord.__proto__;
|
|
86
|
+
}
|
|
87
|
+
(currentRecord?.prototype ?? {}).stream ??= extend(setStrings(function stream() {
|
|
88
|
+
return this.body;
|
|
89
|
+
}), Q(() => ReadableStream) ?? {});
|
|
90
|
+
})();
|
|
91
|
+
}
|
|
92
|
+
if(!('body' in Blob.prototype)){
|
|
93
|
+
Object.defineProperty(Blob.prototype,'body',{
|
|
94
|
+
get:extend(setStrings(function body(){return this.stream();})),
|
|
95
|
+
set:()=>{},
|
|
96
|
+
configurable:true,
|
|
97
|
+
enumerable:false
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
})();
|
package/extensions/file.js
CHANGED
|
@@ -1,9 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Constructor Polyfill
|
|
3
|
+
*
|
|
4
|
+
* Provides a File class implementation for environments that lack it, such as:
|
|
5
|
+
* - Cloudflare Workers
|
|
6
|
+
* - Google Apps Script
|
|
7
|
+
* - Some serverless edge runtimes
|
|
8
|
+
* - Older Node.js versions (pre-20)
|
|
9
|
+
*
|
|
10
|
+
* The File class extends Blob and adds file-specific properties:
|
|
11
|
+
* - name: The file name
|
|
12
|
+
* - lastModified: Timestamp in milliseconds
|
|
13
|
+
* - lastModifiedDate: Date object (deprecated but included for compatibility)
|
|
14
|
+
* - webkitRelativePath: Empty string (for compatibility)
|
|
15
|
+
*
|
|
16
|
+
* This is a functional implementation that allows File objects to be created
|
|
17
|
+
* and used in environments where the native File constructor is missing.
|
|
18
|
+
*
|
|
19
|
+
* @note This is a complete implementation, not a partial shim
|
|
20
|
+
*/
|
|
1
21
|
(() => {
|
|
2
22
|
const Q = fn => {
|
|
3
23
|
try {
|
|
4
24
|
return fn?.()
|
|
5
25
|
} catch {}
|
|
6
26
|
};
|
|
27
|
+
const setHidden = (obj, prop, value) => {
|
|
28
|
+
Object.defineProperty(obj, prop, {
|
|
29
|
+
value,
|
|
30
|
+
writable: true,
|
|
31
|
+
enumerable: false,
|
|
32
|
+
configurable: true
|
|
33
|
+
});
|
|
34
|
+
}
|
|
7
35
|
const $global = Q(() => globalThis) ?? Q(() => global) ?? Q(() => self) ?? Q(() => window) ?? this;
|
|
8
36
|
if (typeof File === 'undefined') {
|
|
9
37
|
// Sham File class extending Blob
|
|
@@ -18,9 +46,9 @@
|
|
|
18
46
|
super(bits, blobOptions);
|
|
19
47
|
|
|
20
48
|
// Add File-specific properties
|
|
21
|
-
this
|
|
22
|
-
this
|
|
23
|
-
this
|
|
49
|
+
setHidden(this, '&name', filename);
|
|
50
|
+
setHidden(this, '&lastModified', lastModified);
|
|
51
|
+
setHidden(this, '&lastModifiedDate', new Date(lastModified));
|
|
24
52
|
}
|
|
25
53
|
|
|
26
54
|
get name() {
|
package/extensions/location.js
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Location Constructor Polyfill
|
|
3
|
+
*
|
|
4
|
+
* Provides a Location class implementation for environments that lack it, such as:
|
|
5
|
+
* - Cloudflare Workers
|
|
6
|
+
* - Deno Deploy
|
|
7
|
+
* - Vercel Edge Runtime
|
|
8
|
+
* - Service Workers in some contexts
|
|
9
|
+
* - Node.js and other server environments
|
|
10
|
+
*
|
|
11
|
+
* The Location class extends URL and adds browser Location API compatibility:
|
|
12
|
+
* - ancestorOrigins: Empty list (server environments have no origin hierarchy)
|
|
13
|
+
* - assign(url): Updates the href (no navigation in server context)
|
|
14
|
+
* - reload(forceReload): No-op with console log (no page to reload)
|
|
15
|
+
* - replace(url): Updates the href (no navigation in server context)
|
|
16
|
+
*
|
|
17
|
+
* This allows code written for browsers to run in serverless environments
|
|
18
|
+
* without modification, though navigation methods are no-ops.
|
|
19
|
+
*
|
|
20
|
+
* @note Methods like reload() don't actually navigate - use URL directly for parsing
|
|
21
|
+
*/
|
|
1
22
|
(() => {
|
|
2
23
|
const Q = fn => {
|
|
3
24
|
try {
|
|
@@ -1,17 +1,122 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Type Extensions - Sets up prototype chains for Web API methods
|
|
3
|
+
* Extends methods on Request, Response, and Blob to inherit from the class
|
|
4
|
+
* of the type they return (e.g., Response.prototype.blob extended with Blob)
|
|
5
|
+
* This provides better runtime type traceability and debugging experience
|
|
6
|
+
*/
|
|
7
|
+
(() => {
|
|
8
|
+
const Q = fn => {
|
|
9
|
+
try {
|
|
10
|
+
return fn?.()
|
|
11
|
+
} catch {}
|
|
12
|
+
};
|
|
13
|
+
const constructPrototype = newClass => {
|
|
14
|
+
try {
|
|
15
|
+
if (newClass?.prototype) return newClass;
|
|
16
|
+
const constProto = newClass?.constructor?.prototype;
|
|
17
|
+
if (constProto) {
|
|
18
|
+
newClass.prototype = Q(() => constProto?.bind?.(constProto)) ?? Object.create(Object(constProto));
|
|
19
|
+
return newClass;
|
|
20
|
+
}
|
|
21
|
+
newClass.prototype = Q(() => newClass?.bind?.(newClass)) ?? Object.create(Object(newClass));
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.warn(e, newClass);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const extend = (thisClass, superClass) => {
|
|
27
|
+
try {
|
|
28
|
+
constructPrototype(thisClass);
|
|
29
|
+
constructPrototype(superClass);
|
|
30
|
+
Object.setPrototypeOf(
|
|
31
|
+
thisClass.prototype,
|
|
32
|
+
superClass?.prototype ??
|
|
33
|
+
superClass?.constructor?.prototype ??
|
|
34
|
+
superClass
|
|
35
|
+
);
|
|
36
|
+
Object.setPrototypeOf(thisClass, superClass);
|
|
37
|
+
|
|
38
|
+
} catch (e) {
|
|
39
|
+
console.warn(e, {
|
|
40
|
+
thisClass,
|
|
41
|
+
superClass
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return thisClass;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Map of method names to their return type constructors
|
|
49
|
+
* Methods that return specific types get extended with those types' prototypes
|
|
50
|
+
*/
|
|
51
|
+
const typeMap = {
|
|
52
|
+
blob: Q(() => Blob),
|
|
53
|
+
text: Q(() => TextDecoder),
|
|
54
|
+
json: Q(() => JSON),
|
|
55
|
+
arrayBuffer: Q(() => ArrayBuffer),
|
|
56
|
+
stream: Q(() => ReadableStream),
|
|
57
|
+
formData: Q(() => FormData),
|
|
58
|
+
bytes: Q(() => Uint8Array),
|
|
59
|
+
slice: Q(() => Blob)
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Map of getter property names to their return type constructors
|
|
64
|
+
*/
|
|
65
|
+
const getterMap = {
|
|
66
|
+
url: Q(() => URL),
|
|
67
|
+
headers: Q(() => Headers),
|
|
68
|
+
body: Q(() => ReadableStream)
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Extend methods on Request, Response, and Blob prototypes
|
|
73
|
+
* to inherit from their return type constructors
|
|
74
|
+
*/
|
|
75
|
+
for (const record of [Q(() => Request), Q(() => Response), Q(() => Blob)]) {
|
|
76
|
+
if (!record) continue;
|
|
77
|
+
|
|
78
|
+
// Extend methods that return typed values
|
|
79
|
+
for (const [methodName, TypeClass] of Object.entries(typeMap)) {
|
|
80
|
+
const method = record.prototype?.[methodName];
|
|
81
|
+
if (method && TypeClass) {
|
|
82
|
+
try {
|
|
83
|
+
extend(method, TypeClass);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.warn(`Failed to extend ${record.name}.prototype.${methodName}`, e);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Extend getter properties
|
|
91
|
+
for (const [propName, TypeClass] of Object.entries(getterMap)) {
|
|
92
|
+
const descriptor = Object.getOwnPropertyDescriptor(record.prototype, propName);
|
|
93
|
+
if (descriptor?.get && TypeClass) {
|
|
94
|
+
try {
|
|
95
|
+
extend(descriptor.get, TypeClass);
|
|
96
|
+
} catch (e) {
|
|
97
|
+
console.warn(`Failed to extend ${record.name}.prototype.${propName} getter`, e);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Also extend ArrayBuffer methods if present
|
|
104
|
+
const arrayBufferMethods = {
|
|
105
|
+
bytes: Q(() => Uint8Array),
|
|
106
|
+
slice: Q(() => ArrayBuffer)
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
if (Q(() => ArrayBuffer)) {
|
|
110
|
+
for (const [methodName, TypeClass] of Object.entries(arrayBufferMethods)) {
|
|
111
|
+
const method = ArrayBuffer.prototype?.[methodName];
|
|
112
|
+
if (method && TypeClass) {
|
|
113
|
+
try {
|
|
114
|
+
extend(method, TypeClass);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
console.warn(`Failed to extend ArrayBuffer.prototype.${methodName}`, e);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
17
121
|
})();
|
|
122
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript definitions for web-streams-shim extensions
|
|
3
|
+
* Additional non-standard convenience methods for Web Streams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/// <reference lib="dom" />
|
|
7
|
+
/// <reference path="./web-streams-core.d.ts" />
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extended Request interface with additional streaming utilities
|
|
11
|
+
*/
|
|
12
|
+
interface Request {
|
|
13
|
+
/**
|
|
14
|
+
* Returns the body as a ReadableStream (alias for body property)
|
|
15
|
+
* Non-standard convenience method
|
|
16
|
+
* @returns The request body as a ReadableStream
|
|
17
|
+
*/
|
|
18
|
+
stream(): ReadableStream<Uint8Array>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Extended Response interface with additional streaming utilities
|
|
23
|
+
*/
|
|
24
|
+
interface Response {
|
|
25
|
+
/**
|
|
26
|
+
* Returns the body as a ReadableStream (alias for body property)
|
|
27
|
+
* Non-standard convenience method
|
|
28
|
+
* @returns The response body as a ReadableStream
|
|
29
|
+
*/
|
|
30
|
+
stream(): ReadableStream<Uint8Array>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Extended Blob interface with body property and streaming support
|
|
35
|
+
*/
|
|
36
|
+
interface Blob {
|
|
37
|
+
/**
|
|
38
|
+
* Returns the blob content as a ReadableStream
|
|
39
|
+
* Non-standard convenience method that matches Request/Response API
|
|
40
|
+
* @returns The blob content as a ReadableStream
|
|
41
|
+
*/
|
|
42
|
+
stream(): ReadableStream<Uint8Array>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The blob content as a ReadableStream
|
|
46
|
+
* Non-standard property that matches Request/Response API
|
|
47
|
+
*/
|
|
48
|
+
body: ReadableStream<Uint8Array>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Indicates whether the blob body has been consumed
|
|
52
|
+
* Non-standard property that matches Request/Response API
|
|
53
|
+
*/
|
|
54
|
+
bodyUsed: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export {};
|