diff --git a/config/importmap.rb b/config/importmap.rb index 6988153410..3431633f98 100644 --- a/config/importmap.rb +++ b/config/importmap.rb @@ -4,7 +4,7 @@ pin "@hotwired/turbo-rails", to: "turbo.min.js" pin "@hotwired/stimulus", to: "stimulus.min.js" pin "@hotwired/stimulus-loading", to: "stimulus-loading.js" -pin "@rails/request.js", to: "rails-request.js" # @0.0.11 +pin "@rails/request.js", to: "@rails--request.js" # @0.0.13 pin_all_from "app/javascript/controllers", under: "controllers" pin_all_from "app/javascript/helpers", under: "helpers" diff --git a/vendor/javascript/@rails--request.js b/vendor/javascript/@rails--request.js new file mode 100644 index 0000000000..1aa2eaee64 --- /dev/null +++ b/vendor/javascript/@rails--request.js @@ -0,0 +1,4 @@ +// @rails/request.js@0.0.13 downloaded from https://ga.jspm.io/npm:@rails/request.js@0.0.13/src/index.js + +class FetchResponse{constructor(t){this.response=t}get statusCode(){return this.response.status}get redirected(){return this.response.redirected}get ok(){return this.response.ok}get unauthenticated(){return this.statusCode===401}get unprocessableEntity(){return this.statusCode===422}get authenticationURL(){return this.response.headers.get("WWW-Authenticate")}get contentType(){const t=this.response.headers.get("Content-Type")||"";return t.replace(/;.*$/,"")}get headers(){return this.response.headers}get html(){return this.contentType.match(/^(application|text)\/(html|xhtml\+xml)$/)?this.text:Promise.reject(new Error(`Expected an HTML response but got "${this.contentType}" instead`))}get json(){return this.contentType.match(/^application\/.*json$/)?this.responseJson||(this.responseJson=this.response.json()):Promise.reject(new Error(`Expected a JSON response but got "${this.contentType}" instead`))}get text(){return this.responseText||(this.responseText=this.response.text())}get isTurboStream(){return this.contentType.match(/^text\/vnd\.turbo-stream\.html/)}get isScript(){return this.contentType.match(/\b(?:java|ecma)script\b/)}async renderTurboStream(){if(!this.isTurboStream)return Promise.reject(new Error(`Expected a Turbo Stream response but got "${this.contentType}" instead`));window.Turbo?await window.Turbo.renderStreamMessage(await this.text):console.warn("You must set `window.Turbo = Turbo` to automatically process Turbo Stream events with request.js")}async activeScript(){if(!this.isScript)return Promise.reject(new Error(`Expected a Script response but got "${this.contentType}" instead`));{const t=document.createElement("script");const e=document.querySelector("meta[name=csp-nonce]");if(e){const n=e.nonce===""?e.content:e.nonce;n&&t.setAttribute("nonce",n)}t.innerHTML=await this.text;document.body.appendChild(t)}}}class RequestInterceptor{static register(t){this.interceptor=t}static get(){return this.interceptor}static reset(){this.interceptor=void 0}}function t(t){const e=document.cookie?document.cookie.split("; "):[];const n=`${encodeURIComponent(t)}=`;const s=e.find((t=>t.startsWith(n)));if(s){const t=s.split("=").slice(1).join("=");if(t)return decodeURIComponent(t)}}function e(t){const e={};for(const n in t){const s=t[n];s!==void 0&&(e[n]=s)}return e}function n(t){const e=document.head.querySelector(`meta[name="${t}"]`);return e&&e.content}function s(t){return[...t].reduce(((t,[e,n])=>t.concat(typeof n==="string"?[[e,n]]:[])),[])}function r(t,e){for(const[n,s]of e)if(!(s instanceof window.File))if(t.has(n)&&!n.includes("[]")){t.delete(n);t.set(n,s)}else t.append(n,s)}class FetchRequest{constructor(t,e,n={}){this.method=t;this.options=n;this.originalUrl=e.toString()}async perform(){try{const t=RequestInterceptor.get();t&&await t(this)}catch(t){console.error(t)}const t=window.Turbo?window.Turbo.fetch:window.fetch;const e=new FetchResponse(await t(this.url,this.fetchOptions));if(e.unauthenticated&&e.authenticationURL)return Promise.reject(window.location.href=e.authenticationURL);e.isScript&&await e.activeScript();const n=e.ok||e.unprocessableEntity;n&&e.isTurboStream&&await e.renderTurboStream();return e}addHeader(t,e){const n=this.additionalHeaders;n[t]=e;this.options.headers=n}sameHostname(){if(!this.originalUrl.startsWith("http:")&&!this.originalUrl.startsWith("https:"))return true;try{return new URL(this.originalUrl).hostname===window.location.hostname}catch(t){return true}}get fetchOptions(){return{method:this.method.toUpperCase(),headers:this.headers,body:this.formattedBody,signal:this.signal,credentials:this.credentials,redirect:this.redirect,keepalive:this.keepalive}}get headers(){const t={"X-Requested-With":"XMLHttpRequest","Content-Type":this.contentType,Accept:this.accept};this.sameHostname()&&(t["X-CSRF-Token"]=this.csrfToken);return e(Object.assign(t,this.additionalHeaders))}get csrfToken(){return t(n("csrf-param"))||n("csrf-token")}get contentType(){return this.options.contentType?this.options.contentType:this.body==null||this.body instanceof window.FormData?void 0:this.body instanceof window.File?this.body.type:"application/json"}get accept(){switch(this.responseKind){case"html":return"text/html, application/xhtml+xml";case"turbo-stream":return"text/vnd.turbo-stream.html, text/html, application/xhtml+xml";case"json":return"application/json, application/vnd.api+json";case"script":return"text/javascript, application/javascript";default:return"*/*"}}get body(){return this.options.body}get query(){const t=(this.originalUrl.split("?")[1]||"").split("#")[0];const e=new URLSearchParams(t);let n=this.options.query;n=n instanceof window.FormData?s(n):n instanceof window.URLSearchParams?n.entries():Object.entries(n||{});r(e,n);const o=e.toString();return o.length>0?`?${o}`:""}get url(){return this.originalUrl.split("?")[0].split("#")[0]+this.query}get responseKind(){return this.options.responseKind||"html"}get signal(){return this.options.signal}get redirect(){return this.options.redirect||"follow"}get credentials(){return this.options.credentials||"same-origin"}get keepalive(){return this.options.keepalive||false}get additionalHeaders(){return this.options.headers||{}}get formattedBody(){const t=Object.prototype.toString.call(this.body)==="[object String]";const e=this.headers["Content-Type"]==="application/json";return e&&!t?JSON.stringify(this.body):this.body}}async function o(t,e){const n=new FetchRequest("get",t,e);return n.perform()}async function i(t,e){const n=new FetchRequest("post",t,e);return n.perform()}async function c(t,e){const n=new FetchRequest("put",t,e);return n.perform()}async function a(t,e){const n=new FetchRequest("patch",t,e);return n.perform()}async function h(t,e){const n=new FetchRequest("delete",t,e);return n.perform()}export{FetchRequest,FetchResponse,RequestInterceptor,h as destroy,o as get,a as patch,i as post,c as put}; + diff --git a/vendor/javascript/rails-request.js b/vendor/javascript/rails-request.js deleted file mode 100644 index 4b8d648cec..0000000000 --- a/vendor/javascript/rails-request.js +++ /dev/null @@ -1,308 +0,0 @@ -// Patched to use Turbo.fetch. @TODO Upstream this. -class FetchResponse { - constructor(response) { - this.response = response; - } - get statusCode() { - return this.response.status; - } - get redirected() { - return this.response.redirected; - } - get ok() { - return this.response.ok; - } - get unauthenticated() { - return this.statusCode === 401; - } - get unprocessableEntity() { - return this.statusCode === 422; - } - get authenticationURL() { - return this.response.headers.get("WWW-Authenticate"); - } - get contentType() { - const contentType = this.response.headers.get("Content-Type") || ""; - return contentType.replace(/;.*$/, ""); - } - get headers() { - return this.response.headers; - } - get html() { - if (this.contentType.match(/^(application|text)\/(html|xhtml\+xml)$/)) { - return this.text; - } - return Promise.reject(new Error(`Expected an HTML response but got "${this.contentType}" instead`)); - } - get json() { - if (this.contentType.match(/^application\/.*json$/)) { - return this.responseJson || (this.responseJson = this.response.json()); - } - return Promise.reject(new Error(`Expected a JSON response but got "${this.contentType}" instead`)); - } - get text() { - return this.responseText || (this.responseText = this.response.text()); - } - get isTurboStream() { - return this.contentType.match(/^text\/vnd\.turbo-stream\.html/); - } - get isScript() { - return this.contentType.match(/\b(?:java|ecma)script\b/); - } - async renderTurboStream() { - if (this.isTurboStream) { - if (window.Turbo) { - await window.Turbo.renderStreamMessage(await this.text); - } else { - console.warn("You must set `window.Turbo = Turbo` to automatically process Turbo Stream events with request.js"); - } - } else { - return Promise.reject(new Error(`Expected a Turbo Stream response but got "${this.contentType}" instead`)); - } - } - async activeScript() { - if (this.isScript) { - const script = document.createElement("script"); - const metaTag = document.querySelector("meta[name=csp-nonce]"); - if (metaTag) { - const nonce = metaTag.nonce === "" ? metaTag.content : metaTag.nonce; - if (nonce) { - script.setAttribute("nonce", nonce); - } - } - script.innerHTML = await this.text; - document.body.appendChild(script); - } else { - return Promise.reject(new Error(`Expected a Script response but got "${this.contentType}" instead`)); - } - } -} - -class RequestInterceptor { - static register(interceptor) { - this.interceptor = interceptor; - } - static get() { - return this.interceptor; - } - static reset() { - this.interceptor = undefined; - } -} - -function getCookie(name) { - const cookies = document.cookie ? document.cookie.split("; ") : []; - const prefix = `${encodeURIComponent(name)}=`; - const cookie = cookies.find((cookie => cookie.startsWith(prefix))); - if (cookie) { - const value = cookie.split("=").slice(1).join("="); - if (value) { - return decodeURIComponent(value); - } - } -} - -function compact(object) { - const result = {}; - for (const key in object) { - const value = object[key]; - if (value !== undefined) { - result[key] = value; - } - } - return result; -} - -function metaContent(name) { - const element = document.head.querySelector(`meta[name="${name}"]`); - return element && element.content; -} - -function stringEntriesFromFormData(formData) { - return [ ...formData ].reduce(((entries, [name, value]) => entries.concat(typeof value === "string" ? [ [ name, value ] ] : [])), []); -} - -function mergeEntries(searchParams, entries) { - for (const [name, value] of entries) { - if (value instanceof window.File) continue; - if (searchParams.has(name) && !name.includes("[]")) { - searchParams.delete(name); - searchParams.set(name, value); - } else { - searchParams.append(name, value); - } - } -} - -class FetchRequest { - constructor(method, url, options = {}) { - this.method = method; - this.options = options; - this.originalUrl = url.toString(); - } - async perform() { - try { - const requestInterceptor = RequestInterceptor.get(); - if (requestInterceptor) { - await requestInterceptor(this); - } - } catch (error) { - console.error(error); - } - const fetch = this.responseKind === "turbo-stream" && window.Turbo ? window.Turbo.fetch : window.fetch; - const response = new FetchResponse(await Turbo.fetch(this.url, this.fetchOptions)); - if (response.unauthenticated && response.authenticationURL) { - return Promise.reject(window.location.href = response.authenticationURL); - } - if (response.isScript) { - await response.activeScript(); - } - const responseStatusIsTurboStreamable = response.ok || response.unprocessableEntity; - if (responseStatusIsTurboStreamable && response.isTurboStream) { - await response.renderTurboStream(); - } - return response; - } - addHeader(key, value) { - const headers = this.additionalHeaders; - headers[key] = value; - this.options.headers = headers; - } - sameHostname() { - if (!this.originalUrl.startsWith("http:") && !this.originalUrl.startsWith("https:")) { - return true; - } - try { - return new URL(this.originalUrl).hostname === window.location.hostname; - } catch (_) { - return true; - } - } - get fetchOptions() { - return { - method: this.method.toUpperCase(), - headers: this.headers, - body: this.formattedBody, - signal: this.signal, - credentials: this.credentials, - redirect: this.redirect, - keepalive: this.keepalive - }; - } - get headers() { - const baseHeaders = { - "X-Requested-With": "XMLHttpRequest", - "Content-Type": this.contentType, - Accept: this.accept - }; - if (this.sameHostname()) { - baseHeaders["X-CSRF-Token"] = this.csrfToken; - } - return compact(Object.assign(baseHeaders, this.additionalHeaders)); - } - get csrfToken() { - return getCookie(metaContent("csrf-param")) || metaContent("csrf-token"); - } - get contentType() { - if (this.options.contentType) { - return this.options.contentType; - } else if (this.body == null || this.body instanceof window.FormData) { - return undefined; - } else if (this.body instanceof window.File) { - return this.body.type; - } - return "application/json"; - } - get accept() { - switch (this.responseKind) { - case "html": - return "text/html, application/xhtml+xml"; - - case "turbo-stream": - return "text/vnd.turbo-stream.html, text/html, application/xhtml+xml"; - - case "json": - return "application/json, application/vnd.api+json"; - - case "script": - return "text/javascript, application/javascript"; - - default: - return "*/*"; - } - } - get body() { - return this.options.body; - } - get query() { - const originalQuery = (this.originalUrl.split("?")[1] || "").split("#")[0]; - const params = new URLSearchParams(originalQuery); - let requestQuery = this.options.query; - if (requestQuery instanceof window.FormData) { - requestQuery = stringEntriesFromFormData(requestQuery); - } else if (requestQuery instanceof window.URLSearchParams) { - requestQuery = requestQuery.entries(); - } else { - requestQuery = Object.entries(requestQuery || {}); - } - mergeEntries(params, requestQuery); - const query = params.toString(); - return query.length > 0 ? `?${query}` : ""; - } - get url() { - return this.originalUrl.split("?")[0].split("#")[0] + this.query; - } - get responseKind() { - return this.options.responseKind || "html"; - } - get signal() { - return this.options.signal; - } - get redirect() { - return this.options.redirect || "follow"; - } - get credentials() { - return this.options.credentials || "same-origin"; - } - get keepalive() { - return this.options.keepalive || false; - } - get additionalHeaders() { - return this.options.headers || {}; - } - get formattedBody() { - const bodyIsAString = Object.prototype.toString.call(this.body) === "[object String]"; - const contentTypeIsJson = this.headers["Content-Type"] === "application/json"; - if (contentTypeIsJson && !bodyIsAString) { - return JSON.stringify(this.body); - } - return this.body; - } -} - -async function get(url, options) { - const request = new FetchRequest("get", url, options); - return request.perform(); -} - -async function post(url, options) { - const request = new FetchRequest("post", url, options); - return request.perform(); -} - -async function put(url, options) { - const request = new FetchRequest("put", url, options); - return request.perform(); -} - -async function patch(url, options) { - const request = new FetchRequest("patch", url, options); - return request.perform(); -} - -async function destroy(url, options) { - const request = new FetchRequest("delete", url, options); - return request.perform(); -} - -export { FetchRequest, FetchResponse, RequestInterceptor, destroy, get, patch, post, put };