@gjsify/fetch 0.0.2
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/README.md +9 -0
- package/lib/cjs/body.js +284 -0
- package/lib/cjs/errors/abort-error.js +28 -0
- package/lib/cjs/errors/base.js +36 -0
- package/lib/cjs/errors/fetch-error.js +40 -0
- package/lib/cjs/headers.js +231 -0
- package/lib/cjs/index.js +246 -0
- package/lib/cjs/request.js +306 -0
- package/lib/cjs/response.js +162 -0
- package/lib/cjs/types/index.js +17 -0
- package/lib/cjs/types/system-error.js +16 -0
- package/lib/cjs/utils/blob-from.js +124 -0
- package/lib/cjs/utils/get-search.js +30 -0
- package/lib/cjs/utils/is-redirect.js +26 -0
- package/lib/cjs/utils/is.js +47 -0
- package/lib/cjs/utils/multipart-parser.js +372 -0
- package/lib/cjs/utils/referrer.js +172 -0
- package/lib/esm/body.js +255 -0
- package/lib/esm/errors/abort-error.js +9 -0
- package/lib/esm/errors/base.js +17 -0
- package/lib/esm/errors/fetch-error.js +21 -0
- package/lib/esm/headers.js +202 -0
- package/lib/esm/index.js +224 -0
- package/lib/esm/request.js +281 -0
- package/lib/esm/response.js +133 -0
- package/lib/esm/types/index.js +1 -0
- package/lib/esm/types/system-error.js +1 -0
- package/lib/esm/utils/blob-from.js +101 -0
- package/lib/esm/utils/get-search.js +11 -0
- package/lib/esm/utils/is-redirect.js +7 -0
- package/lib/esm/utils/is.js +28 -0
- package/lib/esm/utils/multipart-parser.js +353 -0
- package/lib/esm/utils/referrer.js +153 -0
- package/package.json +53 -0
- package/src/body.ts +415 -0
- package/src/errors/abort-error.ts +10 -0
- package/src/errors/base.ts +20 -0
- package/src/errors/fetch-error.ts +26 -0
- package/src/headers.ts +279 -0
- package/src/index.spec.ts +13 -0
- package/src/index.ts +367 -0
- package/src/request.ts +396 -0
- package/src/response.ts +197 -0
- package/src/test.mts +6 -0
- package/src/types/index.ts +1 -0
- package/src/types/system-error.ts +11 -0
- package/src/utils/blob-from.ts +168 -0
- package/src/utils/get-search.ts +9 -0
- package/src/utils/is-redirect.ts +11 -0
- package/src/utils/is.ts +88 -0
- package/src/utils/multipart-parser.ts +448 -0
- package/src/utils/referrer.ts +350 -0
- package/test.gjs.js +34758 -0
- package/test.gjs.mjs +53177 -0
- package/test.node.js +1226 -0
- package/test.node.mjs +6294 -0
- package/tsconfig.json +19 -0
- package/tsconfig.types.json +8 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { DOMException } from "@gjsify/deno-runtime/ext/web/01_dom_exception";
|
|
2
|
+
import { Blob, File } from "@gjsify/deno-runtime/ext/web/09_file";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
realpathSync,
|
|
6
|
+
statSync,
|
|
7
|
+
rmdirSync,
|
|
8
|
+
createReadStream,
|
|
9
|
+
promises as fs
|
|
10
|
+
} from 'node:fs'
|
|
11
|
+
import { basename, sep, join } from 'node:path'
|
|
12
|
+
import { tmpdir } from 'node:os'
|
|
13
|
+
import process from 'node:process'
|
|
14
|
+
|
|
15
|
+
// import Blob from './index.js'
|
|
16
|
+
|
|
17
|
+
const { stat, mkdtemp } = fs
|
|
18
|
+
let i = 0, tempDir, registry
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} path filepath on the disk
|
|
22
|
+
* @param {string} [type] mimetype to use
|
|
23
|
+
*/
|
|
24
|
+
const blobFromSync = (path: string, type: string) => fromBlob(statSync(path), path, type)
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} path filepath on the disk
|
|
28
|
+
* @param {string} [type] mimetype to use
|
|
29
|
+
* @returns {Promise<Blob>}
|
|
30
|
+
*/
|
|
31
|
+
const blobFrom = (path: string, type: string): Promise<Blob> => stat(path).then(stat => fromBlob(stat, path, type))
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} path filepath on the disk
|
|
35
|
+
* @param {string} [type] mimetype to use
|
|
36
|
+
* @returns {Promise<File>}
|
|
37
|
+
*/
|
|
38
|
+
const fileFrom = (path: string, type: string): Promise<File> => stat(path).then(stat => fromFile(stat, path, type))
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} path filepath on the disk
|
|
42
|
+
* @param {string} [type] mimetype to use
|
|
43
|
+
*/
|
|
44
|
+
const fileFromSync = (path: string, type: string) => fromFile(statSync(path), path, type)
|
|
45
|
+
|
|
46
|
+
// @ts-ignore
|
|
47
|
+
const fromBlob = (stat, path, type = '') => new Blob([new BlobDataItem({
|
|
48
|
+
path,
|
|
49
|
+
size: stat.size,
|
|
50
|
+
lastModified: stat.mtimeMs,
|
|
51
|
+
start: 0
|
|
52
|
+
})], { type })
|
|
53
|
+
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
const fromFile = (stat, path, type = '') => new File([new BlobDataItem({
|
|
56
|
+
path,
|
|
57
|
+
size: stat.size,
|
|
58
|
+
lastModified: stat.mtimeMs,
|
|
59
|
+
start: 0
|
|
60
|
+
})], basename(path), { type, lastModified: stat.mtimeMs })
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates a temporary blob backed by the filesystem.
|
|
64
|
+
* NOTE: requires node.js v14 or higher to use FinalizationRegistry
|
|
65
|
+
*
|
|
66
|
+
* @param {*} data Same as fs.writeFile data
|
|
67
|
+
* @param {BlobPropertyBag & {signal?: AbortSignal}} options
|
|
68
|
+
* @param {AbortSignal} [signal] in case you wish to cancel the write operation
|
|
69
|
+
* @returns {Promise<Blob>}
|
|
70
|
+
*/
|
|
71
|
+
const createTemporaryBlob = async (data: any, { signal, type }: BlobPropertyBag & { signal?: AbortSignal; } = {}): Promise<Blob> => {
|
|
72
|
+
registry = registry || new FinalizationRegistry(fs.unlink)
|
|
73
|
+
tempDir = tempDir || await mkdtemp(realpathSync(tmpdir()) + sep)
|
|
74
|
+
const id = `${i++}`
|
|
75
|
+
const destination = join(tempDir, id)
|
|
76
|
+
if (data instanceof ArrayBuffer) data = new Uint8Array(data)
|
|
77
|
+
await fs.writeFile(destination, data, { signal })
|
|
78
|
+
const blob = await blobFrom(destination, type)
|
|
79
|
+
registry.register(blob, destination)
|
|
80
|
+
return blob
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Creates a temporary File backed by the filesystem.
|
|
85
|
+
* Pretty much the same as constructing a new File(data, name, options)
|
|
86
|
+
*
|
|
87
|
+
* NOTE: requires node.js v14 or higher to use FinalizationRegistry
|
|
88
|
+
* @param {*} data
|
|
89
|
+
* @param {string} name
|
|
90
|
+
* @param {FilePropertyBag & {signal?: AbortSignal}} opts
|
|
91
|
+
* @returns {Promise<File>}
|
|
92
|
+
*/
|
|
93
|
+
const createTemporaryFile = async (data: any, name: string, opts: FilePropertyBag & { signal?: AbortSignal; }): Promise<File> => {
|
|
94
|
+
const blob = await createTemporaryBlob(data)
|
|
95
|
+
return new File([blob], name, opts)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* This is a blob backed up by a file on the disk
|
|
100
|
+
* with minium requirement. Its wrapped around a Blob as a blobPart
|
|
101
|
+
* so you have no direct access to this.
|
|
102
|
+
*
|
|
103
|
+
* @private
|
|
104
|
+
*/
|
|
105
|
+
class BlobDataItem {
|
|
106
|
+
#path: string;
|
|
107
|
+
#start: number;
|
|
108
|
+
size
|
|
109
|
+
lastModified
|
|
110
|
+
originalSize
|
|
111
|
+
|
|
112
|
+
constructor(options) {
|
|
113
|
+
this.#path = options.path
|
|
114
|
+
this.#start = options.start
|
|
115
|
+
this.size = options.size
|
|
116
|
+
this.lastModified = options.lastModified
|
|
117
|
+
this.originalSize = options.originalSize === undefined
|
|
118
|
+
? options.size
|
|
119
|
+
: options.originalSize
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Slicing arguments is first validated and formatted
|
|
124
|
+
* to not be out of range by Blob.prototype.slice
|
|
125
|
+
*/
|
|
126
|
+
slice(start: number, end: number) {
|
|
127
|
+
return new BlobDataItem({
|
|
128
|
+
path: this.#path,
|
|
129
|
+
lastModified: this.lastModified,
|
|
130
|
+
originalSize: this.originalSize,
|
|
131
|
+
size: end - start,
|
|
132
|
+
start: this.#start + start
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async * stream() {
|
|
137
|
+
const { mtimeMs, size } = await stat(this.#path)
|
|
138
|
+
|
|
139
|
+
if (mtimeMs > this.lastModified || this.originalSize !== size) {
|
|
140
|
+
throw new DOMException('The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.', 'NotReadableError')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
yield* createReadStream(this.#path, {
|
|
144
|
+
start: this.#start,
|
|
145
|
+
end: this.#start + this.size - 1
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
get [Symbol.toStringTag]() {
|
|
150
|
+
return 'Blob'
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
process.once('exit', () => {
|
|
155
|
+
tempDir && rmdirSync(tempDir, { recursive: true })
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
export default blobFromSync
|
|
159
|
+
export {
|
|
160
|
+
Blob,
|
|
161
|
+
blobFrom,
|
|
162
|
+
blobFromSync,
|
|
163
|
+
createTemporaryBlob,
|
|
164
|
+
File,
|
|
165
|
+
fileFrom,
|
|
166
|
+
fileFromSync,
|
|
167
|
+
createTemporaryFile
|
|
168
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const getSearch = (parsedURL: URL) => {
|
|
2
|
+
if (parsedURL.search) {
|
|
3
|
+
return parsedURL.search;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const lastOffset = parsedURL.href.length - 1;
|
|
7
|
+
const hash = parsedURL.hash || (parsedURL.href[lastOffset] === '#' ? '#' : '');
|
|
8
|
+
return parsedURL.href[lastOffset - hash.length] === '?' ? '?' : '';
|
|
9
|
+
};
|
package/src/utils/is.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { URL } from '@gjsify/deno-runtime/ext/url/00_url';
|
|
2
|
+
import { Blob } from "@gjsify/deno-runtime/ext/web/09_file";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Is.js
|
|
6
|
+
*
|
|
7
|
+
* Object type checks.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const NAME = Symbol.toStringTag;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Check if `obj` is a URLSearchParams object
|
|
14
|
+
* ref: https://github.com/node-fetch/node-fetch/issues/296#issuecomment-307598143
|
|
15
|
+
* @param {*} object - Object to check for
|
|
16
|
+
* @return {boolean}
|
|
17
|
+
*/
|
|
18
|
+
export const isURLSearchParameters = object => {
|
|
19
|
+
return (
|
|
20
|
+
typeof object === 'object' &&
|
|
21
|
+
typeof object.append === 'function' &&
|
|
22
|
+
typeof object.delete === 'function' &&
|
|
23
|
+
typeof object.get === 'function' &&
|
|
24
|
+
typeof object.getAll === 'function' &&
|
|
25
|
+
typeof object.has === 'function' &&
|
|
26
|
+
typeof object.set === 'function' &&
|
|
27
|
+
typeof object.sort === 'function' &&
|
|
28
|
+
object[NAME] === 'URLSearchParams'
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Check if `object` is a W3C `Blob` object (which `File` inherits from)
|
|
34
|
+
* @param object Object to check for
|
|
35
|
+
*/
|
|
36
|
+
export const isBlob = (value: any): value is Blob => {
|
|
37
|
+
return (
|
|
38
|
+
value &&
|
|
39
|
+
typeof value === 'object' &&
|
|
40
|
+
typeof value.arrayBuffer === 'function' &&
|
|
41
|
+
typeof value.type === 'string' &&
|
|
42
|
+
typeof value.stream === 'function' &&
|
|
43
|
+
typeof value.constructor === 'function' &&
|
|
44
|
+
/^(Blob|File)$/.test(value[NAME])
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if `obj` is an instance of AbortSignal.
|
|
50
|
+
* @param object - Object to check for
|
|
51
|
+
*/
|
|
52
|
+
export const isAbortSignal = (object: any) => {
|
|
53
|
+
return (
|
|
54
|
+
typeof object === 'object' && (
|
|
55
|
+
object[NAME] === 'AbortSignal' ||
|
|
56
|
+
object[NAME] === 'EventTarget'
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* isDomainOrSubdomain reports whether sub is a subdomain (or exact match) of
|
|
63
|
+
* the parent domain.
|
|
64
|
+
*
|
|
65
|
+
* Both domains must already be in canonical form.
|
|
66
|
+
* @param {string|URL} original
|
|
67
|
+
* @param {string|URL} destination
|
|
68
|
+
*/
|
|
69
|
+
export const isDomainOrSubdomain = (destination, original) => {
|
|
70
|
+
const orig = new URL(original).hostname;
|
|
71
|
+
const dest = new URL(destination).hostname;
|
|
72
|
+
|
|
73
|
+
return orig === dest || orig.endsWith(`.${dest}`);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* isSameProtocol reports whether the two provided URLs use the same protocol.
|
|
78
|
+
*
|
|
79
|
+
* Both domains must already be in canonical form.
|
|
80
|
+
* @param {string|URL} original
|
|
81
|
+
* @param {string|URL} destination
|
|
82
|
+
*/
|
|
83
|
+
export const isSameProtocol = (destination, original) => {
|
|
84
|
+
const orig = new URL(original).protocol;
|
|
85
|
+
const dest = new URL(destination).protocol;
|
|
86
|
+
|
|
87
|
+
return orig === dest;
|
|
88
|
+
};
|