@scalar/snippetz 0.9.32 → 0.10.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/README.md +12 -0
- package/dist/libs/browser-request.d.ts.map +1 -1
- package/dist/libs/browser-request.js +17 -6
- package/dist/plugins/csharp/restsharp/restsharp.d.ts.map +1 -1
- package/dist/plugins/csharp/restsharp/restsharp.js +2 -13
- package/dist/plugins/shell/curl/curl.d.ts.map +1 -1
- package/dist/plugins/shell/curl/curl.js +9 -15
- package/dist/plugins/shell/wget/wget.d.ts.map +1 -1
- package/dist/plugins/shell/wget/wget.js +3 -15
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -102,3 +102,15 @@ We are API nerds. You too? Let's chat on Discord: <https://discord.gg/scalar>
|
|
|
102
102
|
## License
|
|
103
103
|
|
|
104
104
|
The source code in this repository is licensed under [MIT](https://github.com/scalar/scalar/blob/main/LICENSE).
|
|
105
|
+
|
|
106
|
+
## Cookies in browser examples
|
|
107
|
+
|
|
108
|
+
XHR and jQuery examples use `document.cookie` and enable `withCredentials` whenever the HAR contains cookie values, including an explicit `Cookie` header without OpenAPI cookie-style parameters. This intentionally changes those legacy examples: browsers forbid setting the `Cookie` request header directly. Requests without cookies remain uncredentialed.
|
|
109
|
+
|
|
110
|
+
The setup writes cookies for the page's domain and cannot set cookies for an unrelated API domain or create HttpOnly cookies. For a cross-origin API, obtain cookies through that API's authentication flow instead. Cookie domain, path, Secure, SameSite, and browser third-party-cookie policies still apply.
|
|
111
|
+
|
|
112
|
+
[Credentialed cross-origin responses](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS#requests_with_credentials) must permit credentials and the specific calling origin through CORS (`Access-Control-Allow-Credentials: true` and a non-wildcard `Access-Control-Allow-Origin`). Enabling credentials does not itself require preflight; if the request otherwise requires preflight, that check must also succeed.
|
|
113
|
+
|
|
114
|
+
Cookie-style values remain serialized as authored, while legacy structured HAR cookies are percent-encoded. Do not decode and re-encode a combined Cookie header uniformly. Other generators retain their existing environment-specific header behavior.
|
|
115
|
+
|
|
116
|
+
For OpenAPI 3.2 `in: cookie, style: cookie` parameters, omit `explode` or set it to `true`. `explode: false` is invalid for cookies because comma-separated values violate cookie syntax. Scalar tolerates that invalid combination by expanding arrays and objects into separate cookie entries; it emits a developer-console warning once per parameter name. Values are passed through unchanged, so provide any required escaping in the API description.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-request.d.ts","sourceRoot":"","sources":["../../src/libs/browser-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAK7E,wFAAwF;AACxF,eAAO,MAAM,qBAAqB,GAChC,UAAS,OAAO,CAAC,UAAU,CAAM,EACjC,gBAAgB,mBAAmB,KAClC;IACD,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC1C,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"browser-request.d.ts","sourceRoot":"","sources":["../../src/libs/browser-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAK7E,wFAAwF;AACxF,eAAO,MAAM,qBAAqB,GAChC,UAAS,OAAO,CAAC,UAAU,CAAM,EACjC,gBAAgB,mBAAmB,KAClC;IACD,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC1C,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe,EAAE,OAAO,CAAA;CAuEzB,CAAA"}
|
|
@@ -2,7 +2,16 @@ import { Base64 } from 'js-base64';
|
|
|
2
2
|
import { joinUrlAndQuery, normalizeMethod } from './http.js';
|
|
3
3
|
/** Builds browser request values without collapsing repeated headers or form fields. */
|
|
4
4
|
export const prepareBrowserRequest = (request = {}, configuration) => {
|
|
5
|
-
|
|
5
|
+
// Browsers forbid setting Cookie directly. Header values are already serialized,
|
|
6
|
+
// so preserve their escaping when transferring them to the browser cookie store.
|
|
7
|
+
const cookieValues = [
|
|
8
|
+
...(request.cookies ?? []).map(({ name, value }) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`),
|
|
9
|
+
...(request.headers ?? [])
|
|
10
|
+
.filter(({ name }) => name.toLowerCase() === 'cookie')
|
|
11
|
+
.flatMap(({ value }) => (value ?? '').split(';').map((cookie) => cookie.trim()))
|
|
12
|
+
.filter((cookie) => cookie.includes('=')),
|
|
13
|
+
];
|
|
14
|
+
const headers = (request.headers ?? []).filter(({ name }) => name.toLowerCase() !== 'cookie');
|
|
6
15
|
if (configuration?.auth?.username && configuration.auth.password) {
|
|
7
16
|
headers.push({
|
|
8
17
|
name: 'Authorization',
|
|
@@ -10,10 +19,10 @@ export const prepareBrowserRequest = (request = {}, configuration) => {
|
|
|
10
19
|
});
|
|
11
20
|
}
|
|
12
21
|
const setup = [];
|
|
13
|
-
if (
|
|
14
|
-
setup.push(
|
|
15
|
-
for (const
|
|
16
|
-
setup.push(`document.cookie = ${JSON.stringify(`${
|
|
22
|
+
if (cookieValues.length) {
|
|
23
|
+
setup.push("// Run on the request origin; document.cookie writes cookies for the current page's domain.");
|
|
24
|
+
for (const cookie of cookieValues) {
|
|
25
|
+
setup.push(`document.cookie = ${JSON.stringify(`${cookie}; path=/`)};`);
|
|
17
26
|
}
|
|
18
27
|
}
|
|
19
28
|
const postData = request.postData;
|
|
@@ -58,7 +67,9 @@ export const prepareBrowserRequest = (request = {}, configuration) => {
|
|
|
58
67
|
// The browser must supply the boundary matching its FormData serialization.
|
|
59
68
|
headers: multipart ? headers.filter(({ name }) => name.toLowerCase() !== 'content-type') : headers,
|
|
60
69
|
setup,
|
|
61
|
-
|
|
70
|
+
// This intentionally includes explicit Cookie headers, even without cookie-style parameters.
|
|
71
|
+
// Cross-origin use additionally requires credentialed CORS and eligible stored cookies.
|
|
72
|
+
withCredentials: Boolean(cookieValues.length),
|
|
62
73
|
body: multipart ? 'body' : formBody,
|
|
63
74
|
};
|
|
64
75
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restsharp.d.ts","sourceRoot":"","sources":["../../../../src/plugins/csharp/restsharp/restsharp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"restsharp.d.ts","sourceRoot":"","sources":["../../../../src/plugins/csharp/restsharp/restsharp.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAsCpD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,MAkH7B,CAAA"}
|
|
@@ -1,18 +1,7 @@
|
|
|
1
|
+
import { isJsonMediaType } from '@scalar/helpers/http/is-json-media-type';
|
|
1
2
|
import { parseMimeType } from '@scalar/helpers/http/mime-type';
|
|
2
3
|
import { encode } from 'js-base64';
|
|
3
4
|
import { joinUrlAndQuery } from '../../../libs/http.js';
|
|
4
|
-
/**
|
|
5
|
-
* True for `application/json`, any RFC 6839 `+json` structured-syntax suffix
|
|
6
|
-
* (e.g. `application/vnd.api+json`), and parameterized variants
|
|
7
|
-
* (e.g. `application/json;charset=utf-8`). Case-insensitive.
|
|
8
|
-
*/
|
|
9
|
-
const isJsonContentType = (value) => {
|
|
10
|
-
if (!value) {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
const { subtype } = parseMimeType(value);
|
|
14
|
-
return subtype === 'json' || subtype.endsWith('+json');
|
|
15
|
-
};
|
|
16
5
|
/**
|
|
17
6
|
* Maps an HTTP method to a RestSharp `Method` enum member. The enum uses
|
|
18
7
|
* PascalCase members (`Method.Get`, `Method.Post`, ...), so we title-case the
|
|
@@ -97,7 +86,7 @@ export const csharpRestsharp = {
|
|
|
97
86
|
// Compare against the essence so parameterized values (e.g. a `boundary` or
|
|
98
87
|
// `charset`) still match the form, multipart, and octet-stream branches.
|
|
99
88
|
const essence = mimeType ? parseMimeType(mimeType).essence : undefined;
|
|
100
|
-
if (
|
|
89
|
+
if (isJsonMediaType(mimeType)) {
|
|
101
90
|
if (text) {
|
|
102
91
|
let body = text;
|
|
103
92
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"curl.d.ts","sourceRoot":"","sources":["../../../../src/plugins/shell/curl/curl.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"curl.d.ts","sourceRoot":"","sources":["../../../../src/plugins/shell/curl/curl.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAIpD;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,MAiKvB,CAAA"}
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isJsonMediaType } from '@scalar/helpers/http/is-json-media-type';
|
|
2
|
+
import { isStreamingContentType } from '@scalar/helpers/http/is-streaming-content-type';
|
|
2
3
|
import { escapeSingleQuotes } from '../../../libs/shell.js';
|
|
3
|
-
/**
|
|
4
|
-
* True for `application/json`, any RFC 6839 `+json` structured-syntax suffix
|
|
5
|
-
* (e.g. `application/vnd.api+json`), and parameterized variants
|
|
6
|
-
* (e.g. `application/json;charset=utf-8`). Case-insensitive.
|
|
7
|
-
*/
|
|
8
|
-
const isJsonContentType = (value) => {
|
|
9
|
-
if (!value) {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
const { subtype } = parseMimeType(value);
|
|
13
|
-
return subtype === 'json' || subtype.endsWith('+json');
|
|
14
|
-
};
|
|
15
4
|
/**
|
|
16
5
|
* shell/curl
|
|
17
6
|
*/
|
|
@@ -93,7 +82,7 @@ export const shellCurl = {
|
|
|
93
82
|
}
|
|
94
83
|
// Body
|
|
95
84
|
if (normalizedRequest.postData) {
|
|
96
|
-
if (
|
|
85
|
+
if (isJsonMediaType(normalizedRequest.postData.mimeType)) {
|
|
97
86
|
// Pretty print JSON data
|
|
98
87
|
if (normalizedRequest.postData.text) {
|
|
99
88
|
try {
|
|
@@ -109,6 +98,11 @@ export const shellCurl = {
|
|
|
109
98
|
}
|
|
110
99
|
}
|
|
111
100
|
}
|
|
101
|
+
else if (isStreamingContentType(normalizedRequest.postData.mimeType ?? '')) {
|
|
102
|
+
// Use the explicit binary mode consistently for framed streaming media types.
|
|
103
|
+
const escapedText = escapeSingleQuotes(normalizedRequest.postData.text ?? '');
|
|
104
|
+
parts.push(`--data-binary '${escapedText}'`);
|
|
105
|
+
}
|
|
112
106
|
else if (normalizedRequest.postData.mimeType === 'application/octet-stream') {
|
|
113
107
|
const escapedText = escapeSingleQuotes(normalizedRequest.postData.text ?? '');
|
|
114
108
|
parts.push(`--data-binary '${escapedText}'`);
|
|
@@ -136,7 +130,7 @@ export const shellCurl = {
|
|
|
136
130
|
const rawValue = param.value ?? '';
|
|
137
131
|
// Pretty-print parts whose contentType is JSON so the snippet stays readable,
|
|
138
132
|
// mirroring what we already do for `--data` JSON bodies above.
|
|
139
|
-
const isJsonPart =
|
|
133
|
+
const isJsonPart = isJsonMediaType(param.contentType);
|
|
140
134
|
let displayValue = rawValue;
|
|
141
135
|
if (isJsonPart && rawValue) {
|
|
142
136
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wget.d.ts","sourceRoot":"","sources":["../../../../src/plugins/shell/wget/wget.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"wget.d.ts","sourceRoot":"","sources":["../../../../src/plugins/shell/wget/wget.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAgBpD;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,MAsFvB,CAAA"}
|
|
@@ -1,17 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isJsonMediaType } from '@scalar/helpers/http/is-json-media-type';
|
|
2
2
|
import { escapeSingleQuotes } from '../../../libs/shell.js';
|
|
3
|
-
/**
|
|
4
|
-
* True for `application/json`, any RFC 6839 `+json` structured-syntax suffix
|
|
5
|
-
* (e.g. `application/vnd.api+json`), and parameterized variants
|
|
6
|
-
* (e.g. `application/json;charset=utf-8`). Case-insensitive.
|
|
7
|
-
*/
|
|
8
|
-
const isJsonContentType = (value) => {
|
|
9
|
-
if (!value) {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
const { subtype } = parseMimeType(value);
|
|
13
|
-
return subtype === 'json' || subtype.endsWith('+json');
|
|
14
|
-
};
|
|
15
3
|
/**
|
|
16
4
|
* Pretty-prints a JSON string and falls back to the original value when it
|
|
17
5
|
* cannot be parsed. Keeps the generated snippet readable, mirroring curl.
|
|
@@ -72,7 +60,7 @@ export const shellWget = {
|
|
|
72
60
|
// Body
|
|
73
61
|
if (normalizedRequest.postData) {
|
|
74
62
|
const { mimeType, text, params } = normalizedRequest.postData;
|
|
75
|
-
if (
|
|
63
|
+
if (isJsonMediaType(mimeType)) {
|
|
76
64
|
if (text) {
|
|
77
65
|
parts.push(`--body-data '${escapeSingleQuotes(prettyPrintJson(text))}'`);
|
|
78
66
|
}
|
|
@@ -96,7 +84,7 @@ export const shellWget = {
|
|
|
96
84
|
}
|
|
97
85
|
else {
|
|
98
86
|
const rawValue = param.value ?? '';
|
|
99
|
-
const displayValue =
|
|
87
|
+
const displayValue = isJsonMediaType(param.contentType) && rawValue ? prettyPrintJson(rawValue) : rawValue;
|
|
100
88
|
parts.push(`--body-data '${escapeSingleQuotes(`${param.name}=${displayValue}`)}'`);
|
|
101
89
|
}
|
|
102
90
|
});
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"url": "git+https://github.com/scalar/scalar.git",
|
|
10
10
|
"directory": "packages/snippetz"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.
|
|
12
|
+
"version": "0.10.0",
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">=22"
|
|
15
15
|
},
|
|
@@ -238,8 +238,8 @@
|
|
|
238
238
|
"CHANGELOG"
|
|
239
239
|
],
|
|
240
240
|
"dependencies": {
|
|
241
|
-
"@scalar/helpers": "0.
|
|
242
|
-
"@scalar/types": "0.
|
|
241
|
+
"@scalar/helpers": "0.13.0",
|
|
242
|
+
"@scalar/types": "0.21.0",
|
|
243
243
|
"js-base64": "^3.9.2"
|
|
244
244
|
},
|
|
245
245
|
"devDependencies": {
|