@ohos-ports/curlconverter 4.12.0-beta.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.
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/dist/src/Headers.d.ts +23 -0
- package/dist/src/Headers.js +287 -0
- package/dist/src/Headers.js.map +1 -0
- package/dist/src/Query.d.ts +8 -0
- package/dist/src/Query.js +136 -0
- package/dist/src/Query.js.map +1 -0
- package/dist/src/Request.d.ts +200 -0
- package/dist/src/Request.js +1191 -0
- package/dist/src/Request.js.map +1 -0
- package/dist/src/Warnings.d.ts +15 -0
- package/dist/src/Warnings.js +111 -0
- package/dist/src/Warnings.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +333 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/curl/auth.d.ts +12 -0
- package/dist/src/curl/auth.js +40 -0
- package/dist/src/curl/auth.js.map +1 -0
- package/dist/src/curl/form.d.ts +28 -0
- package/dist/src/curl/form.js +218 -0
- package/dist/src/curl/form.js.map +1 -0
- package/dist/src/curl/opts.d.ts +2052 -0
- package/dist/src/curl/opts.js +1000 -0
- package/dist/src/curl/opts.js.map +1 -0
- package/dist/src/curl/url.d.ts +14 -0
- package/dist/src/curl/url.js +218 -0
- package/dist/src/curl/url.js.map +1 -0
- package/dist/src/generators/ansible.d.ts +5 -0
- package/dist/src/generators/ansible.js +282 -0
- package/dist/src/generators/ansible.js.map +1 -0
- package/dist/src/generators/c.d.ts +12 -0
- package/dist/src/generators/c.js +1230 -0
- package/dist/src/generators/c.js.map +1 -0
- package/dist/src/generators/cfml.d.ts +5 -0
- package/dist/src/generators/cfml.js +134 -0
- package/dist/src/generators/cfml.js.map +1 -0
- package/dist/src/generators/clojure.d.ts +7 -0
- package/dist/src/generators/clojure.js +436 -0
- package/dist/src/generators/clojure.js.map +1 -0
- package/dist/src/generators/csharp.d.ts +8 -0
- package/dist/src/generators/csharp.js +356 -0
- package/dist/src/generators/csharp.js.map +1 -0
- package/dist/src/generators/dart.d.ts +5 -0
- package/dist/src/generators/dart.js +273 -0
- package/dist/src/generators/dart.js.map +1 -0
- package/dist/src/generators/elixir.d.ts +8 -0
- package/dist/src/generators/elixir.js +296 -0
- package/dist/src/generators/elixir.js.map +1 -0
- package/dist/src/generators/go.d.ts +5 -0
- package/dist/src/generators/go.js +228 -0
- package/dist/src/generators/go.js.map +1 -0
- package/dist/src/generators/har.d.ts +7 -0
- package/dist/src/generators/har.js +115 -0
- package/dist/src/generators/har.js.map +1 -0
- package/dist/src/generators/http.d.ts +7 -0
- package/dist/src/generators/http.js +165 -0
- package/dist/src/generators/http.js.map +1 -0
- package/dist/src/generators/httpie.d.ts +5 -0
- package/dist/src/generators/httpie.js +446 -0
- package/dist/src/generators/httpie.js.map +1 -0
- package/dist/src/generators/java/httpurlconnection.d.ts +5 -0
- package/dist/src/generators/java/httpurlconnection.js +123 -0
- package/dist/src/generators/java/httpurlconnection.js.map +1 -0
- package/dist/src/generators/java/java.d.ts +8 -0
- package/dist/src/generators/java/java.js +247 -0
- package/dist/src/generators/java/java.js.map +1 -0
- package/dist/src/generators/java/jsoup.d.ts +5 -0
- package/dist/src/generators/java/jsoup.js +152 -0
- package/dist/src/generators/java/jsoup.js.map +1 -0
- package/dist/src/generators/java/okhttp.d.ts +5 -0
- package/dist/src/generators/java/okhttp.js +232 -0
- package/dist/src/generators/java/okhttp.js.map +1 -0
- package/dist/src/generators/javascript/axios.d.ts +5 -0
- package/dist/src/generators/javascript/axios.js +332 -0
- package/dist/src/generators/javascript/axios.js.map +1 -0
- package/dist/src/generators/javascript/got.d.ts +5 -0
- package/dist/src/generators/javascript/got.js +277 -0
- package/dist/src/generators/javascript/got.js.map +1 -0
- package/dist/src/generators/javascript/http.d.ts +8 -0
- package/dist/src/generators/javascript/http.js +183 -0
- package/dist/src/generators/javascript/http.js.map +1 -0
- package/dist/src/generators/javascript/javascript.d.ts +37 -0
- package/dist/src/generators/javascript/javascript.js +676 -0
- package/dist/src/generators/javascript/javascript.js.map +1 -0
- package/dist/src/generators/javascript/jquery.d.ts +15 -0
- package/dist/src/generators/javascript/jquery.js +297 -0
- package/dist/src/generators/javascript/jquery.js.map +1 -0
- package/dist/src/generators/javascript/ky.d.ts +7 -0
- package/dist/src/generators/javascript/ky.js +245 -0
- package/dist/src/generators/javascript/ky.js.map +1 -0
- package/dist/src/generators/javascript/request.d.ts +5 -0
- package/dist/src/generators/javascript/request.js +113 -0
- package/dist/src/generators/javascript/request.js.map +1 -0
- package/dist/src/generators/javascript/superagent.d.ts +10 -0
- package/dist/src/generators/javascript/superagent.js +301 -0
- package/dist/src/generators/javascript/superagent.js.map +1 -0
- package/dist/src/generators/javascript/xhr.d.ts +8 -0
- package/dist/src/generators/javascript/xhr.js +147 -0
- package/dist/src/generators/javascript/xhr.js.map +1 -0
- package/dist/src/generators/json.d.ts +45 -0
- package/dist/src/generators/json.js +197 -0
- package/dist/src/generators/json.js.map +1 -0
- package/dist/src/generators/julia.d.ts +8 -0
- package/dist/src/generators/julia.js +365 -0
- package/dist/src/generators/julia.js.map +1 -0
- package/dist/src/generators/kotlin.d.ts +8 -0
- package/dist/src/generators/kotlin.js +325 -0
- package/dist/src/generators/kotlin.js.map +1 -0
- package/dist/src/generators/lua.d.ts +5 -0
- package/dist/src/generators/lua.js +192 -0
- package/dist/src/generators/lua.js.map +1 -0
- package/dist/src/generators/matlab/common.d.ts +16 -0
- package/dist/src/generators/matlab/common.js +238 -0
- package/dist/src/generators/matlab/common.js.map +1 -0
- package/dist/src/generators/matlab/httpinterface.d.ts +2 -0
- package/dist/src/generators/matlab/httpinterface.js +211 -0
- package/dist/src/generators/matlab/httpinterface.js.map +1 -0
- package/dist/src/generators/matlab/matlab.d.ts +5 -0
- package/dist/src/generators/matlab/matlab.js +29 -0
- package/dist/src/generators/matlab/matlab.js.map +1 -0
- package/dist/src/generators/matlab/webservices.d.ts +2 -0
- package/dist/src/generators/matlab/webservices.js +191 -0
- package/dist/src/generators/matlab/webservices.js.map +1 -0
- package/dist/src/generators/objectivec.d.ts +8 -0
- package/dist/src/generators/objectivec.js +269 -0
- package/dist/src/generators/objectivec.js.map +1 -0
- package/dist/src/generators/ocaml.d.ts +8 -0
- package/dist/src/generators/ocaml.js +150 -0
- package/dist/src/generators/ocaml.js.map +1 -0
- package/dist/src/generators/perl.d.ts +9 -0
- package/dist/src/generators/perl.js +209 -0
- package/dist/src/generators/perl.js.map +1 -0
- package/dist/src/generators/php/guzzle.d.ts +5 -0
- package/dist/src/generators/php/guzzle.js +328 -0
- package/dist/src/generators/php/guzzle.js.map +1 -0
- package/dist/src/generators/php/php.d.ts +8 -0
- package/dist/src/generators/php/php.js +217 -0
- package/dist/src/generators/php/php.js.map +1 -0
- package/dist/src/generators/php/requests.d.ts +5 -0
- package/dist/src/generators/php/requests.js +89 -0
- package/dist/src/generators/php/requests.js.map +1 -0
- package/dist/src/generators/powershell.d.ts +11 -0
- package/dist/src/generators/powershell.js +377 -0
- package/dist/src/generators/powershell.js.map +1 -0
- package/dist/src/generators/python/http.d.ts +5 -0
- package/dist/src/generators/python/http.js +128 -0
- package/dist/src/generators/python/http.js.map +1 -0
- package/dist/src/generators/python/python.d.ts +22 -0
- package/dist/src/generators/python/python.js +1693 -0
- package/dist/src/generators/python/python.js.map +1 -0
- package/dist/src/generators/r/httr.d.ts +10 -0
- package/dist/src/generators/r/httr.js +298 -0
- package/dist/src/generators/r/httr.js.map +1 -0
- package/dist/src/generators/r/httr2.d.ts +5 -0
- package/dist/src/generators/r/httr2.js +214 -0
- package/dist/src/generators/r/httr2.js.map +1 -0
- package/dist/src/generators/ruby/httparty.d.ts +6 -0
- package/dist/src/generators/ruby/httparty.js +308 -0
- package/dist/src/generators/ruby/httparty.js.map +1 -0
- package/dist/src/generators/ruby/ruby.d.ts +14 -0
- package/dist/src/generators/ruby/ruby.js +515 -0
- package/dist/src/generators/ruby/ruby.js.map +1 -0
- package/dist/src/generators/rust.d.ts +8 -0
- package/dist/src/generators/rust.js +177 -0
- package/dist/src/generators/rust.js.map +1 -0
- package/dist/src/generators/swift.d.ts +8 -0
- package/dist/src/generators/swift.js +219 -0
- package/dist/src/generators/swift.js.map +1 -0
- package/dist/src/generators/wget.d.ts +8 -0
- package/dist/src/generators/wget.js +472 -0
- package/dist/src/generators/wget.js.map +1 -0
- package/dist/src/index.d.ts +53 -0
- package/dist/src/index.js +54 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/parse.d.ts +13 -0
- package/dist/src/parse.js +43 -0
- package/dist/src/parse.js.map +1 -0
- package/dist/src/shell/Parser.d.ts +4 -0
- package/dist/src/shell/Parser.js +6 -0
- package/dist/src/shell/Parser.js.map +1 -0
- package/dist/src/shell/Word.d.ts +48 -0
- package/dist/src/shell/Word.js +486 -0
- package/dist/src/shell/Word.js.map +1 -0
- package/dist/src/shell/tokenizer.d.ts +3 -0
- package/dist/src/shell/tokenizer.js +610 -0
- package/dist/src/shell/tokenizer.js.map +1 -0
- package/dist/src/shell/webParser.d.ts +4 -0
- package/dist/src/shell/webParser.js +15 -0
- package/dist/src/shell/webParser.js.map +1 -0
- package/dist/src/utils.d.ts +6 -0
- package/dist/src/utils.js +21 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/tools/compare-requests.d.ts +2 -0
- package/dist/tools/compare-requests.js +448 -0
- package/dist/tools/compare-requests.js.map +1 -0
- package/dist/tools/gen-test.d.ts +2 -0
- package/dist/tools/gen-test.js +114 -0
- package/dist/tools/gen-test.js.map +1 -0
- package/dist/tree-sitter-bash.wasm +0 -0
- package/index.d.ts +3 -0
- package/package.json +97 -0
- package/src/Headers.ts +334 -0
- package/src/Query.ts +147 -0
- package/src/Request.ts +1649 -0
- package/src/Warnings.ts +154 -0
- package/src/cli.ts +561 -0
- package/src/curl/auth.ts +45 -0
- package/src/curl/form.ts +297 -0
- package/src/curl/opts.ts +1417 -0
- package/src/curl/url.ts +241 -0
- package/src/generators/ansible.ts +364 -0
- package/src/generators/c.ts +1371 -0
- package/src/generators/cfml.ts +155 -0
- package/src/generators/clojure.ts +521 -0
- package/src/generators/csharp.ts +383 -0
- package/src/generators/dart.ts +293 -0
- package/src/generators/elixir.ts +349 -0
- package/src/generators/go.ts +269 -0
- package/src/generators/har.ts +147 -0
- package/src/generators/http.ts +194 -0
- package/src/generators/httpie.ts +506 -0
- package/src/generators/java/httpurlconnection.ts +151 -0
- package/src/generators/java/java.ts +280 -0
- package/src/generators/java/jsoup.ts +172 -0
- package/src/generators/java/okhttp.ts +257 -0
- package/src/generators/javascript/axios.ts +395 -0
- package/src/generators/javascript/got.ts +362 -0
- package/src/generators/javascript/http.ts +232 -0
- package/src/generators/javascript/javascript.ts +825 -0
- package/src/generators/javascript/jquery.ts +360 -0
- package/src/generators/javascript/ky.ts +315 -0
- package/src/generators/javascript/request.ts +152 -0
- package/src/generators/javascript/superagent.ts +373 -0
- package/src/generators/javascript/xhr.ts +192 -0
- package/src/generators/json.ts +293 -0
- package/src/generators/julia.ts +398 -0
- package/src/generators/kotlin.ts +355 -0
- package/src/generators/lua.ts +209 -0
- package/src/generators/matlab/common.ts +281 -0
- package/src/generators/matlab/httpinterface.ts +284 -0
- package/src/generators/matlab/matlab.ts +39 -0
- package/src/generators/matlab/webservices.ts +263 -0
- package/src/generators/objectivec.ts +297 -0
- package/src/generators/ocaml.ts +168 -0
- package/src/generators/perl.ts +237 -0
- package/src/generators/php/guzzle.ts +368 -0
- package/src/generators/php/php.ts +239 -0
- package/src/generators/php/requests.ts +103 -0
- package/src/generators/powershell.ts +430 -0
- package/src/generators/python/http.ts +167 -0
- package/src/generators/python/python.ts +1934 -0
- package/src/generators/r/httr.ts +328 -0
- package/src/generators/r/httr2.ts +268 -0
- package/src/generators/ruby/httparty.ts +351 -0
- package/src/generators/ruby/ruby.ts +564 -0
- package/src/generators/rust.ts +243 -0
- package/src/generators/swift.ts +235 -0
- package/src/generators/wget.ts +533 -0
- package/src/index.ts +89 -0
- package/src/parse.ts +72 -0
- package/src/shell/Parser.ts +8 -0
- package/src/shell/Word.ts +531 -0
- package/src/shell/tokenizer.ts +736 -0
- package/src/shell/webParser.ts +18 -0
- package/src/utils.ts +25 -0
- package/tools/compare-requests.ts +571 -0
- package/tools/extract_curl_args.py +511 -0
- package/tools/gen-test.ts +129 -0
package/package.json
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ohos-ports/curlconverter",
|
|
3
|
+
"version": "4.12.0-beta.0",
|
|
4
|
+
"description": "convert curl commands to Python, JavaScript, Go, PHP and more",
|
|
5
|
+
"homepage": "https://curlconverter.com",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Nick Carneiro",
|
|
8
|
+
"email": "nickc@trillworks.com",
|
|
9
|
+
"url": "https://trillworks.com"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/ohos-ports/ohos-ports.git",
|
|
14
|
+
"directory": "ports/curlconverter/4.12.0"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"curl",
|
|
19
|
+
"http",
|
|
20
|
+
"requests",
|
|
21
|
+
"python",
|
|
22
|
+
"converter"
|
|
23
|
+
],
|
|
24
|
+
"main": "dist/src/index.js",
|
|
25
|
+
"types": "dist/src/index.d.ts",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"jsesc": "^3.0.2",
|
|
29
|
+
"lossless-json": "^4.0.2",
|
|
30
|
+
"@ohos-ports/tree-sitter": "^0.25.2-beta.0",
|
|
31
|
+
"web-tree-sitter": "^0.24.3",
|
|
32
|
+
"yamljs": "^0.3.0",
|
|
33
|
+
"@ohos-ports/tree-sitter-bash": "^0.23.3-beta.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@eslint/eslintrc": "^3.1.0",
|
|
37
|
+
"@eslint/js": "^9.12.0",
|
|
38
|
+
"@types/diff": "^5.2.3",
|
|
39
|
+
"@types/har-format": "^1.2.16",
|
|
40
|
+
"@types/jsesc": "^3.0.3",
|
|
41
|
+
"@types/nunjucks": "^3.2.6",
|
|
42
|
+
"@types/tape": "^5.6.4",
|
|
43
|
+
"@types/yamljs": "^0.2.34",
|
|
44
|
+
"@types/yargs": "^17.0.33",
|
|
45
|
+
"@typescript-eslint/eslint-plugin": "^8.9.0",
|
|
46
|
+
"@typescript-eslint/parser": "^8.9.0",
|
|
47
|
+
"c8": "^10.1.2",
|
|
48
|
+
"colors": "^1.4.0",
|
|
49
|
+
"cross-env": "^7.0.3",
|
|
50
|
+
"diff": "^7.0.0",
|
|
51
|
+
"eslint": "^9.12.0",
|
|
52
|
+
"eslint-config-prettier": "^9.1.0",
|
|
53
|
+
"glob": "^11.0.0",
|
|
54
|
+
"husky": "^9.1.6",
|
|
55
|
+
"lint-staged": "^15.2.10",
|
|
56
|
+
"prettier": "^3.3.3",
|
|
57
|
+
"tape": "^5.9.0",
|
|
58
|
+
"@ohos-ports/tree-sitter-cli": "^0.27.0-beta.0",
|
|
59
|
+
"typescript": "^5.6.3",
|
|
60
|
+
"yargs": "^17.7.2"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"compile": "tsc",
|
|
64
|
+
"start": "tsc --watch --preserveWatchOutput -p ./",
|
|
65
|
+
"pretest": "cross-env TSC_COMPILE_ON_ERROR=true npm run compile || exit 0",
|
|
66
|
+
"test": "cross-env NODE_OPTIONS=\"--enable-source-maps --unhandled-rejections=strict\" npx tape dist/test/test.js",
|
|
67
|
+
"coverage": "c8 --check-coverage npm run test",
|
|
68
|
+
"prelint": "npm run compile",
|
|
69
|
+
"lint": "eslint src",
|
|
70
|
+
"format": "prettier --write .",
|
|
71
|
+
"prefix": "npm run compile",
|
|
72
|
+
"fix": "eslint src --fix",
|
|
73
|
+
"prepare": "npx husky && npm run compile && npx tree-sitter build --wasm node_modules/tree-sitter-bash --output dist/tree-sitter-bash.wasm",
|
|
74
|
+
"pregen-test": "npm run compile",
|
|
75
|
+
"gen-test": "node --enable-source-maps ./dist/tools/gen-test.js",
|
|
76
|
+
"compare-requests": "npm run compile && node --enable-source-maps ./dist/tools/compare-requests.js",
|
|
77
|
+
"compare-request": "npm run compile && node --enable-source-maps ./dist/tools/compare-requests.js"
|
|
78
|
+
},
|
|
79
|
+
"lint-staged": {
|
|
80
|
+
"**/*": "prettier --write --ignore-unknown"
|
|
81
|
+
},
|
|
82
|
+
"bin": "dist/src/cli.js",
|
|
83
|
+
"browser": {
|
|
84
|
+
"./dist/src/shell/Parser.js": "./dist/src/shell/webParser.js"
|
|
85
|
+
},
|
|
86
|
+
"bugs": {
|
|
87
|
+
"url": "https://github.com/ohos-ports/ohos-ports/issues"
|
|
88
|
+
},
|
|
89
|
+
"files": [
|
|
90
|
+
"dist",
|
|
91
|
+
"src",
|
|
92
|
+
"tools",
|
|
93
|
+
"index.d.ts",
|
|
94
|
+
"README.md",
|
|
95
|
+
"LICENSE"
|
|
96
|
+
]
|
|
97
|
+
}
|
package/src/Headers.ts
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { Word, eq, joinWords } from "./shell/Word.js";
|
|
2
|
+
import type { Warnings } from "./Warnings.js";
|
|
3
|
+
|
|
4
|
+
// https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Standard_request_fields
|
|
5
|
+
// and then searched for "#" in the RFCs that define each header
|
|
6
|
+
const COMMA_SEPARATED = new Set(
|
|
7
|
+
[
|
|
8
|
+
"A-IM",
|
|
9
|
+
"Accept",
|
|
10
|
+
"Accept-Charset",
|
|
11
|
+
// "Accept-Datetime",
|
|
12
|
+
"Accept-Encoding",
|
|
13
|
+
"Accept-Language",
|
|
14
|
+
// "Access-Control-Request-Method",
|
|
15
|
+
"Access-Control-Request-Headers",
|
|
16
|
+
// TODO: auth-scheme [ 1*SP ( token68 / #auth-param ) ]
|
|
17
|
+
// "Authorization",
|
|
18
|
+
"Cache-Control",
|
|
19
|
+
"Connection",
|
|
20
|
+
"Content-Encoding",
|
|
21
|
+
// "Content-Length",
|
|
22
|
+
// "Content-MD5",
|
|
23
|
+
// "Content-Type", // semicolon
|
|
24
|
+
// "Cookie", // semicolon
|
|
25
|
+
// "Date",
|
|
26
|
+
"Expect",
|
|
27
|
+
"Forwarded",
|
|
28
|
+
// "From",
|
|
29
|
+
// "Host",
|
|
30
|
+
// "HTTP2-Settings",
|
|
31
|
+
"If-Match",
|
|
32
|
+
// "If-Modified-Since",
|
|
33
|
+
"If-None-Match",
|
|
34
|
+
// "If-Range",
|
|
35
|
+
// "If-Unmodified-Since",
|
|
36
|
+
// "Max-Forwards",
|
|
37
|
+
// "Origin",
|
|
38
|
+
// "Pragma",
|
|
39
|
+
// "Prefer", // semicolon
|
|
40
|
+
// "Proxy-Authorization",
|
|
41
|
+
"Range",
|
|
42
|
+
// "Referer",
|
|
43
|
+
"TE",
|
|
44
|
+
"Trailer",
|
|
45
|
+
"Transfer-Encoding",
|
|
46
|
+
// "User-Agent",
|
|
47
|
+
"Upgrade",
|
|
48
|
+
"Via",
|
|
49
|
+
"Warning",
|
|
50
|
+
].map((h) => h.toLowerCase()),
|
|
51
|
+
);
|
|
52
|
+
const SEMICOLON_SEPARATED = new Set(
|
|
53
|
+
["Content-Type", "Cookie", "Prefer"].map((h) => h.toLowerCase()),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
export class Headers implements Iterable<[Word, Word | null]> {
|
|
57
|
+
// null means the header must not be sent, for example
|
|
58
|
+
// curl -H 'Host:' example.com
|
|
59
|
+
readonly headers: [Word, Word | null][];
|
|
60
|
+
readonly lowercase: boolean;
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
headerArgs?: Word[],
|
|
64
|
+
warnings: Warnings = [],
|
|
65
|
+
// Used for warnings
|
|
66
|
+
argName = "--header/H",
|
|
67
|
+
) {
|
|
68
|
+
let headers: [Word, Word | null][] = [];
|
|
69
|
+
|
|
70
|
+
if (headerArgs) {
|
|
71
|
+
for (const header of headerArgs) {
|
|
72
|
+
if (header.startsWith("@")) {
|
|
73
|
+
warnings.push([
|
|
74
|
+
"header-file",
|
|
75
|
+
"passing a file for " +
|
|
76
|
+
argName +
|
|
77
|
+
" is not supported: " +
|
|
78
|
+
JSON.stringify(header.toString()),
|
|
79
|
+
]);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (header.includes(":")) {
|
|
84
|
+
const [name, value] = header.split(":", 2);
|
|
85
|
+
// Passing -H 'Header-Name:' disables sending of that header.
|
|
86
|
+
// If the colon is followed by just spaces they're ignored
|
|
87
|
+
// unless it's -H 'Host: '
|
|
88
|
+
// https://github.com/curl/curl/issues/12782
|
|
89
|
+
const hasValue =
|
|
90
|
+
value && (eq(name, "Host") ? value : value.trim()).toBool();
|
|
91
|
+
const headerValue = hasValue ? value.removeFirstChar(" ") : null;
|
|
92
|
+
headers.push([name, headerValue]);
|
|
93
|
+
} else if (header.includes(";")) {
|
|
94
|
+
const [name] = header.split(";", 2);
|
|
95
|
+
headers.push([name, new Word()]);
|
|
96
|
+
} else {
|
|
97
|
+
// TODO: warn that this header arg is ignored?
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
this.lowercase =
|
|
102
|
+
headers.length > 0 && headers.every((h) => eq(h[0], h[0].toLowerCase()));
|
|
103
|
+
|
|
104
|
+
// Handle repeated headers
|
|
105
|
+
// For Cookie and Accept, merge the values using ';' and ',' respectively
|
|
106
|
+
// For other headers, warn about the repeated header
|
|
107
|
+
const uniqueHeaders: { [key: string]: [Word, Word | null][] } = {};
|
|
108
|
+
for (const [name, value] of headers) {
|
|
109
|
+
// TODO: something better, at least warn that variable is ignored
|
|
110
|
+
const lowerName = name.toLowerCase().toString();
|
|
111
|
+
if (!uniqueHeaders[lowerName]) {
|
|
112
|
+
uniqueHeaders[lowerName] = [];
|
|
113
|
+
}
|
|
114
|
+
uniqueHeaders[lowerName].push([name, value]);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
headers = [];
|
|
118
|
+
for (const [lowerName, repeatedHeaders] of Object.entries(uniqueHeaders)) {
|
|
119
|
+
if (repeatedHeaders.length === 1) {
|
|
120
|
+
headers.push(repeatedHeaders[0]);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
// If they're all null, just use the first one
|
|
124
|
+
if (repeatedHeaders.every((h) => h[1] === null)) {
|
|
125
|
+
const lastRepeat = repeatedHeaders[repeatedHeaders.length - 1];
|
|
126
|
+
// Warn users if some are capitalized differently
|
|
127
|
+
if (new Set(repeatedHeaders.map((h) => h[0])).size > 1) {
|
|
128
|
+
warnings.push([
|
|
129
|
+
"repeated-header",
|
|
130
|
+
`"${lastRepeat[0]}" header unset ${repeatedHeaders.length} times`,
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
headers.push(lastRepeat);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
// Otherwise there's at least one non-null value, so we can ignore the nulls
|
|
137
|
+
// TODO: if the values of the repeated headers are the same, just use the first one
|
|
138
|
+
// 'content-type': 'application/json; application/json',
|
|
139
|
+
// doesn't really make sense
|
|
140
|
+
const nonEmptyHeaders = repeatedHeaders.filter((h) => h[1] !== null);
|
|
141
|
+
if (nonEmptyHeaders.length === 1) {
|
|
142
|
+
headers.push(nonEmptyHeaders[0]);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
let mergeChar = "";
|
|
146
|
+
if (COMMA_SEPARATED.has(lowerName)) {
|
|
147
|
+
mergeChar = ", ";
|
|
148
|
+
} else if (SEMICOLON_SEPARATED.has(lowerName)) {
|
|
149
|
+
mergeChar = "; ";
|
|
150
|
+
}
|
|
151
|
+
if (mergeChar) {
|
|
152
|
+
const merged = joinWords(
|
|
153
|
+
nonEmptyHeaders.map((h) => h[1]) as Word[],
|
|
154
|
+
mergeChar,
|
|
155
|
+
);
|
|
156
|
+
warnings.push([
|
|
157
|
+
"repeated-header",
|
|
158
|
+
`merged ${nonEmptyHeaders.length} "${
|
|
159
|
+
nonEmptyHeaders[nonEmptyHeaders.length - 1][0]
|
|
160
|
+
}" headers together with "${mergeChar.trim()}"`,
|
|
161
|
+
]);
|
|
162
|
+
headers.push([nonEmptyHeaders[0][0], merged]);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
warnings.push([
|
|
167
|
+
"repeated-header",
|
|
168
|
+
`found ${nonEmptyHeaders.length} "${
|
|
169
|
+
nonEmptyHeaders[nonEmptyHeaders.length - 1][0]
|
|
170
|
+
}" headers, only the last one will be sent`,
|
|
171
|
+
]);
|
|
172
|
+
headers = headers.concat(nonEmptyHeaders);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.headers = headers;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
get length(): number {
|
|
179
|
+
return this.headers.length;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
*[Symbol.iterator](): Iterator<[Word, Word | null]> {
|
|
183
|
+
for (const h of this.headers) {
|
|
184
|
+
yield h;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Gets the first header, matching case-insensitively
|
|
189
|
+
get(header: string): Word | null | undefined {
|
|
190
|
+
const lookup = header.toLowerCase();
|
|
191
|
+
for (const [h, v] of this.headers) {
|
|
192
|
+
if (h.toLowerCase().toString() === lookup) {
|
|
193
|
+
return v;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
getContentType(): string | null | undefined {
|
|
200
|
+
const contentTypeHeader = this.get("content-type");
|
|
201
|
+
if (!contentTypeHeader) {
|
|
202
|
+
return contentTypeHeader;
|
|
203
|
+
}
|
|
204
|
+
return contentTypeHeader.split(";")[0].trim().toString();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
has(header: Word | string): boolean {
|
|
208
|
+
const lookup = header.toLowerCase();
|
|
209
|
+
for (const h of this.headers) {
|
|
210
|
+
if (eq(h[0].toLowerCase(), lookup)) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Doesn't overwrite existing headers
|
|
218
|
+
setIfMissing(header: string, value: Word | string): boolean {
|
|
219
|
+
if (this.has(header)) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (this.lowercase) {
|
|
224
|
+
header = header.toLowerCase();
|
|
225
|
+
}
|
|
226
|
+
const k = typeof header === "string" ? new Word(header) : header;
|
|
227
|
+
const v = typeof value === "string" ? new Word(value) : value;
|
|
228
|
+
this.headers.push([k, v]);
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
prependIfMissing(header: string, value: Word | string) {
|
|
233
|
+
if (this.has(header)) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (this.lowercase) {
|
|
238
|
+
header = header.toLowerCase();
|
|
239
|
+
}
|
|
240
|
+
const k = typeof header === "string" ? new Word(header) : header;
|
|
241
|
+
const v = typeof value === "string" ? new Word(value) : value;
|
|
242
|
+
this.headers.unshift([k, v]);
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
set(header: string, value: Word | string) {
|
|
247
|
+
if (this.lowercase) {
|
|
248
|
+
header = header.toLowerCase();
|
|
249
|
+
}
|
|
250
|
+
const k = typeof header === "string" ? new Word(header) : header;
|
|
251
|
+
const v = typeof value === "string" ? new Word(value) : value;
|
|
252
|
+
|
|
253
|
+
// keep it in the same place if we overwrite
|
|
254
|
+
const searchHeader = k.toLowerCase().toString();
|
|
255
|
+
for (let i = 0; i < this.headers.length; i++) {
|
|
256
|
+
if (eq(this.headers[i][0].toLowerCase(), searchHeader)) {
|
|
257
|
+
this.headers[i][1] = v;
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this.headers.push([k, v]);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
delete(header: string) {
|
|
266
|
+
const lookup = header.toLowerCase();
|
|
267
|
+
for (let i = this.headers.length - 1; i >= 0; i--) {
|
|
268
|
+
if (this.headers[i][0].toLowerCase().toString() === lookup) {
|
|
269
|
+
this.headers.splice(i, 1);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// TODO: doesn't this skip the next element after deleting?
|
|
275
|
+
clearNulls() {
|
|
276
|
+
for (let i = this.headers.length - 1; i >= 0; i--) {
|
|
277
|
+
if (this.headers[i][1] === null) {
|
|
278
|
+
this.headers.splice(i, 1);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// TODO: shouldn't be used
|
|
284
|
+
count(header: string) {
|
|
285
|
+
let count = 0;
|
|
286
|
+
const lookup = header.toLowerCase();
|
|
287
|
+
for (const h of this.headers || []) {
|
|
288
|
+
if (h[0].toLowerCase().toString() === lookup) {
|
|
289
|
+
count += 1;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return count;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
toBool(): boolean {
|
|
296
|
+
return this.headers.length > 0 && this.headers.some((h) => h[1] !== null);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Cookie is a type of header.
|
|
301
|
+
export type Cookie = [Word, Word];
|
|
302
|
+
export type Cookies = Array<Cookie>;
|
|
303
|
+
|
|
304
|
+
export function parseCookiesStrict(cookieString: Word): Cookies | null {
|
|
305
|
+
const cookies: Cookies = [];
|
|
306
|
+
for (let cookie of cookieString.split(";")) {
|
|
307
|
+
cookie = cookie.replace(/^ /, "");
|
|
308
|
+
const [name, value] = cookie.split("=", 2);
|
|
309
|
+
if (value === undefined) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
cookies.push([name, value]);
|
|
313
|
+
}
|
|
314
|
+
if (new Set(cookies.map((c) => c[0])).size !== cookies.length) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
return cookies;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function parseCookies(cookieString: Word): Cookies | null {
|
|
321
|
+
const cookies: Cookies = [];
|
|
322
|
+
for (let cookie of cookieString.split(";")) {
|
|
323
|
+
cookie = cookie.trim();
|
|
324
|
+
if (!cookie) {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const [name, value] = cookie.split("=", 2);
|
|
328
|
+
cookies.push([name.trim(), (value || "").trim()]);
|
|
329
|
+
}
|
|
330
|
+
if (new Set(cookies.map((c) => c[0])).size !== cookies.length) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
return cookies;
|
|
334
|
+
}
|
package/src/Query.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Word, eq } from "./shell/Word.js";
|
|
2
|
+
import { UTF8encoder } from "./utils.js";
|
|
3
|
+
|
|
4
|
+
export type QueryList = Array<[Word, Word]>;
|
|
5
|
+
export type QueryDict = Array<[Word, Word | Array<Word>]>;
|
|
6
|
+
|
|
7
|
+
// Can't have a QueryDict without a QueryList
|
|
8
|
+
export type Query = [QueryList, QueryDict | null] | [null, null];
|
|
9
|
+
|
|
10
|
+
// Match Python's urllib.parse.quote() behavior
|
|
11
|
+
// https://github.com/python/cpython/blob/3.11/Lib/urllib/parse.py#L826
|
|
12
|
+
// curl and Python let you send non-ASCII characters by encoding each UTF-8 byte.
|
|
13
|
+
// TODO: ignore hex case?
|
|
14
|
+
function _percentEncode(s: string): string {
|
|
15
|
+
return [...UTF8encoder.encode(s)]
|
|
16
|
+
.map((b) => {
|
|
17
|
+
if (
|
|
18
|
+
// A-Z
|
|
19
|
+
(b >= 0x41 && b <= 0x5a) ||
|
|
20
|
+
// a-z
|
|
21
|
+
(b >= 0x61 && b <= 0x7a) ||
|
|
22
|
+
// 0-9
|
|
23
|
+
(b >= 0x30 && b <= 0x39) ||
|
|
24
|
+
// -._~
|
|
25
|
+
b === 0x2d ||
|
|
26
|
+
b === 0x2e ||
|
|
27
|
+
b === 0x5f ||
|
|
28
|
+
b === 0x7e
|
|
29
|
+
) {
|
|
30
|
+
return String.fromCharCode(b);
|
|
31
|
+
}
|
|
32
|
+
return "%" + b.toString(16).toUpperCase().padStart(2, "0");
|
|
33
|
+
})
|
|
34
|
+
.join("");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function percentEncode(s: Word): Word {
|
|
38
|
+
const newTokens = [];
|
|
39
|
+
for (const token of s.tokens) {
|
|
40
|
+
if (typeof token === "string") {
|
|
41
|
+
newTokens.push(_percentEncode(token));
|
|
42
|
+
} else {
|
|
43
|
+
newTokens.push(token);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return new Word(newTokens);
|
|
47
|
+
}
|
|
48
|
+
export function percentEncodePlus(s: Word): Word {
|
|
49
|
+
const newTokens = [];
|
|
50
|
+
for (const token of s.tokens) {
|
|
51
|
+
if (typeof token === "string") {
|
|
52
|
+
newTokens.push(_percentEncode(token).replace(/%20/g, "+"));
|
|
53
|
+
} else {
|
|
54
|
+
newTokens.push(token);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return new Word(newTokens);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Reimplements decodeURIComponent but ignores variables/commands
|
|
61
|
+
export function wordDecodeURIComponent(s: Word): Word {
|
|
62
|
+
const newTokens = [];
|
|
63
|
+
for (const token of s.tokens) {
|
|
64
|
+
if (typeof token === "string") {
|
|
65
|
+
newTokens.push(decodeURIComponent(token));
|
|
66
|
+
} else {
|
|
67
|
+
newTokens.push(token);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return new Word(newTokens);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// if url is 'example.com?' the s is ''
|
|
74
|
+
// if url is 'example.com' the s is null
|
|
75
|
+
export function parseQueryString(s: Word | null): Query {
|
|
76
|
+
if (!s || s.isEmpty()) {
|
|
77
|
+
return [null, null];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const asList: QueryList = [];
|
|
81
|
+
for (const param of s.split("&")) {
|
|
82
|
+
// Most software libraries don't let you distinguish between a=&b= and a&b,
|
|
83
|
+
// so if we get an `a&b`-type query string, don't bother.
|
|
84
|
+
if (!param.includes("=")) {
|
|
85
|
+
return [null, null];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const [key, val] = param.split("=", 2);
|
|
89
|
+
let decodedKey;
|
|
90
|
+
let decodedVal;
|
|
91
|
+
try {
|
|
92
|
+
// https://url.spec.whatwg.org/#urlencoded-parsing
|
|
93
|
+
// recommends replacing + with space before decoding.
|
|
94
|
+
decodedKey = wordDecodeURIComponent(key.replace(/\+/g, " "));
|
|
95
|
+
decodedVal = wordDecodeURIComponent(val.replace(/\+/g, " "));
|
|
96
|
+
} catch (e) {
|
|
97
|
+
if (e instanceof URIError) {
|
|
98
|
+
// Query string contains invalid percent encoded characters,
|
|
99
|
+
// we cannot properly convert it.
|
|
100
|
+
return [null, null];
|
|
101
|
+
}
|
|
102
|
+
throw e;
|
|
103
|
+
}
|
|
104
|
+
// If the query string doesn't round-trip, we cannot properly convert it.
|
|
105
|
+
// TODO: this is a bit Python-specific, ideally we would check how each runtime/library
|
|
106
|
+
// percent-encodes query strings. For example, a %27 character in the input query
|
|
107
|
+
// string will be decoded to a ' but won't be re-encoded into a %27 by encodeURIComponent
|
|
108
|
+
const roundTripKey = percentEncode(decodedKey);
|
|
109
|
+
const roundTripVal = percentEncode(decodedVal);
|
|
110
|
+
// If the original data used %20 instead of + (what requests will send), that's close enough
|
|
111
|
+
if (
|
|
112
|
+
(!eq(roundTripKey, key) && !eq(roundTripKey.replace(/%20/g, "+"), key)) ||
|
|
113
|
+
(!eq(roundTripVal, val) && !eq(roundTripVal.replace(/%20/g, "+"), val))
|
|
114
|
+
) {
|
|
115
|
+
return [null, null];
|
|
116
|
+
}
|
|
117
|
+
asList.push([decodedKey, decodedVal]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Group keys
|
|
121
|
+
const keyWords: { [key: string]: Word } = {};
|
|
122
|
+
const uniqueKeys: { [key: string]: Array<Word> } = {};
|
|
123
|
+
let prevKey = null;
|
|
124
|
+
for (const [key, val] of asList) {
|
|
125
|
+
const keyStr = key.toString(); // TODO: do this better
|
|
126
|
+
if (prevKey === keyStr) {
|
|
127
|
+
uniqueKeys[keyStr].push(val);
|
|
128
|
+
} else if (!Object.prototype.hasOwnProperty.call(uniqueKeys, keyStr)) {
|
|
129
|
+
uniqueKeys[keyStr] = [val];
|
|
130
|
+
keyWords[keyStr] = key;
|
|
131
|
+
} else {
|
|
132
|
+
// If there's a repeated key with a different key between
|
|
133
|
+
// one of its repetitions, there is no way to represent
|
|
134
|
+
// this query string as a dictionary.
|
|
135
|
+
return [asList, null];
|
|
136
|
+
}
|
|
137
|
+
prevKey = keyStr;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Convert lists with 1 element to the element
|
|
141
|
+
const asDict: QueryDict = [];
|
|
142
|
+
for (const [keyStr, val] of Object.entries(uniqueKeys)) {
|
|
143
|
+
asDict.push([keyWords[keyStr], val.length === 1 ? val[0] : val]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return [asList, asDict];
|
|
147
|
+
}
|