bare-url 2.5.1 → 2.5.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/binding.c +91 -175
- package/index.js +105 -55
- package/lib/url-search-params.js +100 -25
- package/package.json +1 -1
- package/prebuilds/android-arm/bare-url.bare +0 -0
- package/prebuilds/android-arm64/bare-url.bare +0 -0
- package/prebuilds/android-ia32/bare-url.bare +0 -0
- package/prebuilds/android-x64/bare-url.bare +0 -0
- package/prebuilds/darwin-arm64/bare-url.bare +0 -0
- package/prebuilds/darwin-x64/bare-url.bare +0 -0
- package/prebuilds/ios-arm64/bare-url.bare +0 -0
- package/prebuilds/ios-arm64-simulator/bare-url.bare +0 -0
- package/prebuilds/ios-x64-simulator/bare-url.bare +0 -0
- package/prebuilds/linux-arm64/bare-url.bare +0 -0
- package/prebuilds/linux-x64/bare-url.bare +0 -0
- package/prebuilds/win32-arm64/bare-url.bare +0 -0
- package/prebuilds/win32-x64/bare-url.bare +0 -0
package/binding.c
CHANGED
|
@@ -5,109 +5,90 @@
|
|
|
5
5
|
#include <stdlib.h>
|
|
6
6
|
#include <string.h>
|
|
7
7
|
#include <url.h>
|
|
8
|
-
#include <url/character-set.h>
|
|
9
|
-
#include <url/percent-encode.h>
|
|
10
8
|
#include <utf.h>
|
|
11
9
|
#include <utf/string.h>
|
|
12
10
|
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
0x00 | 0x00 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
35
|
-
// 40 41 42 43 44 45 46 47
|
|
36
|
-
0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
|
|
37
|
-
// 48 49 4a 4b 4c 4d 4e 4f
|
|
38
|
-
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
|
|
39
|
-
// 50 51 52 53 54 55 56 57
|
|
40
|
-
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
|
|
41
|
-
// 58 59 5a 5b 5c 5d 5e 5f
|
|
42
|
-
0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x00,
|
|
43
|
-
// 60 61 62 63 64 65 66 67
|
|
44
|
-
0x01 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
|
|
45
|
-
// 68 69 6a 6b 6c 6d 6e 6f
|
|
46
|
-
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
|
|
47
|
-
// 70 71 72 73 74 75 76 77
|
|
48
|
-
0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00 | 0x00,
|
|
49
|
-
// 78 79 7a 7b 7c 7d 7e 7f
|
|
50
|
-
0x00 | 0x00 | 0x00 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
51
|
-
// 80 81 82 83 84 85 86 87
|
|
52
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
53
|
-
// 88 89 8a 8b 8c 8d 8e 8f
|
|
54
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
55
|
-
// 90 91 92 93 94 95 96 97
|
|
56
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
57
|
-
// 98 99 9a 9b 9c 9d 9e 9f
|
|
58
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
59
|
-
// a0 a1 a2 a3 a4 a5 a6 a7
|
|
60
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
61
|
-
// a8 a9 aa ab ac ad ae af
|
|
62
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
63
|
-
// b0 b1 b2 b3 b4 b5 b6 b7
|
|
64
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
65
|
-
// b8 b9 ba bb bc bd be bf
|
|
66
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
67
|
-
// c0 c1 c2 c3 c4 c5 c6 c7
|
|
68
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
69
|
-
// c8 c9 ca cb cc cd ce cf
|
|
70
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
71
|
-
// d0 d1 d2 d3 d4 d5 d6 d7
|
|
72
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
73
|
-
// d8 d9 da db dc dd de df
|
|
74
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
75
|
-
// e0 e1 e2 e3 e4 e5 e6 e7
|
|
76
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
77
|
-
// e8 e9 ea eb ec ed ee ef
|
|
78
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
79
|
-
// f0 f1 f2 f3 f4 f5 f6 f7
|
|
80
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
81
|
-
// f8 f9 fa fb fc fd fe ff
|
|
82
|
-
0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80,
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// https://url.spec.whatwg.org/#concept-urlencoded-serializer
|
|
86
|
-
static void
|
|
87
|
-
bare_url__serialize_form_urlencoded_component(js_env_t *env, js_value_t *value, utf8_string_t *result) {
|
|
11
|
+
// Maximum length, in bytes, of a UTF-8 string that is read into a stack buffer.
|
|
12
|
+
// Longer strings fall back to a heap allocation. This covers the vast majority
|
|
13
|
+
// of URLs without touching the heap.
|
|
14
|
+
#define BARE_URL_STACK_STRING_MAX 1024
|
|
15
|
+
|
|
16
|
+
// The UTF-8 encoding of a JavaScript string, together with whatever backs it.
|
|
17
|
+
typedef struct {
|
|
18
|
+
const utf8_t *data;
|
|
19
|
+
size_t len;
|
|
20
|
+
|
|
21
|
+
js_string_view_t *view;
|
|
22
|
+
utf8_t *heap;
|
|
23
|
+
} bare_url_string_t;
|
|
24
|
+
|
|
25
|
+
// Exposes `value` as UTF-8. Strings that are stored as ASCII, which is nearly
|
|
26
|
+
// all of them, are borrowed directly from the engine without being copied.
|
|
27
|
+
// Anything else is transcoded into `stack`, or into a freshly allocated heap
|
|
28
|
+
// buffer when it does not fit. The result must be released with
|
|
29
|
+
// bare_url__free_string().
|
|
30
|
+
static inline void
|
|
31
|
+
bare_url__read_string(js_env_t *env, js_value_t *value, utf8_t *stack, size_t stack_len, bare_url_string_t *result) {
|
|
88
32
|
int err;
|
|
89
33
|
|
|
34
|
+
js_string_encoding_t encoding;
|
|
35
|
+
const void *data;
|
|
90
36
|
size_t len;
|
|
91
|
-
err = js_get_value_string_utf8(env, value, NULL, 0, &len);
|
|
92
|
-
assert(err == 0);
|
|
93
37
|
|
|
94
|
-
|
|
95
|
-
err = js_get_value_string_utf8(env, value, input, len, NULL);
|
|
38
|
+
err = js_get_string_view(env, value, &encoding, &data, &len, &result->view);
|
|
96
39
|
assert(err == 0);
|
|
97
40
|
|
|
98
|
-
|
|
99
|
-
utf8_t c = input[i];
|
|
41
|
+
result->heap = NULL;
|
|
100
42
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
43
|
+
size_t utf8_len;
|
|
44
|
+
|
|
45
|
+
if (encoding == js_utf8) {
|
|
46
|
+
result->data = (const utf8_t *) data;
|
|
47
|
+
result->len = len;
|
|
48
|
+
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (encoding == js_latin1) {
|
|
53
|
+
utf8_len = utf8_length_from_latin1((const latin1_t *) data, len);
|
|
54
|
+
|
|
55
|
+
// A Latin-1 string that is no longer as UTF-8 contains only ASCII, whose
|
|
56
|
+
// Latin-1 and UTF-8 encodings are identical.
|
|
57
|
+
if (utf8_len == len) {
|
|
58
|
+
result->data = (const utf8_t *) data;
|
|
59
|
+
result->len = len;
|
|
60
|
+
|
|
61
|
+
return;
|
|
105
62
|
}
|
|
63
|
+
} else {
|
|
64
|
+
assert(encoding == js_utf16le);
|
|
65
|
+
|
|
66
|
+
utf8_len = utf8_length_from_utf16le((const utf16_t *) data, len);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
utf8_t *buffer = utf8_len <= stack_len ? stack : (result->heap = malloc(utf8_len));
|
|
106
70
|
|
|
107
|
-
|
|
71
|
+
if (encoding == js_latin1) {
|
|
72
|
+
latin1_convert_to_utf8((const latin1_t *) data, len, buffer);
|
|
73
|
+
} else {
|
|
74
|
+
utf16le_convert_to_utf8((const utf16_t *) data, len, buffer);
|
|
108
75
|
}
|
|
109
76
|
|
|
110
|
-
|
|
77
|
+
result->data = buffer;
|
|
78
|
+
result->len = utf8_len;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Releases a string read with bare_url__read_string(), freeing its buffer only
|
|
82
|
+
// when it was heap allocated rather than borrowed or written to the caller's
|
|
83
|
+
// stack buffer.
|
|
84
|
+
static inline void
|
|
85
|
+
bare_url__free_string(js_env_t *env, bare_url_string_t *string) {
|
|
86
|
+
int err;
|
|
87
|
+
|
|
88
|
+
free(string->heap);
|
|
89
|
+
|
|
90
|
+
err = js_release_string_view(env, string->view);
|
|
91
|
+
assert(err == 0);
|
|
111
92
|
}
|
|
112
93
|
|
|
113
94
|
static js_value_t *
|
|
@@ -134,17 +115,14 @@ bare_url_parse(js_env_t *env, js_callback_info_t *info) {
|
|
|
134
115
|
url_init(&base);
|
|
135
116
|
|
|
136
117
|
if (has_base) {
|
|
137
|
-
|
|
138
|
-
err = js_get_value_string_utf8(env, argv[1], NULL, 0, &len);
|
|
139
|
-
assert(err == 0);
|
|
118
|
+
utf8_t stack[BARE_URL_STACK_STRING_MAX];
|
|
140
119
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
assert(err == 0);
|
|
120
|
+
bare_url_string_t input;
|
|
121
|
+
bare_url__read_string(env, argv[1], stack, sizeof(stack), &input);
|
|
144
122
|
|
|
145
|
-
err = url_parse(&base, input, len, NULL);
|
|
123
|
+
err = url_parse(&base, input.data, input.len, NULL);
|
|
146
124
|
|
|
147
|
-
|
|
125
|
+
bare_url__free_string(env, &input);
|
|
148
126
|
|
|
149
127
|
if (err < 0) {
|
|
150
128
|
url_destroy(&base);
|
|
@@ -155,22 +133,17 @@ bare_url_parse(js_env_t *env, js_callback_info_t *info) {
|
|
|
155
133
|
}
|
|
156
134
|
}
|
|
157
135
|
|
|
158
|
-
|
|
159
|
-
err = js_get_value_string_utf8(env, argv[0], NULL, 0, &len);
|
|
160
|
-
assert(err == 0);
|
|
161
|
-
|
|
162
|
-
utf8_t *input = malloc(len);
|
|
163
|
-
err = js_get_value_string_utf8(env, argv[0], input, len, NULL);
|
|
164
|
-
assert(err == 0);
|
|
136
|
+
utf8_t stack[BARE_URL_STACK_STRING_MAX];
|
|
165
137
|
|
|
166
|
-
|
|
138
|
+
bare_url_string_t input;
|
|
139
|
+
bare_url__read_string(env, argv[0], stack, sizeof(stack), &input);
|
|
167
140
|
|
|
168
141
|
url_t url;
|
|
169
142
|
url_init(&url);
|
|
170
143
|
|
|
171
|
-
err = url_parse(&url, input, len, has_base ? &base : NULL);
|
|
144
|
+
err = url_parse(&url, input.data, input.len, has_base ? &base : NULL);
|
|
172
145
|
|
|
173
|
-
|
|
146
|
+
bare_url__free_string(env, &input);
|
|
174
147
|
|
|
175
148
|
if (err < 0) {
|
|
176
149
|
url_destroy(&base);
|
|
@@ -182,7 +155,7 @@ bare_url_parse(js_env_t *env, js_callback_info_t *info) {
|
|
|
182
155
|
}
|
|
183
156
|
|
|
184
157
|
js_value_t *href;
|
|
185
|
-
err =
|
|
158
|
+
err = js_create_string_latin1(env, (const latin1_t *) url.href.data, url.href.len, &href);
|
|
186
159
|
assert(err == 0);
|
|
187
160
|
|
|
188
161
|
uint32_t *components;
|
|
@@ -217,17 +190,14 @@ bare_url_can_parse(js_env_t *env, js_callback_info_t *info) {
|
|
|
217
190
|
url_init(&base);
|
|
218
191
|
|
|
219
192
|
if (has_base) {
|
|
220
|
-
|
|
221
|
-
err = js_get_value_string_utf8(env, argv[1], NULL, 0, &len);
|
|
222
|
-
assert(err == 0);
|
|
193
|
+
utf8_t stack[BARE_URL_STACK_STRING_MAX];
|
|
223
194
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
assert(err == 0);
|
|
195
|
+
bare_url_string_t input;
|
|
196
|
+
bare_url__read_string(env, argv[1], stack, sizeof(stack), &input);
|
|
227
197
|
|
|
228
|
-
err = url_parse(&base, input, len, NULL);
|
|
198
|
+
err = url_parse(&base, input.data, input.len, NULL);
|
|
229
199
|
|
|
230
|
-
|
|
200
|
+
bare_url__free_string(env, &input);
|
|
231
201
|
|
|
232
202
|
if (err < 0) {
|
|
233
203
|
url_destroy(&base);
|
|
@@ -240,20 +210,17 @@ bare_url_can_parse(js_env_t *env, js_callback_info_t *info) {
|
|
|
240
210
|
}
|
|
241
211
|
}
|
|
242
212
|
|
|
243
|
-
|
|
244
|
-
err = js_get_value_string_utf8(env, argv[0], NULL, 0, &len);
|
|
245
|
-
assert(err == 0);
|
|
213
|
+
utf8_t stack[BARE_URL_STACK_STRING_MAX];
|
|
246
214
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
assert(err == 0);
|
|
215
|
+
bare_url_string_t input;
|
|
216
|
+
bare_url__read_string(env, argv[0], stack, sizeof(stack), &input);
|
|
250
217
|
|
|
251
218
|
url_t url;
|
|
252
219
|
url_init(&url);
|
|
253
220
|
|
|
254
|
-
err = url_parse(&url, input, len, has_base ? &base : NULL);
|
|
221
|
+
err = url_parse(&url, input.data, input.len, has_base ? &base : NULL);
|
|
255
222
|
|
|
256
|
-
|
|
223
|
+
bare_url__free_string(env, &input);
|
|
257
224
|
|
|
258
225
|
url_destroy(&base);
|
|
259
226
|
url_destroy(&url);
|
|
@@ -265,56 +232,6 @@ bare_url_can_parse(js_env_t *env, js_callback_info_t *info) {
|
|
|
265
232
|
return result;
|
|
266
233
|
}
|
|
267
234
|
|
|
268
|
-
static js_value_t *
|
|
269
|
-
bare_url_serialize_form_urlencoded(js_env_t *env, js_callback_info_t *info) {
|
|
270
|
-
int err;
|
|
271
|
-
|
|
272
|
-
size_t argc = 1;
|
|
273
|
-
js_value_t *argv[1];
|
|
274
|
-
|
|
275
|
-
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
276
|
-
assert(err == 0);
|
|
277
|
-
|
|
278
|
-
assert(argc == 1);
|
|
279
|
-
|
|
280
|
-
uint32_t len;
|
|
281
|
-
err = js_get_array_length(env, argv[0], &len);
|
|
282
|
-
assert(err == 0);
|
|
283
|
-
|
|
284
|
-
utf8_string_t result;
|
|
285
|
-
utf8_string_init(&result);
|
|
286
|
-
|
|
287
|
-
for (uint32_t i = 0; i + 1 < len; i += 2) {
|
|
288
|
-
js_value_t *pair[2];
|
|
289
|
-
|
|
290
|
-
err = js_get_element(env, argv[0], i, &pair[0]);
|
|
291
|
-
assert(err == 0);
|
|
292
|
-
|
|
293
|
-
err = js_get_element(env, argv[0], i + 1, &pair[1]);
|
|
294
|
-
assert(err == 0);
|
|
295
|
-
|
|
296
|
-
if (i != 0) {
|
|
297
|
-
err = utf8_string_append_character(&result, '&');
|
|
298
|
-
assert(err == 0);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
bare_url__serialize_form_urlencoded_component(env, pair[0], &result);
|
|
302
|
-
|
|
303
|
-
err = utf8_string_append_character(&result, '=');
|
|
304
|
-
assert(err == 0);
|
|
305
|
-
|
|
306
|
-
bare_url__serialize_form_urlencoded_component(env, pair[1], &result);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
js_value_t *output;
|
|
310
|
-
err = js_create_string_utf8(env, result.data, result.len, &output);
|
|
311
|
-
assert(err == 0);
|
|
312
|
-
|
|
313
|
-
utf8_string_destroy(&result);
|
|
314
|
-
|
|
315
|
-
return output;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
235
|
static js_value_t *
|
|
319
236
|
bare_url_exports(js_env_t *env, js_value_t *exports) {
|
|
320
237
|
int err;
|
|
@@ -330,7 +247,6 @@ bare_url_exports(js_env_t *env, js_value_t *exports) {
|
|
|
330
247
|
|
|
331
248
|
V("parse", bare_url_parse)
|
|
332
249
|
V("canParse", bare_url_can_parse)
|
|
333
|
-
V("serializeSearchParams", bare_url_serialize_form_urlencoded)
|
|
334
250
|
#undef V
|
|
335
251
|
|
|
336
252
|
return exports;
|
package/index.js
CHANGED
|
@@ -7,6 +7,20 @@ const kind = Symbol.for('bare.url.kind')
|
|
|
7
7
|
|
|
8
8
|
const isWindows = Bare.platform === 'win32'
|
|
9
9
|
|
|
10
|
+
// Scratch buffer that the binding writes the parsed component offsets into. A
|
|
11
|
+
// single shared buffer is reused across every parse.
|
|
12
|
+
//
|
|
13
|
+
// The offsets are copied out into fields on the URL immediately after a
|
|
14
|
+
// successful parse, so nothing observes the buffer across calls.
|
|
15
|
+
const components = new Uint32Array(8)
|
|
16
|
+
|
|
17
|
+
// The value used for a component that is not present in the URL.
|
|
18
|
+
const unset = 0xffffffff
|
|
19
|
+
|
|
20
|
+
// The characters that pathToFileURL() has to percent-encode itself. A backslash
|
|
21
|
+
// is a path separator on Windows and so is left alone there.
|
|
22
|
+
const reserved = isWindows ? /[%#?\n\r\t]/ : /[%#?\n\r\t\\]/
|
|
23
|
+
|
|
10
24
|
class URL {
|
|
11
25
|
static get [kind]() {
|
|
12
26
|
return 0 // Compatibility version
|
|
@@ -19,11 +33,17 @@ class URL {
|
|
|
19
33
|
|
|
20
34
|
if (base !== undefined) base = String(base)
|
|
21
35
|
|
|
22
|
-
this.
|
|
36
|
+
this._href = undefined
|
|
37
|
+
this._schemeEnd = 0
|
|
38
|
+
this._usernameEnd = 0
|
|
39
|
+
this._hostStart = 0
|
|
40
|
+
this._hostEnd = 0
|
|
41
|
+
this._pathStart = 0
|
|
42
|
+
this._queryStart = 0
|
|
43
|
+
this._fragmentStart = 0
|
|
44
|
+
this._params = null
|
|
23
45
|
|
|
24
46
|
this._parse(input, base, opts.throw !== false)
|
|
25
|
-
|
|
26
|
-
if (this._href) this._params = new URLSearchParams(this.search, this)
|
|
27
47
|
}
|
|
28
48
|
|
|
29
49
|
get [kind]() {
|
|
@@ -39,23 +59,23 @@ class URL {
|
|
|
39
59
|
set href(value) {
|
|
40
60
|
this._update(value)
|
|
41
61
|
|
|
42
|
-
this._params._parse(this.search)
|
|
62
|
+
if (this._params) this._params._parse(this.search)
|
|
43
63
|
}
|
|
44
64
|
|
|
45
65
|
// https://url.spec.whatwg.org/#dom-url-protocol
|
|
46
66
|
|
|
47
67
|
get protocol() {
|
|
48
|
-
return this._slice(0, this.
|
|
68
|
+
return this._slice(0, this._schemeEnd) + ':'
|
|
49
69
|
}
|
|
50
70
|
|
|
51
71
|
set protocol(value) {
|
|
52
|
-
this._update(this._replace(value.replace(/:+$/, ''), 0, this.
|
|
72
|
+
this._update(this._replace(value.replace(/:+$/, ''), 0, this._schemeEnd))
|
|
53
73
|
}
|
|
54
74
|
|
|
55
75
|
// https://url.spec.whatwg.org/#dom-url-username
|
|
56
76
|
|
|
57
77
|
get username() {
|
|
58
|
-
return this._slice(this.
|
|
78
|
+
return this._slice(this._schemeEnd + 3 /* :// */, this._usernameEnd)
|
|
59
79
|
}
|
|
60
80
|
|
|
61
81
|
set username(value) {
|
|
@@ -65,13 +85,13 @@ class URL {
|
|
|
65
85
|
|
|
66
86
|
if (this.username === '') value += '@'
|
|
67
87
|
|
|
68
|
-
this._update(this._replace(value, this.
|
|
88
|
+
this._update(this._replace(value, this._schemeEnd + 3 /* :// */, this._usernameEnd))
|
|
69
89
|
}
|
|
70
90
|
|
|
71
91
|
// https://url.spec.whatwg.org/#dom-url-password
|
|
72
92
|
|
|
73
93
|
get password() {
|
|
74
|
-
return this._href.slice(this.
|
|
94
|
+
return this._href.slice(this._usernameEnd + 1 /* : */, this._hostStart - 1 /* @ */)
|
|
75
95
|
}
|
|
76
96
|
|
|
77
97
|
set password(value) {
|
|
@@ -79,8 +99,8 @@ class URL {
|
|
|
79
99
|
return
|
|
80
100
|
}
|
|
81
101
|
|
|
82
|
-
let start = this.
|
|
83
|
-
let end = this.
|
|
102
|
+
let start = this._usernameEnd + 1 /* : */
|
|
103
|
+
let end = this._hostStart - 1 /* @ */
|
|
84
104
|
|
|
85
105
|
if (this.password === '') {
|
|
86
106
|
value = ':' + value
|
|
@@ -98,7 +118,7 @@ class URL {
|
|
|
98
118
|
// https://url.spec.whatwg.org/#dom-url-host
|
|
99
119
|
|
|
100
120
|
get host() {
|
|
101
|
-
return this._slice(this.
|
|
121
|
+
return this._slice(this._hostStart, this._pathStart)
|
|
102
122
|
}
|
|
103
123
|
|
|
104
124
|
set host(value) {
|
|
@@ -107,14 +127,14 @@ class URL {
|
|
|
107
127
|
}
|
|
108
128
|
|
|
109
129
|
this._update(
|
|
110
|
-
this._replace(value, this.
|
|
130
|
+
this._replace(value, this._hostStart, value.includes(':') ? this._pathStart : this._hostEnd)
|
|
111
131
|
)
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
// https://url.spec.whatwg.org/#dom-url-hostname
|
|
115
135
|
|
|
116
136
|
get hostname() {
|
|
117
|
-
return this._slice(this.
|
|
137
|
+
return this._slice(this._hostStart, this._hostEnd)
|
|
118
138
|
}
|
|
119
139
|
|
|
120
140
|
set hostname(value) {
|
|
@@ -122,13 +142,13 @@ class URL {
|
|
|
122
142
|
return
|
|
123
143
|
}
|
|
124
144
|
|
|
125
|
-
this._update(this._replace(value, this.
|
|
145
|
+
this._update(this._replace(value, this._hostStart, this._hostEnd))
|
|
126
146
|
}
|
|
127
147
|
|
|
128
148
|
// https://url.spec.whatwg.org/#dom-url-port
|
|
129
149
|
|
|
130
150
|
get port() {
|
|
131
|
-
return this._slice(this.
|
|
151
|
+
return this._slice(this._hostEnd + 1 /* : */, this._pathStart)
|
|
132
152
|
}
|
|
133
153
|
|
|
134
154
|
set port(value) {
|
|
@@ -136,20 +156,20 @@ class URL {
|
|
|
136
156
|
return
|
|
137
157
|
}
|
|
138
158
|
|
|
139
|
-
let start = this.
|
|
159
|
+
let start = this._hostEnd + 1 /* : */
|
|
140
160
|
|
|
141
161
|
if (this.port === '') {
|
|
142
162
|
value = ':' + value
|
|
143
163
|
start--
|
|
144
164
|
}
|
|
145
165
|
|
|
146
|
-
this._update(this._replace(value, start, this.
|
|
166
|
+
this._update(this._replace(value, start, this._pathStart))
|
|
147
167
|
}
|
|
148
168
|
|
|
149
169
|
// https://url.spec.whatwg.org/#dom-url-pathname
|
|
150
170
|
|
|
151
171
|
get pathname() {
|
|
152
|
-
return this._slice(this.
|
|
172
|
+
return this._slice(this._pathStart, this._queryStart - 1 /* ? */)
|
|
153
173
|
}
|
|
154
174
|
|
|
155
175
|
set pathname(value) {
|
|
@@ -161,41 +181,45 @@ class URL {
|
|
|
161
181
|
value = '/' + value
|
|
162
182
|
}
|
|
163
183
|
|
|
164
|
-
this._update(this._replace(value, this.
|
|
184
|
+
this._update(this._replace(value, this._pathStart, this._queryStart - 1 /* ? */))
|
|
165
185
|
}
|
|
166
186
|
|
|
167
187
|
// https://url.spec.whatwg.org/#dom-url-search
|
|
168
188
|
|
|
169
189
|
get search() {
|
|
170
|
-
return this._slice(this.
|
|
190
|
+
return this._slice(this._queryStart - 1 /* ? */, this._fragmentStart - 1 /* # */)
|
|
171
191
|
}
|
|
172
192
|
|
|
173
193
|
set search(value) {
|
|
174
194
|
if (value && value[0] !== '?') value = '?' + value
|
|
175
195
|
|
|
176
196
|
this._update(
|
|
177
|
-
this._replace(value, this.
|
|
197
|
+
this._replace(value, this._queryStart - 1 /* ? */, this._fragmentStart - 1 /* # */)
|
|
178
198
|
)
|
|
179
199
|
|
|
180
|
-
this._params._parse(this.search)
|
|
200
|
+
if (this._params) this._params._parse(this.search)
|
|
181
201
|
}
|
|
182
202
|
|
|
183
203
|
// https://url.spec.whatwg.org/#dom-url-searchparams
|
|
184
204
|
|
|
185
205
|
get searchParams() {
|
|
206
|
+
if (this._params === null) {
|
|
207
|
+
this._params = new URLSearchParams(this.search, this)
|
|
208
|
+
}
|
|
209
|
+
|
|
186
210
|
return this._params
|
|
187
211
|
}
|
|
188
212
|
|
|
189
213
|
// https://url.spec.whatwg.org/#dom-url-hash
|
|
190
214
|
|
|
191
215
|
get hash() {
|
|
192
|
-
return this._slice(this.
|
|
216
|
+
return this._slice(this._fragmentStart - 1 /* # */)
|
|
193
217
|
}
|
|
194
218
|
|
|
195
219
|
set hash(value) {
|
|
196
220
|
if (value && value[0] !== '#') value = '#' + value
|
|
197
221
|
|
|
198
|
-
this._update(this._replace(value, this.
|
|
222
|
+
this._update(this._replace(value, this._fragmentStart - 1 /* # */))
|
|
199
223
|
}
|
|
200
224
|
|
|
201
225
|
toString() {
|
|
@@ -233,18 +257,32 @@ class URL {
|
|
|
233
257
|
}
|
|
234
258
|
|
|
235
259
|
_parse(input, base, shouldThrow) {
|
|
260
|
+
let href
|
|
261
|
+
|
|
236
262
|
try {
|
|
237
|
-
|
|
238
|
-
String(input),
|
|
239
|
-
base ? String(base) : null,
|
|
240
|
-
this._components,
|
|
241
|
-
shouldThrow
|
|
242
|
-
)
|
|
263
|
+
href = binding.parse(input, base || null, components, shouldThrow)
|
|
243
264
|
} catch (err) {
|
|
244
265
|
if (err instanceof TypeError) throw err
|
|
245
266
|
|
|
246
267
|
throw errors.INVALID_URL(`Invalid URL '${input}'`, input)
|
|
247
268
|
}
|
|
269
|
+
|
|
270
|
+
if (href === undefined) return
|
|
271
|
+
|
|
272
|
+
this._href = href
|
|
273
|
+
this._schemeEnd = components[0]
|
|
274
|
+
this._usernameEnd = components[1]
|
|
275
|
+
this._hostStart = components[2]
|
|
276
|
+
this._hostEnd = components[3]
|
|
277
|
+
this._pathStart = components[5]
|
|
278
|
+
|
|
279
|
+
const queryStart = components[6]
|
|
280
|
+
const fragmentStart = components[7]
|
|
281
|
+
|
|
282
|
+
const end = href.length + 1
|
|
283
|
+
|
|
284
|
+
this._queryStart = queryStart === unset ? end : queryStart
|
|
285
|
+
this._fragmentStart = fragmentStart === unset ? end : fragmentStart
|
|
248
286
|
}
|
|
249
287
|
|
|
250
288
|
_update(input) {
|
|
@@ -301,27 +339,35 @@ exports.fileURLToPath = function fileURLToPath(url) {
|
|
|
301
339
|
throw errors.INVALID_URL_SCHEME('The URL must use the file: protocol')
|
|
302
340
|
}
|
|
303
341
|
|
|
304
|
-
if (isWindows) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
} else {
|
|
311
|
-
if (url.hostname) {
|
|
312
|
-
throw errors.INVALID_FILE_URL_HOST("The file: URL host must be 'localhost' or empty")
|
|
313
|
-
}
|
|
342
|
+
if (!isWindows && url.hostname) {
|
|
343
|
+
throw errors.INVALID_FILE_URL_HOST("The file: URL host must be 'localhost' or empty")
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const encoded = url.pathname
|
|
314
347
|
|
|
315
|
-
|
|
348
|
+
// Every check below looks for a percent encoded sequence, as does the decoding
|
|
349
|
+
// that follows, so a path without any can skip all of them.
|
|
350
|
+
const hasEncoded = encoded.includes('%')
|
|
351
|
+
|
|
352
|
+
if (hasEncoded) {
|
|
353
|
+
if (isWindows) {
|
|
354
|
+
if (/%2f|%5c/i.test(encoded)) {
|
|
355
|
+
throw errors.INVALID_FILE_URL_PATH(
|
|
356
|
+
'The file: URL path must not include encoded \\ or / characters'
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
} else if (/%2f/i.test(encoded)) {
|
|
316
360
|
throw errors.INVALID_FILE_URL_PATH('The file: URL path must not include encoded / characters')
|
|
317
361
|
}
|
|
318
|
-
}
|
|
319
362
|
|
|
320
|
-
|
|
321
|
-
|
|
363
|
+
if (/%00/i.test(encoded)) {
|
|
364
|
+
throw errors.INVALID_FILE_URL_PATH(
|
|
365
|
+
'The file: URL path must not include encoded NUL characters'
|
|
366
|
+
)
|
|
367
|
+
}
|
|
322
368
|
}
|
|
323
369
|
|
|
324
|
-
const pathname = path.normalize(decodeURIComponent(
|
|
370
|
+
const pathname = path.normalize(hasEncoded ? decodeURIComponent(encoded) : encoded)
|
|
325
371
|
|
|
326
372
|
if (isWindows) {
|
|
327
373
|
if (url.hostname) return '\\\\' + url.hostname + pathname
|
|
@@ -347,16 +393,20 @@ exports.pathToFileURL = function pathToFileURL(pathname) {
|
|
|
347
393
|
resolved += '\\'
|
|
348
394
|
}
|
|
349
395
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
396
|
+
// Paths hardly ever contain any of these, so one pass to rule them out is
|
|
397
|
+
// cheaper than the six or seven replacements it stands in for.
|
|
398
|
+
if (reserved.test(resolved)) {
|
|
399
|
+
resolved = resolved
|
|
400
|
+
.replaceAll('%', '%25') // Must be first
|
|
401
|
+
.replaceAll('#', '%23')
|
|
402
|
+
.replaceAll('?', '%3f')
|
|
403
|
+
.replaceAll('\n', '%0a')
|
|
404
|
+
.replaceAll('\r', '%0d')
|
|
405
|
+
.replaceAll('\t', '%09')
|
|
357
406
|
|
|
358
|
-
|
|
359
|
-
|
|
407
|
+
if (!isWindows) {
|
|
408
|
+
resolved = resolved.replaceAll('\\', '%5c')
|
|
409
|
+
}
|
|
360
410
|
}
|
|
361
411
|
|
|
362
412
|
return new URL('file:' + resolved)
|
package/lib/url-search-params.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const binding = require('../binding')
|
|
2
|
-
|
|
3
1
|
const kind = Symbol.for('bare.url.search-params.kind')
|
|
4
2
|
|
|
5
3
|
class URLSearchParams {
|
|
@@ -11,17 +9,21 @@ class URLSearchParams {
|
|
|
11
9
|
|
|
12
10
|
// https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams
|
|
13
11
|
constructor(init, url = null) {
|
|
14
|
-
this._params =
|
|
12
|
+
this._params = null
|
|
15
13
|
|
|
16
14
|
if (url) URLSearchParams._urls.set(this, url)
|
|
17
15
|
|
|
18
16
|
if (typeof init === 'string') {
|
|
19
17
|
this._parse(init)
|
|
20
|
-
} else
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
} else {
|
|
19
|
+
this._params = new Map()
|
|
20
|
+
|
|
21
|
+
if (init) {
|
|
22
|
+
for (const [name, value] of typeof init[Symbol.iterator] === 'function'
|
|
23
|
+
? init
|
|
24
|
+
: Object.entries(init)) {
|
|
25
|
+
this.append(name, value)
|
|
26
|
+
}
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
}
|
|
@@ -147,41 +149,51 @@ class URLSearchParams {
|
|
|
147
149
|
|
|
148
150
|
// https://url.spec.whatwg.org/#concept-urlencoded-parser
|
|
149
151
|
_parse(input) {
|
|
150
|
-
|
|
152
|
+
const params = new Map()
|
|
151
153
|
|
|
152
|
-
this._params =
|
|
154
|
+
this._params = params
|
|
153
155
|
|
|
154
|
-
|
|
155
|
-
if (sequence.length === 0) continue
|
|
156
|
+
const len = input.length
|
|
156
157
|
|
|
157
|
-
|
|
158
|
+
let start = input.charCodeAt(0) === 0x3f /* ? */ ? 1 : 0
|
|
158
159
|
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
while (start < len) {
|
|
161
|
+
let end = input.indexOf('&', start)
|
|
162
|
+
if (end === -1) end = len
|
|
161
163
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
+
if (end !== start) {
|
|
165
|
+
let split = input.indexOf('=', start)
|
|
166
|
+
if (split === -1 || split > end) split = end
|
|
164
167
|
|
|
165
|
-
|
|
168
|
+
const name = decode(input.slice(start, split))
|
|
169
|
+
const value = split === end ? '' : decode(input.slice(split + 1, end))
|
|
170
|
+
|
|
171
|
+
const list = params.get(name)
|
|
166
172
|
|
|
167
|
-
|
|
168
|
-
list
|
|
169
|
-
this._params.set(name, list)
|
|
173
|
+
if (list === undefined) params.set(name, [value])
|
|
174
|
+
else list.push(value)
|
|
170
175
|
}
|
|
171
176
|
|
|
172
|
-
|
|
177
|
+
start = end + 1
|
|
173
178
|
}
|
|
174
179
|
}
|
|
175
180
|
|
|
176
181
|
// https://url.spec.whatwg.org/#concept-urlencoded-serializer
|
|
177
182
|
_serialize() {
|
|
178
|
-
|
|
183
|
+
let result = ''
|
|
184
|
+
let separator = ''
|
|
179
185
|
|
|
180
186
|
for (const [name, values] of this._params) {
|
|
181
|
-
|
|
187
|
+
// The name is shared by all of its values, so it is only encoded once.
|
|
188
|
+
const encoded = encode(String(name))
|
|
189
|
+
|
|
190
|
+
for (const value of values) {
|
|
191
|
+
result += separator + encoded + '=' + encode(String(value))
|
|
192
|
+
separator = '&'
|
|
193
|
+
}
|
|
182
194
|
}
|
|
183
195
|
|
|
184
|
-
return
|
|
196
|
+
return result
|
|
185
197
|
}
|
|
186
198
|
}
|
|
187
199
|
|
|
@@ -192,3 +204,66 @@ exports.isURLSearchParams = function isURLSearchParams(value) {
|
|
|
192
204
|
|
|
193
205
|
return typeof value === 'object' && value !== null && value[kind] === URLSearchParams[kind]
|
|
194
206
|
}
|
|
207
|
+
|
|
208
|
+
// https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer
|
|
209
|
+
//
|
|
210
|
+
// Everything except ASCII alphanumerics and `*-._` is percent-encoded, and a
|
|
211
|
+
// space becomes `+` rather than `%20`. Names and values usually consist entirely
|
|
212
|
+
// of characters that are left alone, so a component is tested as a whole before
|
|
213
|
+
// any of it is rewritten.
|
|
214
|
+
const unencoded = /^[\w*.-]*$/
|
|
215
|
+
|
|
216
|
+
// As above, but allowing spaces, which need nothing more than turning into `+`.
|
|
217
|
+
// Spaces are by far the most common reason for a component to need rewriting at
|
|
218
|
+
// all, so recognizing them here spares those components a trip through
|
|
219
|
+
// encodeURIComponent().
|
|
220
|
+
const spaced = /^[\w*. -]*$/
|
|
221
|
+
|
|
222
|
+
// The characters that encodeURIComponent() leaves alone but the urlencoded byte
|
|
223
|
+
// serializer does not. Two patterns are needed because a global regular
|
|
224
|
+
// expression cannot be shared between test() and replace(), as test() advances
|
|
225
|
+
// its lastIndex.
|
|
226
|
+
const extra = /[!'()~]/
|
|
227
|
+
const extraAll = /[!'()~]/g
|
|
228
|
+
|
|
229
|
+
const escapes = {
|
|
230
|
+
'!': '%21',
|
|
231
|
+
"'": '%27',
|
|
232
|
+
'(': '%28',
|
|
233
|
+
')': '%29',
|
|
234
|
+
'~': '%7E'
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// A surrogate that is not part of a pair, and so does not encode a scalar value.
|
|
238
|
+
const lone = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g
|
|
239
|
+
|
|
240
|
+
function encode(component) {
|
|
241
|
+
if (unencoded.test(component)) return component
|
|
242
|
+
|
|
243
|
+
if (spaced.test(component)) return component.replaceAll(' ', '+')
|
|
244
|
+
|
|
245
|
+
let encoded
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
encoded = encodeURIComponent(component)
|
|
249
|
+
} catch {
|
|
250
|
+
// encodeURIComponent() rejects lone surrogates, whereas the UTF-8 encoder the
|
|
251
|
+
// serializer is defined in terms of replaces them with U+FFFD.
|
|
252
|
+
encoded = encodeURIComponent(component.replace(lone, '\ufffd'))
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (encoded.includes('%20')) encoded = encoded.replaceAll('%20', '+')
|
|
256
|
+
|
|
257
|
+
if (extra.test(encoded)) encoded = encoded.replace(extraAll, (match) => escapes[match])
|
|
258
|
+
|
|
259
|
+
return encoded
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// https://url.spec.whatwg.org/#percent-decode-after-encoding
|
|
263
|
+
function decode(component) {
|
|
264
|
+
if (component.indexOf('+') !== -1) component = component.replaceAll('+', ' ')
|
|
265
|
+
|
|
266
|
+
if (component.indexOf('%') === -1) return component
|
|
267
|
+
|
|
268
|
+
return decodeURIComponent(component)
|
|
269
|
+
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|