telefunc
Version:
Remote functions. Instead of API.
140 lines (139 loc) • 5.46 kB
JavaScript
export { runTelefunc };
import { assert, objectAssign, isProduction } from './utils.js';
import { loadTelefuncFiles } from './runTelefunc/loadTelefuncFiles.js';
import { parseHttpRequest } from './runTelefunc/parseHttpRequest.js';
// import { getEtag } from './runTelefunc/getEtag.js'
import { executeTelefunction } from './runTelefunc/executeTelefunction.js';
import { serializeTelefunctionResult } from './runTelefunc/serializeTelefunctionResult.js';
import { handleError } from './runTelefunc/handleError.js';
import { callBugListeners } from './runTelefunc/onBug.js';
import { applyShield } from './runTelefunc/applyShield.js';
import { findTelefunction } from './runTelefunc/findTelefunction.js';
import { getServerConfig } from './serverConfig.js';
import { STATUS_CODE_THROW_ABORT, STATUS_CODE_SHIELD_VALIDATION_ERROR, STATUS_BODY_SHIELD_VALIDATION_ERROR, STATUS_CODE_INTERNAL_SERVER_ERROR, STATUS_BODY_INTERNAL_SERVER_ERROR, STATUS_CODE_MALFORMED_REQUEST, STATUS_BODY_MALFORMED_REQUEST, STATUS_CODE_SUCCESS, } from '../../shared/constants.js';
const shieldValidationError = {
statusCode: STATUS_CODE_SHIELD_VALIDATION_ERROR,
body: STATUS_BODY_SHIELD_VALIDATION_ERROR,
contentType: 'text/plain',
etag: null,
};
// HTTP Response for:
// - User's telefunction threw an error that isn't `Abort()` (i.e. the telefunction has a bug).
// - The `.telefunc.js` file exports a non-function value.
// - The Telefunc code threw an error (i.e. Telefunc has a bug).
const serverError = {
statusCode: STATUS_CODE_INTERNAL_SERVER_ERROR,
body: STATUS_BODY_INTERNAL_SERVER_ERROR,
contentType: 'text/plain',
etag: null,
};
// HTTP Response for:
// - Some non-telefunc client makes a malformed HTTP request.
// - The telefunction couldn't be found.
const malformedRequest = {
statusCode: STATUS_CODE_MALFORMED_REQUEST,
body: STATUS_BODY_MALFORMED_REQUEST,
contentType: 'text/plain',
etag: null,
};
async function runTelefunc(runContext) {
try {
return await runTelefunc_(runContext);
}
catch (err) {
callBugListeners(err);
handleError(err);
return {
err,
...serverError,
};
}
}
async function runTelefunc_(httpRequest) {
const runContext = {};
{
// TO-DO/eventually: remove? Since `serverConfig` is global I don't think we need to set it to `runContext`, see for example https://github.com/brillout/telefunc/commit/5e3367d2d463b72e805e75ddfc68ef7f177a35c0
const serverConfig = getServerConfig();
objectAssign(runContext, {
httpRequest,
serverConfig: {
disableNamingConvention: serverConfig.disableNamingConvention,
telefuncUrl: serverConfig.telefuncUrl,
log: serverConfig.log,
},
appRootDir: serverConfig.root,
telefuncFilesManuallyProvidedByUser: serverConfig.telefuncFiles,
});
}
{
const logMalformedRequests = !isProduction(); /* || process.env.DEBUG.includes('telefunc') */
objectAssign(runContext, { logMalformedRequests });
}
objectAssign(runContext, {
providedContext: httpRequest.context || null,
});
{
const parsed = parseHttpRequest(runContext);
if (parsed.isMalformedRequest) {
return malformedRequest;
}
const { telefunctionKey, telefunctionArgs, telefuncFilePath, telefunctionName } = parsed;
objectAssign(runContext, {
telefunctionKey,
telefunctionArgs,
telefuncFilePath,
telefunctionName,
});
}
{
const { telefuncFilesLoaded, telefuncFilesAll } = await loadTelefuncFiles(runContext);
assert(telefuncFilesLoaded, 'No `.telefunc.js` file found');
objectAssign(runContext, { telefuncFilesLoaded, telefuncFilesAll });
}
{
const telefunction = await findTelefunction(runContext);
if (!telefunction) {
return malformedRequest;
}
objectAssign(runContext, { telefunction });
}
{
const { isValidRequest } = applyShield(runContext);
objectAssign(runContext, { isValidRequest });
if (!isValidRequest) {
objectAssign(runContext, {
telefunctionAborted: true,
telefunctionReturn: undefined,
});
return shieldValidationError;
}
}
{
assert(runContext.isValidRequest);
const { telefunctionReturn, telefunctionAborted, telefunctionHasErrored, telefunctionError } = await executeTelefunction(runContext);
objectAssign(runContext, {
telefunctionReturn,
telefunctionHasErrored,
telefunctionAborted,
telefunctionError,
});
}
if (runContext.telefunctionHasErrored) {
throw runContext.telefunctionError;
}
{
const httpResponseBody = serializeTelefunctionResult(runContext);
objectAssign(runContext, { httpResponseBody });
}
// {
// const httpResponseEtag = await getEtag(runContext)
// objectAssign(runContext, { httpResponseEtag })
// }
return {
statusCode: runContext.telefunctionAborted ? STATUS_CODE_THROW_ABORT : STATUS_CODE_SUCCESS,
body: runContext.httpResponseBody,
contentType: 'text/plain',
// etag: runContext.httpResponseEtag,
etag: null,
};
}