telefunc
Version:
Remote functions. Instead of API.
90 lines (89 loc) • 4.08 kB
JavaScript
export { makeHttpRequest };
import { parse } from '@brillout/json-serializer/parse';
import { assert, assertUsage, isObject, objectAssign } from '../utils.js';
import { callOnAbortListeners } from './onAbort.js';
const method = 'POST';
const STATUS_CODE_SUCCESS = 200;
const STATUS_CODE_ABORT = 403;
const STATUS_CODE_BUG = 500;
const STATUS_CODE_INVALID = 400;
async function makeHttpRequest(callContext) {
var _a;
let response;
try {
const fetch = (_a = callContext.fetch) !== null && _a !== void 0 ? _a : window.fetch;
response = await fetch(callContext.telefuncUrl, {
method,
body: callContext.httpRequestBody,
credentials: 'same-origin',
headers: {
...callContext.httpHeaders,
'Content-Type': 'text/plain',
},
});
}
catch (_) {
const telefunctionCallError = new Error('No Server Connection');
objectAssign(telefunctionCallError, { isConnectionError: true });
throw telefunctionCallError;
}
const statusCode = response.status;
if (statusCode === STATUS_CODE_SUCCESS) {
const { ret } = await parseResponseBody(response, callContext);
const telefunctionReturn = ret;
return { telefunctionReturn };
}
else if (statusCode === STATUS_CODE_ABORT) {
const { ret } = await parseResponseBody(response, callContext);
const abortValue = ret;
const telefunctionCallError = new Error(`Aborted telefunction call ${callContext.telefunctionName}() (${callContext.telefuncFilePath}).`);
objectAssign(telefunctionCallError, { isAbort: true, abortValue });
callOnAbortListeners(telefunctionCallError);
throw telefunctionCallError;
}
else if (statusCode === STATUS_CODE_BUG) {
const responseBody = await response.text();
const errMsg = 'Internal Server Error';
assertUsage(responseBody === errMsg, wrongInstallation({ method, callContext }));
throw new Error(`${errMsg}. See server logs.`);
}
else if (statusCode === STATUS_CODE_INVALID) {
const responseBody = await response.text();
assertUsage(responseBody === 'Invalid Telefunc Request', wrongInstallation({ method, callContext }));
/* With Next.js 12: when renaming a `.telefunc.js` file the client makes a request with the new `.telefunc.js` name while the server is still serving the old `.telefunc.js` name. Seems like a race condition: trying again seems to fix the error.
// This should never happen as the Telefunc Client shouldn't make invalid requests
assert(false)
*/
assertUsage(false, 'Try again. You may need to reload the page. (The client and server are/was out-of-sync.)');
}
else {
assertUsage(statusCode !== 404, wrongInstallation({
reason: 'a 404 HTTP response',
method,
isNotInstalled: true,
callContext,
}));
assertUsage(false, wrongInstallation({
reason: `a status code \`${statusCode}\` which Telefunc never returns`,
method,
callContext,
}));
}
}
async function parseResponseBody(response, callContext) {
const responseBody = await response.text();
const responseBodyParsed = parse(responseBody);
assertUsage(isObject(responseBodyParsed) && 'ret' in responseBodyParsed, wrongInstallation({ method, callContext }));
assert(response.status !== STATUS_CODE_ABORT || 'abort' in responseBodyParsed);
const { ret } = responseBodyParsed;
return { ret };
}
function wrongInstallation({ reason = 'an HTTP response body that Telefunc never generates', callContext, method, isNotInstalled, }) {
let msg = [`Telefunc doesn't seem to be `];
if (!isNotInstalled)
msg.push('(properly) ');
msg.push('installed on your server');
msg.push(...[`: the HTTP ${method} \`${callContext.telefuncUrl}\` request returned `, reason]);
msg.push(`, see https://telefunc.com/install`);
return msg.join('');
}