@gjsify/querystring 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/src/index.ts ADDED
@@ -0,0 +1,574 @@
1
+ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
2
+ // https://github.com/denoland/deno_std/blob/main/node/querystring.ts
3
+
4
+ import { Buffer } from "buffer";
5
+ import { NodeURIError } from "./error.js";
6
+
7
+ const hexTable = new Array(256);
8
+ for (let i = 0; i < 256; ++i) {
9
+ hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase();
10
+ }
11
+
12
+ function encodeStr(
13
+ str: string,
14
+ noEscapeTable: Int8Array,
15
+ hexTable: string[],
16
+ ): string {
17
+ const len = str.length;
18
+ if (len === 0) return "";
19
+
20
+ let out = "";
21
+ let lastPos = 0;
22
+
23
+ for (let i = 0; i < len; i++) {
24
+ let c = str.charCodeAt(i);
25
+ // ASCII
26
+ if (c < 0x80) {
27
+ if (noEscapeTable[c] === 1) continue;
28
+ if (lastPos < i) out += str.slice(lastPos, i);
29
+ lastPos = i + 1;
30
+ out += hexTable[c];
31
+ continue;
32
+ }
33
+
34
+ if (lastPos < i) out += str.slice(lastPos, i);
35
+
36
+ // Multi-byte characters ...
37
+ if (c < 0x800) {
38
+ lastPos = i + 1;
39
+ out += hexTable[0xc0 | (c >> 6)] + hexTable[0x80 | (c & 0x3f)];
40
+ continue;
41
+ }
42
+ if (c < 0xd800 || c >= 0xe000) {
43
+ lastPos = i + 1;
44
+ out += hexTable[0xe0 | (c >> 12)] +
45
+ hexTable[0x80 | ((c >> 6) & 0x3f)] +
46
+ hexTable[0x80 | (c & 0x3f)];
47
+ continue;
48
+ }
49
+ // Surrogate pair
50
+ ++i;
51
+
52
+ // This branch should never happen because all URLSearchParams entries
53
+ // should already be converted to USVString. But, included for
54
+ // completion's sake anyway.
55
+ if (i >= len) throw new ERR_INVALID_URI();
56
+
57
+ const c2 = str.charCodeAt(i) & 0x3ff;
58
+
59
+ lastPos = i + 1;
60
+ c = 0x10000 + (((c & 0x3ff) << 10) | c2);
61
+ out += hexTable[0xf0 | (c >> 18)] +
62
+ hexTable[0x80 | ((c >> 12) & 0x3f)] +
63
+ hexTable[0x80 | ((c >> 6) & 0x3f)] +
64
+ hexTable[0x80 | (c & 0x3f)];
65
+ }
66
+ if (lastPos === 0) return str;
67
+ if (lastPos < len) return out + str.slice(lastPos);
68
+ return out;
69
+ }
70
+
71
+ export class ERR_INVALID_URI extends NodeURIError {
72
+ constructor() {
73
+ super("ERR_INVALID_URI", `URI malformed`);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Alias of querystring.parse()
79
+ * @legacy
80
+ */
81
+ export const decode = parse;
82
+
83
+ /**
84
+ * Alias of querystring.stringify()
85
+ * @legacy
86
+ */
87
+ export const encode = stringify;
88
+
89
+ /**
90
+ * replaces encodeURIComponent()
91
+ * @see https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4
92
+ */
93
+ function qsEscape(str: unknown): string {
94
+ if (typeof str !== "string") {
95
+ if (typeof str === "object") {
96
+ str = String(str);
97
+ } else {
98
+ str += "";
99
+ }
100
+ }
101
+ return encodeStr(str as string, noEscape, hexTable);
102
+ }
103
+
104
+ /**
105
+ * Performs URL percent-encoding on the given `str` in a manner that is optimized for the specific requirements of URL query strings.
106
+ * Used by `querystring.stringify()` and is generally not expected to be used directly.
107
+ * It is exported primarily to allow application code to provide a replacement percent-encoding implementation if necessary by assigning `querystring.escape` to an alternative function.
108
+ * @legacy
109
+ * @see Tested in `test-querystring-escape.js`
110
+ */
111
+ export const escape = qsEscape;
112
+
113
+ export interface ParsedUrlQuery {
114
+ [key: string]: string | string[] | undefined;
115
+ }
116
+
117
+ interface ParseOptions {
118
+ /** The function to use when decoding percent-encoded characters in the query string. */
119
+ decodeURIComponent?: (string: string) => string;
120
+ /** Specifies the maximum number of keys to parse. */
121
+ maxKeys?: number;
122
+ }
123
+
124
+ // deno-fmt-ignore
125
+ const isHexTable = new Int8Array([
126
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
127
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
128
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 - 47
129
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
130
+ 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64 - 79
131
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 - 95
132
+ 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96 - 111
133
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 112 - 127
134
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 ...
135
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
136
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
137
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
138
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
139
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
141
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ... 256
142
+ ]);
143
+
144
+ function charCodes(str: string): number[] {
145
+ const ret = new Array(str.length);
146
+ for (let i = 0; i < str.length; ++i) {
147
+ ret[i] = str.charCodeAt(i);
148
+ }
149
+ return ret;
150
+ }
151
+
152
+ function addKeyVal(
153
+ obj: ParsedUrlQuery,
154
+ key: string,
155
+ value: string,
156
+ keyEncoded: boolean,
157
+ valEncoded: boolean,
158
+ decode: (encodedURIComponent: string) => string,
159
+ ) {
160
+ if (key.length > 0 && keyEncoded) {
161
+ key = decode(key);
162
+ }
163
+ if (value.length > 0 && valEncoded) {
164
+ value = decode(value);
165
+ }
166
+
167
+ if (obj[key] === undefined) {
168
+ obj[key] = value;
169
+ } else {
170
+ const curValue = obj[key];
171
+ // A simple Array-specific property check is enough here to
172
+ // distinguish from a string value and is faster and still safe
173
+ // since we are generating all of the values being assigned.
174
+ if ((curValue as string[]).pop) {
175
+ (curValue as string[])[curValue!.length] = value;
176
+ } else {
177
+ obj[key] = [curValue as string, value];
178
+ }
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Parses a URL query string into a collection of key and value pairs.
184
+ * @param str The URL query string to parse
185
+ * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'.
186
+ * @param eq The substring used to delimit keys and values in the query string. Default: '='.
187
+ * @param options The parse options
188
+ * @param options.decodeURIComponent The function to use when decoding percent-encoded characters in the query string. Default: `querystring.unescape()`.
189
+ * @param options.maxKeys Specifies the maximum number of keys to parse. Specify `0` to remove key counting limitations. Default: `1000`.
190
+ * @legacy
191
+ * @see Tested in test-querystring.js
192
+ */
193
+ export function parse(
194
+ str: string,
195
+ sep = "&",
196
+ eq = "=",
197
+ { decodeURIComponent = unescape, maxKeys = 1000 }: ParseOptions = {},
198
+ ): ParsedUrlQuery {
199
+ const obj: ParsedUrlQuery = Object.create(null);
200
+
201
+ if (typeof str !== "string" || str.length === 0) {
202
+ return obj;
203
+ }
204
+
205
+ const sepCodes = (!sep ? [38] /* & */ : charCodes(String(sep)));
206
+ const eqCodes = (!eq ? [61] /* = */ : charCodes(String(eq)));
207
+ const sepLen = sepCodes.length;
208
+ const eqLen = eqCodes.length;
209
+
210
+ let pairs = 1000;
211
+ if (typeof maxKeys === "number") {
212
+ // -1 is used in place of a value like Infinity for meaning
213
+ // "unlimited pairs" because of additional checks V8 (at least as of v5.4)
214
+ // has to do when using variables that contain values like Infinity. Since
215
+ // `pairs` is always decremented and checked explicitly for 0, -1 works
216
+ // effectively the same as Infinity, while providing a significant
217
+ // performance boost.
218
+ pairs = maxKeys > 0 ? maxKeys : -1;
219
+ }
220
+
221
+ let decode = unescape;
222
+ if (decodeURIComponent) {
223
+ decode = decodeURIComponent;
224
+ }
225
+ const customDecode = (decode !== unescape);
226
+
227
+ let lastPos = 0;
228
+ let sepIdx = 0;
229
+ let eqIdx = 0;
230
+ let key = "";
231
+ let value = "";
232
+ let keyEncoded = customDecode;
233
+ let valEncoded = customDecode;
234
+ const plusChar = (customDecode ? "%20" : " ");
235
+ let encodeCheck = 0;
236
+ for (let i = 0; i < str.length; ++i) {
237
+ const code = str.charCodeAt(i);
238
+
239
+ // Try matching key/value pair separator (e.g. '&')
240
+ if (code === sepCodes[sepIdx]) {
241
+ if (++sepIdx === sepLen) {
242
+ // Key/value pair separator match!
243
+ const end = i - sepIdx + 1;
244
+ if (eqIdx < eqLen) {
245
+ // We didn't find the (entire) key/value separator
246
+ if (lastPos < end) {
247
+ // Treat the substring as part of the key instead of the value
248
+ key += str.slice(lastPos, end);
249
+ } else if (key.length === 0) {
250
+ // We saw an empty substring between separators
251
+ if (--pairs === 0) {
252
+ return obj;
253
+ }
254
+ lastPos = i + 1;
255
+ sepIdx = eqIdx = 0;
256
+ continue;
257
+ }
258
+ } else if (lastPos < end) {
259
+ value += str.slice(lastPos, end);
260
+ }
261
+
262
+ addKeyVal(obj, key, value, keyEncoded, valEncoded, decode);
263
+
264
+ if (--pairs === 0) {
265
+ return obj;
266
+ }
267
+ key = value = "";
268
+ encodeCheck = 0;
269
+ lastPos = i + 1;
270
+ sepIdx = eqIdx = 0;
271
+ }
272
+ } else {
273
+ sepIdx = 0;
274
+ // Try matching key/value separator (e.g. '=') if we haven't already
275
+ if (eqIdx < eqLen) {
276
+ if (code === eqCodes[eqIdx]) {
277
+ if (++eqIdx === eqLen) {
278
+ // Key/value separator match!
279
+ const end = i - eqIdx + 1;
280
+ if (lastPos < end) {
281
+ key += str.slice(lastPos, end);
282
+ }
283
+ encodeCheck = 0;
284
+ lastPos = i + 1;
285
+ }
286
+ continue;
287
+ } else {
288
+ eqIdx = 0;
289
+ if (!keyEncoded) {
290
+ // Try to match an (valid) encoded byte once to minimize unnecessary
291
+ // calls to string decoding functions
292
+ if (code === 37 /* % */) {
293
+ encodeCheck = 1;
294
+ continue;
295
+ } else if (encodeCheck > 0) {
296
+ if (isHexTable[code] === 1) {
297
+ if (++encodeCheck === 3) {
298
+ keyEncoded = true;
299
+ }
300
+ continue;
301
+ } else {
302
+ encodeCheck = 0;
303
+ }
304
+ }
305
+ }
306
+ }
307
+ if (code === 43 /* + */) {
308
+ if (lastPos < i) {
309
+ key += str.slice(lastPos, i);
310
+ }
311
+ key += plusChar;
312
+ lastPos = i + 1;
313
+ continue;
314
+ }
315
+ }
316
+ if (code === 43 /* + */) {
317
+ if (lastPos < i) {
318
+ value += str.slice(lastPos, i);
319
+ }
320
+ value += plusChar;
321
+ lastPos = i + 1;
322
+ } else if (!valEncoded) {
323
+ // Try to match an (valid) encoded byte (once) to minimize unnecessary
324
+ // calls to string decoding functions
325
+ if (code === 37 /* % */) {
326
+ encodeCheck = 1;
327
+ } else if (encodeCheck > 0) {
328
+ if (isHexTable[code] === 1) {
329
+ if (++encodeCheck === 3) {
330
+ valEncoded = true;
331
+ }
332
+ } else {
333
+ encodeCheck = 0;
334
+ }
335
+ }
336
+ }
337
+ }
338
+ }
339
+
340
+ // Deal with any leftover key or value data
341
+ if (lastPos < str.length) {
342
+ if (eqIdx < eqLen) {
343
+ key += str.slice(lastPos);
344
+ } else if (sepIdx < sepLen) {
345
+ value += str.slice(lastPos);
346
+ }
347
+ } else if (eqIdx === 0 && key.length === 0) {
348
+ // We ended on an empty substring
349
+ return obj;
350
+ }
351
+
352
+ addKeyVal(obj, key, value, keyEncoded, valEncoded, decode);
353
+
354
+ return obj;
355
+ }
356
+
357
+ interface StringifyOptions {
358
+ /** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */
359
+ encodeURIComponent: (string: string) => string;
360
+ }
361
+
362
+ /**
363
+ * These characters do not need escaping when generating query strings:
364
+ * ! - . _ ~
365
+ * ' ( ) *
366
+ * digits
367
+ * alpha (uppercase)
368
+ * alpha (lowercase)
369
+ */
370
+ // deno-fmt-ignore
371
+ const noEscape = new Int8Array([
372
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
373
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
374
+ 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, // 32 - 47
375
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
376
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
377
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 80 - 95
378
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
379
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 112 - 127
380
+ ]);
381
+
382
+ // deno-lint-ignore no-explicit-any
383
+ function stringifyPrimitive(v: any): string {
384
+ if (typeof v === "string") {
385
+ return v;
386
+ }
387
+ if (typeof v === "number" && isFinite(v)) {
388
+ return "" + v;
389
+ }
390
+ if (typeof v === "bigint") {
391
+ return "" + v;
392
+ }
393
+ if (typeof v === "boolean") {
394
+ return v ? "true" : "false";
395
+ }
396
+ return "";
397
+ }
398
+
399
+ function encodeStringifiedCustom(
400
+ // deno-lint-ignore no-explicit-any
401
+ v: any,
402
+ encode: (string: string) => string,
403
+ ): string {
404
+ return encode(stringifyPrimitive(v));
405
+ }
406
+
407
+ // deno-lint-ignore no-explicit-any
408
+ function encodeStringified(v: any, encode: (string: string) => string): string {
409
+ if (typeof v === "string") {
410
+ return (v.length ? encode(v) : "");
411
+ }
412
+ if (typeof v === "number" && isFinite(v)) {
413
+ // Values >= 1e21 automatically switch to scientific notation which requires
414
+ // escaping due to the inclusion of a '+' in the output
415
+ return (Math.abs(v) < 1e21 ? "" + v : encode("" + v));
416
+ }
417
+ if (typeof v === "bigint") {
418
+ return "" + v;
419
+ }
420
+ if (typeof v === "boolean") {
421
+ return v ? "true" : "false";
422
+ }
423
+ return "";
424
+ }
425
+
426
+ /**
427
+ * Produces a URL query string from a given obj by iterating through the object's "own properties".
428
+ * @param obj The object to serialize into a URL query string.
429
+ * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'.
430
+ * @param eq The substring used to delimit keys and values in the query string. Default: '='.
431
+ * @param options The stringify options
432
+ * @param options.encodeURIComponent The function to use when converting URL-unsafe characters to percent-encoding in the query string. Default: `querystring.escape()`.
433
+ * @legacy
434
+ * @see Tested in `test-querystring.js`
435
+ */
436
+ export function stringify(
437
+ // deno-lint-ignore no-explicit-any
438
+ obj: Record<string, any>,
439
+ sep?: string,
440
+ eq?: string,
441
+ options?: StringifyOptions,
442
+ ): string {
443
+ sep ||= "&";
444
+ eq ||= "=";
445
+ const encode = options ? options.encodeURIComponent : qsEscape;
446
+ const convert = options ? encodeStringifiedCustom : encodeStringified;
447
+
448
+ if (obj !== null && typeof obj === "object") {
449
+ const keys = Object.keys(obj);
450
+ const len = keys.length;
451
+ let fields = "";
452
+ for (let i = 0; i < len; ++i) {
453
+ const k = keys[i];
454
+ const v = obj[k];
455
+ let ks = convert(k, encode);
456
+ ks += eq;
457
+
458
+ if (Array.isArray(v)) {
459
+ const vlen = v.length;
460
+ if (vlen === 0) continue;
461
+ if (fields) {
462
+ fields += sep;
463
+ }
464
+ for (let j = 0; j < vlen; ++j) {
465
+ if (j) {
466
+ fields += sep;
467
+ }
468
+ fields += ks;
469
+ fields += convert(v[j], encode);
470
+ }
471
+ } else {
472
+ if (fields) {
473
+ fields += sep;
474
+ }
475
+ fields += ks;
476
+ fields += convert(v, encode);
477
+ }
478
+ }
479
+ return fields;
480
+ }
481
+ return "";
482
+ }
483
+
484
+ // deno-fmt-ignore
485
+ const unhexTable = new Int8Array([
486
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 15
487
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 - 31
488
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 - 47
489
+ +0, +1, +2, +3, +4, +5, +6, +7, +8, +9, -1, -1, -1, -1, -1, -1, // 48 - 63
490
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64 - 79
491
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 - 95
492
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96 - 111
493
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112 - 127
494
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 ...
495
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
496
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
497
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
498
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
499
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
500
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
501
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 255
502
+ ]);
503
+
504
+ /**
505
+ * A safe fast alternative to decodeURIComponent
506
+ */
507
+ export function unescapeBuffer(s: string, decodeSpaces = false): Buffer {
508
+ const out = new Buffer(s.length);
509
+ let index = 0;
510
+ let outIndex = 0;
511
+ let currentChar;
512
+ let nextChar;
513
+ let hexHigh;
514
+ let hexLow;
515
+ const maxLength = s.length - 2;
516
+ // Flag to know if some hex chars have been decoded
517
+ let hasHex = false;
518
+ while (index < s.length) {
519
+ currentChar = s.charCodeAt(index);
520
+ if (currentChar === 43 /* '+' */ && decodeSpaces) {
521
+ out[outIndex++] = 32; // ' '
522
+ index++;
523
+ continue;
524
+ }
525
+ if (currentChar === 37 /* '%' */ && index < maxLength) {
526
+ currentChar = s.charCodeAt(++index);
527
+ hexHigh = unhexTable[currentChar];
528
+ if (!(hexHigh >= 0)) {
529
+ out[outIndex++] = 37; // '%'
530
+ continue;
531
+ } else {
532
+ nextChar = s.charCodeAt(++index);
533
+ hexLow = unhexTable[nextChar];
534
+ if (!(hexLow >= 0)) {
535
+ out[outIndex++] = 37; // '%'
536
+ index--;
537
+ } else {
538
+ hasHex = true;
539
+ currentChar = hexHigh * 16 + hexLow;
540
+ }
541
+ }
542
+ }
543
+ out[outIndex++] = currentChar;
544
+ index++;
545
+ }
546
+ return hasHex ? out.slice(0, outIndex) : out;
547
+ }
548
+
549
+ function qsUnescape(s: string): string {
550
+ try {
551
+ return decodeURIComponent(s);
552
+ } catch {
553
+ return unescapeBuffer(s).toString();
554
+ }
555
+ }
556
+
557
+ /**
558
+ * Performs decoding of URL percent-encoded characters on the given `str`.
559
+ * Used by `querystring.parse()` and is generally not expected to be used directly.
560
+ * It is exported primarily to allow application code to provide a replacement decoding implementation if necessary by assigning `querystring.unescape` to an alternative function.
561
+ * @legacy
562
+ * @see Tested in `test-querystring-escape.js`
563
+ */
564
+ export const unescape = qsUnescape;
565
+
566
+ export default {
567
+ parse,
568
+ stringify,
569
+ decode,
570
+ encode,
571
+ unescape,
572
+ escape,
573
+ unescapeBuffer,
574
+ };
package/src/test.mts ADDED
@@ -0,0 +1,4 @@
1
+
2
+ import { run } from '@gjsify/unit';
3
+ import testSuite from './index.spec.js';
4
+ run({testSuite});