mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	server-ts: Convert services/script
This commit is contained in:
		| @@ -1,6 +1,6 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| const scriptService = require('../../services/script.js'); | const scriptService = require('../../services/script'); | ||||||
| const attributeService = require('../../services/attributes'); | const attributeService = require('../../services/attributes'); | ||||||
| const becca = require('../../becca/becca'); | const becca = require('../../becca/becca'); | ||||||
| const syncService = require('../../services/sync'); | const syncService = require('../../services/sync'); | ||||||
| @@ -11,7 +11,7 @@ const sql = require('../../services/sql'); | |||||||
| // this and does result.then(). | // this and does result.then(). | ||||||
| async function exec(req) { | async function exec(req) { | ||||||
|     try { |     try { | ||||||
|         const {body} = req; |         const { body } = req; | ||||||
|  |  | ||||||
|         const execute = body => scriptService.executeScript( |         const execute = body => scriptService.executeScript( | ||||||
|             body.script, |             body.script, | ||||||
| @@ -115,7 +115,7 @@ function getRelationBundles(req) { | |||||||
|  |  | ||||||
| function getBundle(req) { | function getBundle(req) { | ||||||
|     const note = becca.getNote(req.params.noteId); |     const note = becca.getNote(req.params.noteId); | ||||||
|     const {script, params} = req.body; |     const { script, params } = req.body; | ||||||
|  |  | ||||||
|     return scriptService.getScriptBundleForFrontend(note, script, params); |     return scriptService.getScriptBundleForFrontend(note, script, params); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| const log = require('../services/log'); | const log = require('../services/log'); | ||||||
| const fileService = require('./api/files.js'); | const fileService = require('./api/files.js'); | ||||||
| const scriptService = require('../services/script.js'); | const scriptService = require('../services/script'); | ||||||
| const cls = require('../services/cls'); | const cls = require('../services/cls'); | ||||||
| const sql = require('../services/sql'); | const sql = require('../services/sql'); | ||||||
| const becca = require('../becca/becca'); | const becca = require('../becca/becca'); | ||||||
|   | |||||||
| @@ -63,7 +63,7 @@ interface Api { | |||||||
|      * Note where the script started executing (entrypoint). |      * Note where the script started executing (entrypoint). | ||||||
|      * As an analogy, in C this would be the file which contains the main() function of the current process. |      * As an analogy, in C this would be the file which contains the main() function of the current process. | ||||||
|      */ |      */ | ||||||
|     startNote: BNote; |     startNote?: BNote; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Note where the script is currently executing. This comes into play when your script is spread in multiple code |      * Note where the script is currently executing. This comes into play when your script is spread in multiple code | ||||||
| @@ -76,7 +76,7 @@ interface Api { | |||||||
|     /** |     /** | ||||||
|      * Entity whose event triggered this execution |      * Entity whose event triggered this execution | ||||||
|      */ |      */ | ||||||
|     originEntity: AbstractBeccaEntity<any>; |     originEntity?: AbstractBeccaEntity<any>; | ||||||
|      |      | ||||||
|     /** |     /** | ||||||
|      * Axios library for HTTP requests. See {@link https://axios-http.com} for documentation |      * Axios library for HTTP requests. See {@link https://axios-http.com} for documentation | ||||||
| @@ -507,6 +507,10 @@ function BackendScriptApi(this: Api, currentNote: BNote, apiParams: ApiParams) { | |||||||
|     this.log = message => { |     this.log = message => { | ||||||
|         log.info(message); |         log.info(message); | ||||||
|  |  | ||||||
|  |         if (!this.startNote) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         const { noteId } = this.startNote; |         const { noteId } = this.startNote; | ||||||
|  |  | ||||||
|         this.logMessages[noteId] = this.logMessages[noteId] || []; |         this.logMessages[noteId] = this.logMessages[noteId] || []; | ||||||
| @@ -621,10 +625,10 @@ function BackendScriptApi(this: Api, currentNote: BNote, apiParams: ApiParams) { | |||||||
|             type: 'execute-script', |             type: 'execute-script', | ||||||
|             script: script, |             script: script, | ||||||
|             params: prepareParams(params), |             params: prepareParams(params), | ||||||
|             startNoteId: this.startNote.noteId, |             startNoteId: this.startNote?.noteId, | ||||||
|             currentNoteId: this.currentNote.noteId, |             currentNoteId: this.currentNote.noteId, | ||||||
|             originEntityName: "notes", // currently there's no other entity on the frontend which can trigger event |             originEntityName: "notes", // currently there's no other entity on the frontend which can trigger event | ||||||
|             originEntityId: ("noteId" in this.originEntity && (this.originEntity as BNote)?.noteId) || null |             originEntityId: (this.originEntity && "noteId" in this.originEntity && (this.originEntity as BNote)?.noteId) || null | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         function prepareParams(params: any[]) { |         function prepareParams(params: any[]) { | ||||||
|   | |||||||
| @@ -2,6 +2,6 @@ import AbstractBeccaEntity = require("../becca/entities/abstract_becca_entity"); | |||||||
| import BNote = require("../becca/entities/bnote"); | import BNote = require("../becca/entities/bnote"); | ||||||
|  |  | ||||||
| export interface ApiParams { | export interface ApiParams { | ||||||
|     startNote: BNote; |     startNote?: BNote; | ||||||
|     originEntity: AbstractBeccaEntity<any>; |     originEntity?: AbstractBeccaEntity<any>; | ||||||
| } | } | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| const eventService = require('./events'); | const eventService = require('./events'); | ||||||
| const scriptService = require('./script.js'); | const scriptService = require('./script'); | ||||||
| const treeService = require('./tree'); | const treeService = require('./tree'); | ||||||
| const noteService = require('./notes'); | const noteService = require('./notes'); | ||||||
| const becca = require('../becca/becca'); | const becca = require('../becca/becca'); | ||||||
| @@ -42,7 +42,7 @@ eventService.subscribe(eventService.NOTE_TITLE_CHANGED, note => { | |||||||
|     } |     } | ||||||
| }); | }); | ||||||
|  |  | ||||||
| eventService.subscribe([ eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED ], ({ entityName, entity }) => { | eventService.subscribe([eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED], ({ entityName, entity }) => { | ||||||
|     if (entityName === 'attributes') { |     if (entityName === 'attributes') { | ||||||
|         runAttachedRelations(entity.getNote(), 'runOnAttributeChange', entity); |         runAttachedRelations(entity.getNote(), 'runOnAttributeChange', entity); | ||||||
|  |  | ||||||
| @@ -58,7 +58,7 @@ eventService.subscribe([ eventService.ENTITY_CHANGED, eventService.ENTITY_DELETE | |||||||
|     } |     } | ||||||
| }); | }); | ||||||
|  |  | ||||||
| eventService.subscribe(eventService.ENTITY_CHANGED, ({entityName, entity}) => { | eventService.subscribe(eventService.ENTITY_CHANGED, ({ entityName, entity }) => { | ||||||
|     if (entityName === 'branches') { |     if (entityName === 'branches') { | ||||||
|         const parentNote = becca.getNote(entity.parentNoteId); |         const parentNote = becca.getNote(entity.parentNoteId); | ||||||
|  |  | ||||||
| @@ -74,7 +74,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, ({entityName, entity}) => { | |||||||
|     } |     } | ||||||
| }); | }); | ||||||
|  |  | ||||||
| eventService.subscribe(eventService.NOTE_CONTENT_CHANGE, ({entity}) => { | eventService.subscribe(eventService.NOTE_CONTENT_CHANGE, ({ entity }) => { | ||||||
|     runAttachedRelations(entity, 'runOnNoteContentChange', entity); |     runAttachedRelations(entity, 'runOnNoteContentChange', entity); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| const scriptService = require('./script.js'); | const scriptService = require('./script'); | ||||||
| const cls = require('./cls'); | const cls = require('./cls'); | ||||||
| const sqlInit = require('./sql_init'); | const sqlInit = require('./sql_init'); | ||||||
| const config = require('./config'); | const config = require('./config'); | ||||||
| @@ -34,7 +34,7 @@ function runNotesWithLabel(runAttrValue) { | |||||||
|         if ((runOnInstances.length === 0 || runOnInstances.includes(instanceName)) |         if ((runOnInstances.length === 0 || runOnInstances.includes(instanceName)) | ||||||
|             && (runAtHours.length === 0 || runAtHours.includes(currentHours)) |             && (runAtHours.length === 0 || runAtHours.includes(currentHours)) | ||||||
|         ) { |         ) { | ||||||
|             scriptService.executeNoteNoException(note, {originEntity: note}); |             scriptService.executeNoteNoException(note, { originEntity: note }); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,9 +1,22 @@ | |||||||
| const ScriptContext = require('./script_context'); | import ScriptContext = require('./script_context'); | ||||||
| const cls = require('./cls'); | import cls = require('./cls'); | ||||||
| const log = require('./log'); | import log = require('./log'); | ||||||
| const becca = require('../becca/becca'); | import becca = require('../becca/becca'); | ||||||
|  | import BNote = require('../becca/entities/bnote'); | ||||||
|  | import { ApiParams } from './backend_script_api_interface'; | ||||||
| 
 | 
 | ||||||
| function executeNote(note, apiParams) { | interface Bundle { | ||||||
|  |     note?: BNote; | ||||||
|  |     noteId?: string; | ||||||
|  |     script: string; | ||||||
|  |     html: string; | ||||||
|  |     allNotes?: BNote[]; | ||||||
|  |     allNoteIds?: string[]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ScriptParams = any[]; | ||||||
|  | 
 | ||||||
|  | function executeNote(note: BNote, apiParams: ApiParams) { | ||||||
|     if (!note.isJavaScript() || note.getScriptEnv() !== 'backend' || !note.isContentAvailable()) { |     if (!note.isJavaScript() || note.getScriptEnv() !== 'backend' || !note.isContentAvailable()) { | ||||||
|         log.info(`Cannot execute note ${note.noteId} "${note.title}", note must be of type "Code: JS backend"`); |         log.info(`Cannot execute note ${note.noteId} "${note.title}", note must be of type "Code: JS backend"`); | ||||||
| 
 | 
 | ||||||
| @@ -11,11 +24,14 @@ function executeNote(note, apiParams) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const bundle = getScriptBundle(note, true, 'backend'); |     const bundle = getScriptBundle(note, true, 'backend'); | ||||||
| 
 |     if (!bundle) { | ||||||
|  |         throw new Error("Unable to determine bundle."); | ||||||
|  |     } | ||||||
|  |      | ||||||
|     return executeBundle(bundle, apiParams); |     return executeBundle(bundle, apiParams); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function executeNoteNoException(note, apiParams) { | function executeNoteNoException(note: BNote, apiParams: ApiParams) { | ||||||
|     try { |     try { | ||||||
|         executeNote(note, apiParams); |         executeNote(note, apiParams); | ||||||
|     } |     } | ||||||
| @@ -24,7 +40,7 @@ function executeNoteNoException(note, apiParams) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function executeBundle(bundle, apiParams = {}) { | function executeBundle(bundle: Bundle, apiParams: ApiParams = {}) { | ||||||
|     if (!apiParams.startNote) { |     if (!apiParams.startNote) { | ||||||
|         // this is the default case, the only exception is when we want to preserve frontend startNote
 |         // this is the default case, the only exception is when we want to preserve frontend startNote
 | ||||||
|         apiParams.startNote = bundle.note; |         apiParams.startNote = bundle.note; | ||||||
| @@ -33,19 +49,19 @@ function executeBundle(bundle, apiParams = {}) { | |||||||
|     const originalComponentId = cls.get('componentId'); |     const originalComponentId = cls.get('componentId'); | ||||||
| 
 | 
 | ||||||
|     cls.set('componentId', 'script'); |     cls.set('componentId', 'script'); | ||||||
|     cls.set('bundleNoteId', bundle.note.noteId); |     cls.set('bundleNoteId', bundle.note?.noteId); | ||||||
| 
 | 
 | ||||||
|     // last \r\n is necessary if the script contains line comment on its last line
 |     // last \r\n is necessary if the script contains line comment on its last line
 | ||||||
|     const script = `function() {\r
 |     const script = `function() {\r
 | ||||||
| ${bundle.script}\r | ${bundle.script}\r | ||||||
| }`;
 | }`;
 | ||||||
|     const ctx = new ScriptContext(bundle.allNotes, apiParams); |     const ctx = new ScriptContext(bundle.allNotes || [], apiParams); | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|         return execute(ctx, script); |         return execute(ctx, script); | ||||||
|     } |     } | ||||||
|     catch (e) { |     catch (e: any) { | ||||||
|         log.error(`Execution of script "${bundle.note.title}" (${bundle.note.noteId}) failed with error: ${e.message}`); |         log.error(`Execution of script "${bundle.note?.title}" (${bundle.note?.noteId}) failed with error: ${e.message}`); | ||||||
| 
 | 
 | ||||||
|         throw e; |         throw e; | ||||||
|     } |     } | ||||||
| @@ -61,25 +77,36 @@ ${bundle.script}\r | |||||||
|  * This method preserves frontend startNode - that's why we start execution from currentNote and override |  * This method preserves frontend startNode - that's why we start execution from currentNote and override | ||||||
|  * bundle's startNote. |  * bundle's startNote. | ||||||
|  */ |  */ | ||||||
| function executeScript(script, params, startNoteId, currentNoteId, originEntityName, originEntityId) { | function executeScript(script: string, params: ScriptParams, startNoteId: string, currentNoteId: string, originEntityName: string, originEntityId: string) { | ||||||
|     const startNote = becca.getNote(startNoteId); |     const startNote = becca.getNote(startNoteId); | ||||||
|     const currentNote = becca.getNote(currentNoteId); |     const currentNote = becca.getNote(currentNoteId); | ||||||
|     const originEntity = becca.getEntity(originEntityName, originEntityId); |     const originEntity = becca.getEntity(originEntityName, originEntityId); | ||||||
| 
 | 
 | ||||||
|  |     if (!currentNote) { | ||||||
|  |         throw new Error("Cannot find note."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // we're just executing an excerpt of the original frontend script in the backend context, so we must
 |     // we're just executing an excerpt of the original frontend script in the backend context, so we must
 | ||||||
|     // override normal note's content, and it's mime type / script environment
 |     // override normal note's content, and it's mime type / script environment
 | ||||||
|     const overrideContent = `return (${script}\r\n)(${getParams(params)})`; |     const overrideContent = `return (${script}\r\n)(${getParams(params)})`; | ||||||
| 
 | 
 | ||||||
|     const bundle = getScriptBundle(currentNote, true, 'backend', [], overrideContent); |     const bundle = getScriptBundle(currentNote, true, 'backend', [], overrideContent); | ||||||
|  |     if (!bundle) { | ||||||
|  |         throw new Error("Unable to determine script bundle."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!startNote || !originEntity) { | ||||||
|  |         throw new Error("Missing start note or origin entity."); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return executeBundle(bundle, { startNote, originEntity }); |     return executeBundle(bundle, { startNote, originEntity }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function execute(ctx, script) { | function execute(ctx: any, script: string) { | ||||||
|     return function () { return eval(`const apiContext = this;\r\n(${script}\r\n)()`); }.call(ctx); |     return function () { return eval(`const apiContext = this;\r\n(${script}\r\n)()`); }.call(ctx); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getParams(params) { | function getParams(params: ScriptParams) { | ||||||
|     if (!params) { |     if (!params) { | ||||||
|         return params; |         return params; | ||||||
|     } |     } | ||||||
| @@ -94,12 +121,7 @@ function getParams(params) { | |||||||
|     }).join(","); |     }).join(","); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | function getScriptBundleForFrontend(note: BNote, script: string, params: ScriptParams) { | ||||||
|  * @param {BNote} note |  | ||||||
|  * @param {string} [script] |  | ||||||
|  * @param {Array} [params] |  | ||||||
|  */ |  | ||||||
| function getScriptBundleForFrontend(note, script, params) { |  | ||||||
|     let overrideContent = null; |     let overrideContent = null; | ||||||
| 
 | 
 | ||||||
|     if (script) { |     if (script) { | ||||||
| @@ -113,23 +135,16 @@ function getScriptBundleForFrontend(note, script, params) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // for frontend, we return just noteIds because frontend needs to use its own entity instances
 |     // for frontend, we return just noteIds because frontend needs to use its own entity instances
 | ||||||
|     bundle.noteId = bundle.note.noteId; |     bundle.noteId = bundle.note?.noteId; | ||||||
|     delete bundle.note; |     delete bundle.note; | ||||||
| 
 | 
 | ||||||
|     bundle.allNoteIds = bundle.allNotes.map(note => note.noteId); |     bundle.allNoteIds = bundle.allNotes?.map(note => note.noteId); | ||||||
|     delete bundle.allNotes; |     delete bundle.allNotes; | ||||||
| 
 | 
 | ||||||
|     return bundle; |     return bundle; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | function getScriptBundle(note: BNote, root: boolean = true, scriptEnv: string | null = null, includedNoteIds: string[] = [], overrideContent: string | null = null): Bundle | undefined { | ||||||
|  * @param {BNote} note |  | ||||||
|  * @param {boolean} [root=true] |  | ||||||
|  * @param {string|null} [scriptEnv] |  | ||||||
|  * @param {string[]} [includedNoteIds] |  | ||||||
|  * @param {string|null} [overrideContent] |  | ||||||
|  */ |  | ||||||
| function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = [], overrideContent = null) { |  | ||||||
|     if (!note.isContentAvailable()) { |     if (!note.isContentAvailable()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -146,7 +161,7 @@ function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const bundle = { |     const bundle: Bundle = { | ||||||
|         note: note, |         note: note, | ||||||
|         script: '', |         script: '', | ||||||
|         html: '', |         html: '', | ||||||
| @@ -165,10 +180,14 @@ function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = | |||||||
|         const childBundle = getScriptBundle(child, false, scriptEnv, includedNoteIds); |         const childBundle = getScriptBundle(child, false, scriptEnv, includedNoteIds); | ||||||
| 
 | 
 | ||||||
|         if (childBundle) { |         if (childBundle) { | ||||||
|             modules.push(childBundle.note); |             if (childBundle.note) { | ||||||
|  |                 modules.push(childBundle.note); | ||||||
|  |             } | ||||||
|             bundle.script += childBundle.script; |             bundle.script += childBundle.script; | ||||||
|             bundle.html += childBundle.html; |             bundle.html += childBundle.html; | ||||||
|             bundle.allNotes = bundle.allNotes.concat(childBundle.allNotes); |             if (bundle.allNotes && childBundle.allNotes) { | ||||||
|  |                 bundle.allNotes = bundle.allNotes.concat(childBundle.allNotes); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -196,11 +215,11 @@ return module.exports; | |||||||
|     return bundle; |     return bundle; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function sanitizeVariableName(str) { | function sanitizeVariableName(str: string) { | ||||||
|     return str.replace(/[^a-z0-9_]/gim, ""); |     return str.replace(/[^a-z0-9_]/gim, ""); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     executeNote, |     executeNote, | ||||||
|     executeNoteNoException, |     executeNoteNoException, | ||||||
|     executeScript, |     executeScript, | ||||||
| @@ -78,7 +78,7 @@ function searchFromRelation(note: BNote, relationName: string) { | |||||||
|         return []; |         return []; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const scriptService = require('../../script.js'); // to avoid circular dependency |     const scriptService = require('../../script'); // TODO: to avoid circular dependency | ||||||
|     const result = scriptService.executeNote(scriptNote, {originEntity: note}); |     const result = scriptService.executeNote(scriptNote, {originEntity: note}); | ||||||
|  |  | ||||||
|     if (!Array.isArray(result)) { |     if (!Array.isArray(result)) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user