diff --git a/package-lock.json b/package-lock.json index eab9cf355..e38d705a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,9 @@ "dev": true }, "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.2.tgz", + "integrity": "sha512-IoVDIHpsgE/fu7eXBeRWt8zLbDrSvD7H1gpomOkPpBoEN8KCruCqSDdqo8dddwQQrui30KSvQBaMUOJiuFu6QQ==", "dev": true }, "@babel/runtime": { @@ -1953,12 +1953,12 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "catharsis": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", - "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", "dev": true, "requires": { - "lodash": "^4.17.14" + "lodash": "^4.17.15" } }, "chalk": { @@ -2957,9 +2957,9 @@ } }, "electron": { - "version": "13.0.0-beta.24", - "resolved": "https://registry.npmjs.org/electron/-/electron-13.0.0-beta.24.tgz", - "integrity": "sha512-2zgmhrjdkWrKsBIepF1XkRCn4ODFxd4lgB58BLv3E66yiQJC0ugOnHoqeZcsskQ7bBMGGQwGAzB5nwOJ/83BOg==", + "version": "13.0.0-beta.26", + "resolved": "https://registry.npmjs.org/electron/-/electron-13.0.0-beta.26.tgz", + "integrity": "sha512-glDAQjSzV26JvmB9pbdd1dLkjqUvBv9X0B8FrRvIK0BAhEmMzzZ3gKD6VPsxg2omDYMI35oThQ+92D4RxRcb+g==", "dev": true, "requires": { "@electron/get": "^1.0.1", @@ -2968,9 +2968,9 @@ }, "dependencies": { "@types/node": { - "version": "14.14.44", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.44.tgz", - "integrity": "sha512-+gaugz6Oce6ZInfI/tK4Pq5wIIkJMEJUu92RB3Eu93mtj4wjjjz9EB5mLp5s1pSsLXdC/CPut/xF20ZzAQJbTA==", + "version": "14.14.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.45.tgz", + "integrity": "sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==", "dev": true } } @@ -5250,25 +5250,25 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "jsdoc": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.6.tgz", - "integrity": "sha512-znR99e1BHeyEkSvgDDpX0sTiTu+8aQyDl9DawrkOGZTTW8hv0deIFXx87114zJ7gRaDZKVQD/4tr1ifmJp9xhQ==", + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz", + "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==", "dev": true, "requires": { "@babel/parser": "^7.9.4", "bluebird": "^3.7.2", - "catharsis": "^0.8.11", + "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.1", "klaw": "^3.0.0", "markdown-it": "^10.0.0", "markdown-it-anchor": "^5.2.7", - "marked": "^0.8.2", + "marked": "^2.0.3", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", "taffydb": "2.6.2", - "underscore": "~1.10.2" + "underscore": "~1.13.1" }, "dependencies": { "bluebird": { @@ -5716,9 +5716,9 @@ "dev": true }, "marked": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", - "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz", + "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==", "dev": true }, "matcher": { @@ -8026,9 +8026,9 @@ } }, "underscore": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", - "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", "dev": true }, "unescape": { diff --git a/package.json b/package.json index 0705eab59..09cbb3f85 100644 --- a/package.json +++ b/package.json @@ -80,13 +80,13 @@ }, "devDependencies": { "cross-env": "7.0.3", - "electron": "13.0.0-beta.24", + "electron": "13.0.0-beta.26", "electron-builder": "22.11.3", "electron-packager": "15.2.0", "electron-rebuild": "2.3.5", "esm": "3.2.25", "jasmine": "3.7.0", - "jsdoc": "3.6.6", + "jsdoc": "3.6.7", "lorem-ipsum": "2.0.3", "rcedit": "3.0.0", "webpack": "5.37.0", diff --git a/src/routes/api/script.js b/src/routes/api/script.js index de673dd5b..6cf8e9e5d 100644 --- a/src/routes/api/script.js +++ b/src/routes/api/script.js @@ -5,11 +5,11 @@ const attributeService = require('../../services/attributes'); const becca = require('../../services/becca/becca'); const syncService = require('../../services/sync'); -async function exec(req) { +function exec(req) { try { const {body} = req; - const result = await scriptService.executeScript( + const result = scriptService.executeScript( body.script, body.params, body.startNoteId, @@ -29,10 +29,10 @@ async function exec(req) { } } -async function run(req) { +function run(req) { const note = becca.getNote(req.params.noteId); - const result = await scriptService.executeNote(note, { originEntity: note }); + const result = scriptService.executeNote(note, { originEntity: note }); return { executionResult: result }; } diff --git a/src/routes/api/search.js b/src/routes/api/search.js index d6f9691ac..593ada04f 100644 --- a/src/routes/api/search.js +++ b/src/routes/api/search.js @@ -167,7 +167,7 @@ async function searchAndExecute(req) { } } -async function searchFromRelation(note, relationName) { +function searchFromRelation(note, relationName) { const scriptNote = note.getRelationTarget(relationName); if (!scriptNote) { @@ -188,7 +188,7 @@ async function searchFromRelation(note, relationName) { return []; } - const result = await scriptService.executeNote(scriptNote, { originEntity: note }); + const result = scriptService.executeNote(scriptNote, { originEntity: note }); if (!Array.isArray(result)) { log.info(`Result from ${scriptNote.noteId} is not an array.`); diff --git a/src/routes/custom.js b/src/routes/custom.js index 929252492..50bd75a63 100644 --- a/src/routes/custom.js +++ b/src/routes/custom.js @@ -6,7 +6,7 @@ const cls = require('../services/cls'); const sql = require("../services/sql"); const becca = require("../services/becca/becca"); -async function handleRequest(req, res) { +function handleRequest(req, res) { // express puts content after first slash into 0 index element const path = req.params.path + req.params[0]; @@ -72,7 +72,7 @@ async function handleRequest(req, res) { function register(router) { // explicitly no CSRF middleware since it's meant to allow integration from external services - router.all('/custom/:path*', async (req, res, next) => { + router.all('/custom/:path*', (req, res, next) => { cls.namespace.bindEmitter(req); cls.namespace.bindEmitter(res); diff --git a/src/services/script.js b/src/services/script.js index 6bd497d16..096019c16 100644 --- a/src/services/script.js +++ b/src/services/script.js @@ -1,10 +1,9 @@ const ScriptContext = require('./script_context'); -const repository = require('./repository'); const cls = require('./cls'); const log = require('./log'); const becca = require("./becca/becca"); -async function executeNote(note, apiParams) { +function executeNote(note, apiParams) { if (!note.isJavaScript() || note.getScriptEnv() !== 'backend' || !note.isContentAvailable) { log.info(`Cannot execute note ${note.noteId} "${note.title}", note must be of type "Code: JS frontend"`); @@ -16,16 +15,16 @@ async function executeNote(note, apiParams) { return executeBundle(bundle, apiParams); } -async function executeNoteNoException(note, apiParams) { +function executeNoteNoException(note, apiParams) { try { - await executeNote(note, apiParams); + executeNote(note, apiParams); } catch (e) { // just swallow, exception is logged already in executeNote } } -async function executeBundle(bundle, apiParams = {}) { +function executeBundle(bundle, apiParams = {}) { if (!apiParams.startNote) { // this is the default case, the only exception is when we want to preserve frontend startNote apiParams.startNote = bundle.note; @@ -34,12 +33,12 @@ async function executeBundle(bundle, apiParams = {}) { cls.set('sourceId', 'script'); // last \r\n is necessary if script contains line comment on its last line - const script = "async function() {\r\n" + bundle.script + "\r\n}"; + const script = "function() {\r\n" + bundle.script + "\r\n}"; const ctx = new ScriptContext(bundle.allNotes, apiParams); try { - return await execute(ctx, script); + return execute(ctx, script); } catch (e) { log.error(`Execution of script "${bundle.note.title}" (${bundle.note.noteId}) failed with error: ${e.message}`); @@ -49,10 +48,13 @@ async function executeBundle(bundle, apiParams = {}) { } /** + * THIS METHOD CANT BE ASYNC, OTHERWISE TRANSACTION WRAPPER WON'T BE EFFECTIVE AND WE WILL BE LOSING THE + * ENTITY CHANGES IN CLS. + * * This method preserves frontend startNode - that's why we start execution from currentNote and override * bundle's startNote. */ -async function executeScript(script, params, startNoteId, currentNoteId, originEntityName, originEntityId) { +function executeScript(script, params, startNoteId, currentNoteId, originEntityName, originEntityId) { const startNote = becca.getNote(startNoteId); const currentNote = becca.getNote(currentNoteId); const originEntity = becca.getEntityFromName(originEntityName, originEntityId); @@ -63,11 +65,11 @@ async function executeScript(script, params, startNoteId, currentNoteId, originE const bundle = getScriptBundle(currentNote, true, null, [], backendOverrideContent); - return await executeBundle(bundle, { startNote, originEntity }); + return executeBundle(bundle, { startNote, originEntity }); } -async function execute(ctx, script) { - return await (function() { return eval(`const apiContext = this;\r\n(${script}\r\n)()`); }.call(ctx)); +function execute(ctx, script) { + return function() { return eval(`const apiContext = this;\r\n(${script}\r\n)()`); }.call(ctx); } function getParams(params) { @@ -153,10 +155,13 @@ function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = const moduleNoteIds = modules.map(mod => mod.noteId); + // only frontend scripts are async. Backend cannot be async because of transaction management. + const isFrontend = scriptEnv === 'frontend'; + if (note.isJavaScript()) { bundle.script += ` apiContext.modules['${note.noteId}'] = {}; -${root ? 'return ' : ''}await ((async function(exports, module, require, api` + (modules.length > 0 ? ', ' : '') + +${root ? 'return ' : ''}${isFrontend ? 'await' : ''} ((${isFrontend ? 'async' : ''} function(exports, module, require, api` + (modules.length > 0 ? ', ' : '') + modules.map(child => sanitizeVariableName(child.title)).join(', ') + `) { try { ${backendOverrideContent || note.getContent()};