@gjsify/fetch 0.0.4 → 0.1.0

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.
Files changed (88) hide show
  1. package/README.md +27 -2
  2. package/globals.mjs +12 -0
  3. package/lib/body.d.ts +69 -0
  4. package/lib/body.js +375 -0
  5. package/lib/errors/abort-error.d.ts +7 -0
  6. package/lib/errors/abort-error.js +9 -0
  7. package/lib/errors/base.d.ts +6 -0
  8. package/lib/errors/base.js +17 -0
  9. package/lib/errors/fetch-error.d.ts +16 -0
  10. package/lib/errors/fetch-error.js +23 -0
  11. package/lib/esm/body.js +104 -56
  12. package/lib/esm/errors/base.js +3 -1
  13. package/lib/esm/headers.js +116 -131
  14. package/lib/esm/index.js +145 -190
  15. package/lib/esm/request.js +42 -41
  16. package/lib/esm/response.js +19 -4
  17. package/lib/esm/utils/blob-from.js +2 -98
  18. package/lib/esm/utils/data-uri.js +23 -0
  19. package/lib/esm/utils/is.js +7 -3
  20. package/lib/esm/utils/multipart-parser.js +5 -2
  21. package/lib/esm/utils/referrer.js +10 -10
  22. package/lib/esm/utils/soup-helpers.js +22 -0
  23. package/lib/headers.d.ts +33 -0
  24. package/lib/headers.js +195 -0
  25. package/lib/index.d.ts +18 -0
  26. package/lib/index.js +205 -0
  27. package/lib/request.d.ts +101 -0
  28. package/lib/request.js +308 -0
  29. package/lib/response.d.ts +73 -0
  30. package/lib/response.js +158 -0
  31. package/lib/types/index.d.ts +1 -0
  32. package/lib/types/index.js +1 -0
  33. package/lib/types/system-error.d.ts +11 -0
  34. package/lib/types/system-error.js +2 -0
  35. package/lib/utils/blob-from.d.ts +2 -0
  36. package/lib/utils/blob-from.js +4 -0
  37. package/lib/utils/data-uri.d.ts +10 -0
  38. package/lib/utils/data-uri.js +27 -0
  39. package/lib/utils/get-search.d.ts +1 -0
  40. package/lib/utils/get-search.js +8 -0
  41. package/lib/utils/is-redirect.d.ts +7 -0
  42. package/lib/utils/is-redirect.js +10 -0
  43. package/lib/utils/is.d.ts +35 -0
  44. package/lib/utils/is.js +74 -0
  45. package/lib/utils/multipart-parser.d.ts +2 -0
  46. package/lib/utils/multipart-parser.js +396 -0
  47. package/lib/utils/referrer.d.ts +76 -0
  48. package/lib/utils/referrer.js +283 -0
  49. package/lib/utils/soup-helpers.d.ts +12 -0
  50. package/lib/utils/soup-helpers.js +25 -0
  51. package/package.json +23 -27
  52. package/src/body.ts +181 -169
  53. package/src/errors/base.ts +3 -1
  54. package/src/headers.ts +155 -202
  55. package/src/index.spec.ts +268 -3
  56. package/src/index.ts +199 -312
  57. package/src/request.ts +84 -75
  58. package/src/response.ts +48 -18
  59. package/src/test.mts +1 -1
  60. package/src/utils/blob-from.ts +4 -164
  61. package/src/utils/data-uri.ts +29 -0
  62. package/src/utils/is.ts +15 -15
  63. package/src/utils/multipart-parser.ts +3 -3
  64. package/src/utils/referrer.ts +11 -11
  65. package/src/utils/soup-helpers.ts +37 -0
  66. package/tsconfig.json +4 -4
  67. package/tsconfig.tsbuildinfo +1 -0
  68. package/lib/cjs/body.js +0 -255
  69. package/lib/cjs/errors/abort-error.js +0 -9
  70. package/lib/cjs/errors/base.js +0 -17
  71. package/lib/cjs/errors/fetch-error.js +0 -21
  72. package/lib/cjs/headers.js +0 -202
  73. package/lib/cjs/index.js +0 -224
  74. package/lib/cjs/request.js +0 -281
  75. package/lib/cjs/response.js +0 -133
  76. package/lib/cjs/types/index.js +0 -1
  77. package/lib/cjs/types/system-error.js +0 -1
  78. package/lib/cjs/utils/blob-from.js +0 -101
  79. package/lib/cjs/utils/get-search.js +0 -11
  80. package/lib/cjs/utils/is-redirect.js +0 -7
  81. package/lib/cjs/utils/is.js +0 -28
  82. package/lib/cjs/utils/multipart-parser.js +0 -353
  83. package/lib/cjs/utils/referrer.js +0 -153
  84. package/test.gjs.js +0 -34758
  85. package/test.gjs.mjs +0 -53172
  86. package/test.node.js +0 -1226
  87. package/test.node.mjs +0 -6273
  88. package/tsconfig.types.json +0 -8
