mirror of
https://github.com/denoland/deno.git
synced 2025-01-12 00:54:02 -05:00
feat(ext/web): implement static Response.json
(#14566)
This commit adds support for the static `Response.json` method.
This commit is contained in:
parent
42fec5150e
commit
a5b50d0915
5 changed files with 118 additions and 46 deletions
|
@ -15,7 +15,8 @@
|
|||
const { isProxy } = Deno.core;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const consoleInternal = window.__bootstrap.console;
|
||||
const { HTTP_TAB_OR_SPACE, regexMatcher } = window.__bootstrap.infra;
|
||||
const { HTTP_TAB_OR_SPACE, regexMatcher, serializeJSValueToJSONString } =
|
||||
window.__bootstrap.infra;
|
||||
const { extractBody, mixinBody } = window.__bootstrap.fetchBody;
|
||||
const { getLocationHref } = window.__bootstrap.location;
|
||||
const { extractMimeType } = window.__bootstrap.mimesniff;
|
||||
|
@ -157,6 +158,56 @@
|
|||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://fetch.spec.whatwg.org#initialize-a-response
|
||||
* @param {Response} response
|
||||
* @param {ResponseInit} init
|
||||
* @param {{ body: __bootstrap.fetchBody.InnerBody, contentType: string | null } | null} bodyWithType
|
||||
*/
|
||||
function initializeAResponse(response, init, bodyWithType) {
|
||||
// 1.
|
||||
if ((init.status < 200 || init.status > 599) && init.status != 101) {
|
||||
throw new RangeError(
|
||||
`The status provided (${init.status}) is not equal to 101 and outside the range [200, 599].`,
|
||||
);
|
||||
}
|
||||
|
||||
// 2.
|
||||
if (
|
||||
init.statusText &&
|
||||
!RegExpPrototypeTest(REASON_PHRASE_RE, init.statusText)
|
||||
) {
|
||||
throw new TypeError("Status text is not valid.");
|
||||
}
|
||||
|
||||
// 3.
|
||||
response[_response].status = init.status;
|
||||
|
||||
// 4.
|
||||
response[_response].statusMessage = init.statusText;
|
||||
|
||||
// 5.
|
||||
/** @type {__bootstrap.headers.Headers} */
|
||||
const headers = response[_headers];
|
||||
if (init.headers) {
|
||||
fillHeaders(headers, init.headers);
|
||||
}
|
||||
|
||||
// 6.
|
||||
if (bodyWithType !== null) {
|
||||
if (nullBodyStatus(response[_response].status)) {
|
||||
throw new TypeError(
|
||||
"Response with null body status cannot have body",
|
||||
);
|
||||
}
|
||||
const { body, contentType } = bodyWithType;
|
||||
response[_response].body = body;
|
||||
if (contentType !== null && !headers.has("content-type")) {
|
||||
headers.append("Content-Type", contentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Response {
|
||||
get [_mimeType]() {
|
||||
const values = getDecodeSplitHeader(
|
||||
|
@ -217,6 +268,32 @@
|
|||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} data
|
||||
* @param {ResponseInit} init
|
||||
* @returns {Response}
|
||||
*/
|
||||
static json(data, init = {}) {
|
||||
const prefix = "Failed to call 'Response.json'";
|
||||
data = webidl.converters.any(data);
|
||||
init = webidl.converters["ResponseInit_fast"](init, {
|
||||
prefix,
|
||||
context: "Argument 2",
|
||||
});
|
||||
|
||||
const str = serializeJSValueToJSONString(data);
|
||||
const res = extractBody(str);
|
||||
res.contentType = "application/json";
|
||||
const response = webidl.createBranded(Response);
|
||||
response[_response] = newInnerResponse();
|
||||
response[_headers] = headersFromHeaderList(
|
||||
response[_response].headerList,
|
||||
"response",
|
||||
);
|
||||
initializeAResponse(response, init, res);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BodyInit | null} body
|
||||
* @param {ResponseInit} init
|
||||
|
@ -232,40 +309,18 @@
|
|||
context: "Argument 2",
|
||||
});
|
||||
|
||||
if ((init.status < 200 || init.status > 599) && init.status != 101) {
|
||||
throw new RangeError(
|
||||
`The status provided (${init.status}) is not equal to 101 and outside the range [200, 599].`,
|
||||
);
|
||||
}
|
||||
this[_response] = newInnerResponse();
|
||||
this[_headers] = headersFromHeaderList(
|
||||
this[_response].headerList,
|
||||
"response",
|
||||
);
|
||||
|
||||
if (
|
||||
init.statusText &&
|
||||
!RegExpPrototypeTest(REASON_PHRASE_RE, init.statusText)
|
||||
) {
|
||||
throw new TypeError("Status text is not valid.");
|
||||
}
|
||||
|
||||
this[webidl.brand] = webidl.brand;
|
||||
const response = newInnerResponse(init.status, init.statusText);
|
||||
/** @type {InnerResponse} */
|
||||
this[_response] = response;
|
||||
/** @type {Headers} */
|
||||
this[_headers] = headersFromHeaderList(response.headerList, "response");
|
||||
if (init.headers) {
|
||||
fillHeaders(this[_headers], init.headers);
|
||||
}
|
||||
let bodyWithType = null;
|
||||
if (body !== null) {
|
||||
if (nullBodyStatus(response.status)) {
|
||||
throw new TypeError(
|
||||
"Response with null body status cannot have body",
|
||||
);
|
||||
}
|
||||
const res = extractBody(body);
|
||||
response.body = res.body;
|
||||
if (res.contentType !== null && !this[_headers].has("content-type")) {
|
||||
this[_headers].append("Content-Type", res.contentType);
|
||||
}
|
||||
bodyWithType = extractBody(body);
|
||||
}
|
||||
initializeAResponse(this, init, bodyWithType);
|
||||
this[webidl.brand] = webidl.brand;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,23 +11,24 @@
|
|||
((window) => {
|
||||
const core = Deno.core;
|
||||
const {
|
||||
Error,
|
||||
RegExp,
|
||||
ArrayPrototypeMap,
|
||||
StringPrototypeCharCodeAt,
|
||||
NumberPrototypeToString,
|
||||
StringPrototypePadStart,
|
||||
TypeError,
|
||||
ArrayPrototypeJoin,
|
||||
ArrayPrototypeMap,
|
||||
Error,
|
||||
JSONStringify,
|
||||
NumberPrototypeToString,
|
||||
RegExp,
|
||||
SafeArrayIterator,
|
||||
StringPrototypeCharAt,
|
||||
StringPrototypeMatch,
|
||||
StringPrototypeSlice,
|
||||
String,
|
||||
StringPrototypeCharAt,
|
||||
StringPrototypeCharCodeAt,
|
||||
StringPrototypeMatch,
|
||||
StringPrototypePadStart,
|
||||
StringPrototypeReplace,
|
||||
StringPrototypeToUpperCase,
|
||||
StringPrototypeToLowerCase,
|
||||
StringPrototypeSlice,
|
||||
StringPrototypeSubstring,
|
||||
StringPrototypeToLowerCase,
|
||||
StringPrototypeToUpperCase,
|
||||
TypeError,
|
||||
} = window.__bootstrap.primordials;
|
||||
|
||||
const ASCII_DIGIT = ["\u0030-\u0039"];
|
||||
|
@ -294,6 +295,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} value
|
||||
* @returns {string}
|
||||
*/
|
||||
function serializeJSValueToJSONString(value) {
|
||||
const result = JSONStringify(value);
|
||||
if (result === undefined) {
|
||||
throw new TypeError("Value is not JSON serializable.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
window.__bootstrap.infra = {
|
||||
collectSequenceOfCodepoints,
|
||||
ASCII_DIGIT,
|
||||
|
@ -320,5 +333,6 @@
|
|||
forgivingBase64Decode,
|
||||
AssertionError,
|
||||
assert,
|
||||
serializeJSValueToJSONString,
|
||||
};
|
||||
})(globalThis);
|
||||
|
|
1
ext/web/internal.d.ts
vendored
1
ext/web/internal.d.ts
vendored
|
@ -45,6 +45,7 @@ declare namespace globalThis {
|
|||
};
|
||||
forgivingBase64Encode(data: Uint8Array): string;
|
||||
forgivingBase64Decode(data: string): Uint8Array;
|
||||
serializeJSValueToJSONString(value: unknown): string;
|
||||
};
|
||||
|
||||
declare var domException: {
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 114d0fd7e438e64905a5673550385dd690ccb763
|
||||
Subproject commit 1a8281d7aa0eed050c6d8c151a602ce43dd55406
|
|
@ -3215,7 +3215,9 @@
|
|||
"response-consume-stream.any.html": true,
|
||||
"response-consume-stream.any.worker.html": true,
|
||||
"response-init-contenttype.any.html": true,
|
||||
"response-init-contenttype.any.worker.html": true
|
||||
"response-init-contenttype.any.worker.html": true,
|
||||
"response-static-json.any.html": true,
|
||||
"response-static-json.any.worker.html": true
|
||||
},
|
||||
"body": {
|
||||
"formdata.any.html": [
|
||||
|
|
Loading…
Reference in a new issue