1
0
Fork 0
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:
Luca Casonato 2022-05-13 14:28:05 +02:00 committed by GitHub
parent 42fec5150e
commit a5b50d0915
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 118 additions and 46 deletions

View file

@ -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;
}
/**

View file

@ -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);

View file

@ -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

View file

@ -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": [