package/src/index.spec.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { describe, it, expect } from '@gjsify/unit';
1
+ import { describe, it, expect, on } from '@gjsify/unit';
2
2
 
3
- import fetch from 'node-fetch';
3
+ import fetch, { Headers, Request, Response, FormData } from 'fetch';
4
4
 
5
5
  export default async () => {
6
6
 
@@ -10,4 +10,269 @@ export default async () => {
10
10
  });
11
11
  });
12
12
 
13
- }
13
+ await describe('Headers', async () => {
14
+ await it('should create empty headers', async () => {
15
+ const h = new Headers();
16
+ expect([...h.entries()].length).toBe(0);
17
+ });
18
+
19
+ await it('should set and get headers', async () => {
20
+ const h = new Headers();
21
+ h.set('Content-Type', 'text/plain');
22
+ expect(h.get('content-type')).toBe('text/plain');
23
+ });
24
+
25
+ await it('should be case-insensitive', async () => {
26
+ const h = new Headers({ 'X-Custom': 'value' });
27
+ expect(h.get('x-custom')).toBe('value');
28
+ expect(h.has('X-CUSTOM')).toBe(true);
29
+ });
30
+
31
+ await it('should append multiple values', async () => {
32
+ const h = new Headers();
33
+ h.append('Set-Cookie', 'a=1');
34
+ h.append('Set-Cookie', 'b=2');
35
+ expect(h.get('set-cookie')).toBe('a=1, b=2');
36
+ });
37
+
38
+ await it('should delete headers', async () => {
39
+ const h = new Headers({ 'X-Remove': 'yes' });
40
+ h.delete('x-remove');
41
+ expect(h.has('x-remove')).toBe(false);
42
+ });
43
+
44
+ await it('should construct from array pairs', async () => {
45
+ const h = new Headers([['a', '1'], ['b', '2']]);
46
+ expect(h.get('a')).toBe('1');
47
+ expect(h.get('b')).toBe('2');
48
+ });
49
+
50
+ await it('should construct from another Headers', async () => {
51
+ const h1 = new Headers({ 'x-foo': 'bar' });
52
+ const h2 = new Headers(h1);
53
+ expect(h2.get('x-foo')).toBe('bar');
54
+ });
55
+
56
+ await it('should iterate entries in sorted order', async () => {
57
+ const h = new Headers({ 'z-header': '1', 'a-header': '2' });
58
+ const keys = [...h.keys()];
59
+ expect(keys[0]).toBe('a-header');
60
+ expect(keys[1]).toBe('z-header');
61
+ });
62
+
63
+ // raw() is a node-fetch extension, not part of the standard Web API
64
+ await on('Gjs', async () => {
65
+ await it('should support raw() method', async () => {
66
+ const h = new Headers();
67
+ h.append('x-multi', 'a');
68
+ h.append('x-multi', 'b');
69
+ const raw = (h as any).raw();
70
+ expect(raw['x-multi'].length).toBe(2);
71
+ expect(raw['x-multi'][0]).toBe('a');
72
+ expect(raw['x-multi'][1]).toBe('b');
73
+ });
74
+ });
75
+
76
+ await it('should have correct Symbol.toStringTag', async () => {
77
+ const h = new Headers();
78
+ expect(Object.prototype.toString.call(h)).toBe('[object Headers]');
79
+ });
80
+ });
81
+
82
+ await describe('Headers forEach', async () => {
83
+ await it('should iterate all headers with forEach', async () => {
84
+ const h = new Headers({ 'a': '1', 'b': '2' });
85
+ const collected: string[] = [];
86
+ h.forEach((value, key) => { collected.push(`${key}:${value}`); });
87
+ expect(collected.length).toBe(2);
88
+ expect(collected[0]).toBe('a:1');
89
+ expect(collected[1]).toBe('b:2');
90
+ });
91
+
92
+ await it('should return correct values() iterator', async () => {
93
+ const h = new Headers({ 'x': 'hello', 'y': 'world' });
94
+ const values = [...h.values()];
95
+ expect(values.length).toBe(2);
96
+ });
97
+
98
+ await it('should be iterable with for...of', async () => {
99
+ const h = new Headers({ 'content-type': 'text/plain', 'x-custom': 'val' });
100
+ const entries: [string, string][] = [];
101
+ for (const [k, v] of h) entries.push([k, v]);
102
+ expect(entries.length).toBe(2);
103
+ expect(entries[0][0]).toBe('content-type');
104
+ expect(entries[1][0]).toBe('x-custom');
105
+ });
106
+
107
+ await it('should support spread operator', async () => {
108
+ const h = new Headers({ 'a': '1' });
109
+ const arr = [...h];
110
+ expect(arr.length).toBe(1);
111
+ expect(arr[0][0]).toBe('a');
112
+ expect(arr[0][1]).toBe('1');
113
+ });
114
+
115
+ await it('should throw on invalid header name in set()', async () => {
116
+ const h = new Headers();
117
+ expect(() => h.set('invalid header', 'value')).toThrow();
118
+ });
119
+
120
+ await it('should throw on invalid header name in append()', async () => {
121
+ const h = new Headers();
122
+ expect(() => h.append('invalid header', 'value')).toThrow();
123
+ });
124
+
125
+ await it('should support entries() for destructuring', async () => {
126
+ const h = new Headers({ 'host': 'example.com', 'accept': '*/*' });
127
+ const obj: Record<string, string> = {};
128
+ for (const [k, v] of h.entries()) obj[k] = v;
129
+ expect(obj['accept']).toBe('*/*');
130
+ expect(obj['host']).toBe('example.com');
131
+ });
132
+ });
133
+
134
+ await describe('Request', async () => {
135
+ await it('should create a request with URL string', async () => {
136
+ const r = new Request('https://example.com');
137
+ expect(r.url).toBe('https://example.com/');
138
+ expect(r.method).toBe('GET');
139
+ });
140
+
141
+ await it('should normalize method to uppercase', async () => {
142
+ const r = new Request('https://example.com', { method: 'post' });
143
+ expect(r.method).toBe('POST');
144
+ });
145
+
146
+ await it('should set custom headers', async () => {
147
+ const r = new Request('https://example.com', {
148
+ headers: { 'X-Custom': 'test' }
149
+ });
150
+ expect(r.headers.get('x-custom')).toBe('test');
151
+ });
152
+
153
+ await it('should default redirect to "follow"', async () => {
154
+ const r = new Request('https://example.com');
155
+ expect(r.redirect).toBe('follow');
156
+ });
157
+
158
+ await it('should have a signal property', async () => {
159
+ const r = new Request('https://example.com');
160
+ expect(r.signal).toBeDefined();
161
+ });
162
+
163
+ await it('should clone a request', async () => {
164
+ const r = new Request('https://example.com', {
165
+ method: 'POST',
166
+ headers: { 'X-Test': 'value' },
167
+ });
168
+ const clone = r.clone();
169
+ expect(clone.url).toBe(r.url);
170
+ expect(clone.method).toBe('POST');
171
+ expect(clone.headers.get('x-test')).toBe('value');
172
+ });
173
+
174
+ await it('should create request with null body', async () => {
175
+ const r = new Request('https://example.com', { body: null });
176
+ expect(r.body).toBeNull();
177
+ });
178
+ });
179
+
180
+ await describe('Response', async () => {
181
+ await it('should create a response with defaults', async () => {
182
+ const r = new Response();
183
+ expect(r.status).toBe(200);
184
+ expect(r.ok).toBe(true);
185
+ });
186
+
187
+ await it('should create a response with custom status', async () => {
188
+ const r = new Response(null, { status: 404 });
189
+ expect(r.status).toBe(404);
190
+ expect(r.ok).toBe(false);
191
+ });
192
+
193
+ await it('should parse text body', async () => {
194
+ const r = new Response('hello world');
195
+ const text = await r.text();
196
+ expect(text).toBe('hello world');
197
+ });
198
+
199
+ await it('should parse json body', async () => {
200
+ const r = new Response('{"key": "value"}');
201
+ const json = await r.json();
202
+ expect(json.key).toBe('value');
203
+ });
204
+
205
+ await it('should parse arrayBuffer body', async () => {
206
+ const r = new Response('test');
207
+ const ab = await r.arrayBuffer();
208
+ expect(ab.byteLength).toBe(4);
209
+ });
210
+
211
+ await it('should track bodyUsed', async () => {
212
+ const r = new Response('data');
213
+ expect(r.bodyUsed).toBe(false);
214
+ await r.text();
215
+ expect(r.bodyUsed).toBe(true);
216
+ });
217
+
218
+ await it('Response.error() should return error response', async () => {
219
+ const r = Response.error();
220
+ expect(r.type).toBe('error');
221
+ expect(r.status).toBe(0);
222
+ });
223
+
224
+ await it('Response.redirect() should create redirect', async () => {
225
+ const r = Response.redirect('https://example.com', 301);
226
+ expect(r.status).toBe(301);
227
+ expect(r.headers.get('location')).toBe('https://example.com/');
228
+ });
229
+
230
+ await it('Response.json() should create JSON response', async () => {
231
+ const r = Response.json({ message: 'ok' });
232
+ expect(r.status).toBe(200);
233
+ const ct = r.headers.get('content-type') || '';
234
+ expect(ct.includes('application/json')).toBe(true);
235
+ const data = await r.json();
236
+ expect(data.message).toBe('ok');
237
+ });
238
+
239
+ await it('should clone a response', async () => {
240
+ const r = new Response('clone test');
241
+ const c = r.clone();
242
+ const text1 = await r.text();
243
+ const text2 = await c.text();
244
+ expect(text1).toBe('clone test');
245
+ expect(text2).toBe('clone test');
246
+ });
247
+
248
+ await it('should have correct statusText', async () => {
249
+ const r = new Response(null, { status: 201, statusText: 'Created' });
250
+ expect(r.statusText).toBe('Created');
251
+ });
252
+
253
+ await it('should have correct type for normal response', async () => {
254
+ const r = new Response();
255
+ expect(r.type).toBe('default');
256
+ });
257
+
258
+ await it('should have correct headers property', async () => {
259
+ const r = new Response(null, { headers: { 'X-Test': 'yes' } });
260
+ expect(r.headers.get('x-test')).toBe('yes');
261
+ });
262
+ });
263
+
264
+ await describe('data: URI', async () => {
265
+ await it('should fetch a data: URI with text', async () => {
266
+ const r = await fetch('data:text/plain,hello%20world');
267
+ expect(r.status).toBe(200);
268
+ const text = await r.text();
269
+ expect(text).toBe('hello world');
270
+ });
271
+
272
+ await it('should fetch a base64 data: URI', async () => {
273
+ const r = await fetch('data:text/plain;base64,aGVsbG8=');
274
+ const text = await r.text();
275
+ expect(text).toBe('hello');
276
+ });
277
+ });
278
+ };