mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 04:51:31 +08:00 
			
		
		
		
	Merge pull request #41 from TriliumNext/feature/typescript_backend_5
Convert backend to TypeScript (64% -> 67%)
This commit is contained in:
		
						commit
						4c69384b5d
					
				| @ -7,7 +7,7 @@ const compression = require('compression'); | |||||||
| const sessionParser = require('./routes/session_parser.js'); | const sessionParser = require('./routes/session_parser.js'); | ||||||
| const utils = require('./services/utils'); | const utils = require('./services/utils'); | ||||||
| 
 | 
 | ||||||
| require('./services/handlers.js'); | require('./services/handlers'); | ||||||
| require('./becca/becca_loader'); | require('./becca/becca_loader'); | ||||||
| 
 | 
 | ||||||
| const app = express(); | const app = express(); | ||||||
| @ -51,7 +51,7 @@ require('./services/backup'); | |||||||
| // trigger consistency checks timer
 | // trigger consistency checks timer
 | ||||||
| require('./services/consistency_checks'); | require('./services/consistency_checks'); | ||||||
| 
 | 
 | ||||||
| require('./services/scheduler.js'); | require('./services/scheduler'); | ||||||
| 
 | 
 | ||||||
| if (utils.isElectron()) { | if (utils.isElectron()) { | ||||||
|     require('@electron/remote/main').initialize(); |     require('@electron/remote/main').initialize(); | ||||||
|  | |||||||
| @ -668,7 +668,7 @@ class BNote extends AbstractBeccaEntity<BNote> { | |||||||
|      * @param name - relation name to filter |      * @param name - relation name to filter | ||||||
|      * @returns all note's relations (attributes with type relation), excluding inherited ones |      * @returns all note's relations (attributes with type relation), excluding inherited ones | ||||||
|      */ |      */ | ||||||
|     getOwnedRelations(name: string): BAttribute[] { |     getOwnedRelations(name?: string | null): BAttribute[] { | ||||||
|         return this.getOwnedAttributes(RELATION, name); |         return this.getOwnedAttributes(RELATION, name); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -1407,7 +1407,7 @@ class BNote extends AbstractBeccaEntity<BNote> { | |||||||
|      * @param name - relation name |      * @param name - relation name | ||||||
|      * @param value - relation value (noteId) |      * @param value - relation value (noteId) | ||||||
|      */ |      */ | ||||||
|     setRelation(name: string, value: string) { |     setRelation(name: string, value?: string) { | ||||||
|         return this.setAttribute(RELATION, name, value); |         return this.setAttribute(RELATION, name, value); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -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'); | ||||||
|  | |||||||
| @ -1,27 +1,39 @@ | |||||||
| const log = require('./log'); | import log = require('./log'); | ||||||
| const noteService = require('./notes'); | import noteService = require('./notes'); | ||||||
| const sql = require('./sql'); | import sql = require('./sql'); | ||||||
| const utils = require('./utils'); | import utils = require('./utils'); | ||||||
| const attributeService = require('./attributes'); | import attributeService = require('./attributes'); | ||||||
| const dateNoteService = require('./date_notes'); | import dateNoteService = require('./date_notes'); | ||||||
| const treeService = require('./tree'); | import treeService = require('./tree'); | ||||||
| const config = require('./config'); | import config = require('./config'); | ||||||
| const axios = require('axios'); | import axios = require('axios'); | ||||||
| const dayjs = require('dayjs'); | import dayjs = require('dayjs'); | ||||||
| const xml2js = require('xml2js'); | import xml2js = require('xml2js'); | ||||||
| const cloningService = require('./cloning'); | import cloningService = require('./cloning'); | ||||||
| const appInfo = require('./app_info'); | import appInfo = require('./app_info'); | ||||||
| const searchService = require('./search/services/search'); | import searchService = require('./search/services/search'); | ||||||
| const SearchContext = require('./search/search_context'); | import SearchContext = require('./search/search_context'); | ||||||
| const becca = require('../becca/becca'); | import becca = require('../becca/becca'); | ||||||
| const ws = require('./ws'); | import ws = require('./ws'); | ||||||
| const SpacedUpdate = require('./spaced_update'); | import SpacedUpdate = require('./spaced_update'); | ||||||
| const specialNotesService = require('./special_notes'); | import specialNotesService = require('./special_notes'); | ||||||
| const branchService = require('./branches'); | import branchService = require('./branches'); | ||||||
| const exportService = require('./export/zip'); | import exportService = require('./export/zip'); | ||||||
| const syncMutex = require('./sync_mutex'); | import syncMutex = require('./sync_mutex'); | ||||||
| const backupService = require('./backup'); | import backupService = require('./backup'); | ||||||
| const optionsService = require('./options'); | import optionsService = require('./options'); | ||||||
|  | import BNote = require('../becca/entities/bnote'); | ||||||
|  | import AbstractBeccaEntity = require('../becca/entities/abstract_becca_entity'); | ||||||
|  | import BBranch = require('../becca/entities/bbranch'); | ||||||
|  | import BAttribute = require('../becca/entities/battribute'); | ||||||
|  | import BAttachment = require('../becca/entities/battachment'); | ||||||
|  | import BRevision = require('../becca/entities/brevision'); | ||||||
|  | import BEtapiToken = require('../becca/entities/betapi_token'); | ||||||
|  | import BOption = require('../becca/entities/boption'); | ||||||
|  | import { AttributeRow, AttributeType, NoteType } from '../becca/entities/rows'; | ||||||
|  | import Becca from '../becca/becca-interface'; | ||||||
|  | import { NoteParams } from './note-interface'; | ||||||
|  | import { ApiParams } from './backend_script_api_interface'; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -35,139 +47,372 @@ const optionsService = require('./options'); | |||||||
|  * @var {BackendScriptApi} api |  * @var {BackendScriptApi} api | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| /** | interface SearchParams { | ||||||
|  * <p>This is the main backend API interface for scripts. All the properties and methods are published in the "api" object |     includeArchivedNotes?: boolean; | ||||||
|  * available in the JS backend notes. You can use e.g. <code>api.log(api.startNote.title);</code></p> |     ignoreHoistedNote?: boolean; | ||||||
|  * | } | ||||||
|  * @constructor | 
 | ||||||
|  */ | interface NoteAndBranch { | ||||||
| function BackendScriptApi(currentNote, apiParams) { |     note: BNote; | ||||||
|  |     /** object having "note" and "branch" keys representing respective objects */ | ||||||
|  |     branch: BBranch; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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. | ||||||
|      * @type {BNote} |  | ||||||
|      */ |      */ | ||||||
|     this.startNote = apiParams.startNote; |     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 | ||||||
|      * notes, the script starts in "startNote", but then through function calls may jump into another note (currentNote). |      * notes, the script starts in "startNote", but then through function calls may jump into another note (currentNote). | ||||||
|      * A similar concept in C would be __FILE__ |      * A similar concept in C would be __FILE__ | ||||||
|      * Don't mix this up with the concept of active note. |      * Don't mix this up with the concept of active note. | ||||||
|      * @type {BNote} |  | ||||||
|      */ |      */ | ||||||
|     this.currentNote = currentNote; |     currentNote: BNote; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Entity whose event triggered this execution |      * Entity whose event triggered this execution | ||||||
|      * @type {AbstractBeccaEntity} |  | ||||||
|      */ |      */ | ||||||
|     this.originEntity = apiParams.originEntity; |     originEntity?: AbstractBeccaEntity<any>; | ||||||
| 
 |  | ||||||
|     for (const key in apiParams) { |  | ||||||
|         this[key] = apiParams[key]; |  | ||||||
|     } |  | ||||||
|      |      | ||||||
|     /** |     /** | ||||||
|      * 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
 | ||||||
|      * @type {axios} |      * @type {axios} | ||||||
|      * @deprecated use native (browser compatible) fetch() instead |      * @deprecated use native (browser compatible) fetch() instead | ||||||
|      */ |      */ | ||||||
|     this.axios = axios; |     axios: typeof axios; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * day.js library for date manipulation. See {@link https://day.js.org} for documentation
 |      * day.js library for date manipulation. See {@link https://day.js.org} for documentation
 | ||||||
|      * @type {dayjs} |  | ||||||
|      */ |      */ | ||||||
|     this.dayjs = dayjs; |     dayjs: typeof dayjs; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * xml2js library for XML parsing. See {@link https://github.com/Leonidas-from-XIV/node-xml2js} for documentation
 |      * xml2js library for XML parsing. See {@link https://github.com/Leonidas-from-XIV/node-xml2js} for documentation
 | ||||||
|      * @type {xml2js} |  | ||||||
|      */ |      */ | ||||||
|     this.xml2js = xml2js; | 
 | ||||||
|  |     xml2js: typeof xml2js; | ||||||
|      |      | ||||||
|     /** |     /** | ||||||
|      * Instance name identifies particular Trilium instance. It can be useful for scripts |      * Instance name identifies particular Trilium instance. It can be useful for scripts | ||||||
|      * if some action needs to happen on only one specific instance. |      * if some action needs to happen on only one specific instance. | ||||||
|      * |  | ||||||
|      * @returns {string|null} |  | ||||||
|      */ |      */ | ||||||
|     this.getInstanceName = () => config.General ? config.General.instanceName : null; |     getInstanceName(): string | null; | ||||||
|  |      | ||||||
|  |     getNote(noteId: string): BNote | null; | ||||||
|  |     getBranch(branchId: string): BBranch | null; | ||||||
|  |     getAttribute(attachmentId: string): BAttribute | null; | ||||||
|  |     getAttachment(attachmentId: string): BAttachment | null; | ||||||
|  |     getRevision(revisionId: string): BRevision | null; | ||||||
|  |     getEtapiToken(etapiTokenId: string): BEtapiToken | null; | ||||||
|  |     getEtapiTokens(): BEtapiToken[]; | ||||||
|  |     getOption(optionName: string): BOption | null; | ||||||
|  |     getOptions(): BOption[]; | ||||||
|  |     getAttribute(attributeId: string): BAttribute | null; | ||||||
|      |      | ||||||
|     /** |     /** | ||||||
|      * @method |      * This is a powerful search method - you can search by attributes and their values, e.g.: | ||||||
|      * @param {string} noteId |      * "#dateModified =* MONTH AND #log". See {@link https://github.com/zadam/trilium/wiki/Search} for full documentation for all options
 | ||||||
|      * @returns {BNote|null} |  | ||||||
|      */ |      */ | ||||||
|     this.getNote = noteId => becca.getNote(noteId); |     searchForNotes(query: string, searchParams: SearchParams): BNote[]; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} branchId |  | ||||||
|      * @returns {BBranch|null} |  | ||||||
|      */ |  | ||||||
|     this.getBranch = branchId => becca.getBranch(branchId); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} attributeId |  | ||||||
|      * @returns {BAttribute|null} |  | ||||||
|      */ |  | ||||||
|     this.getAttribute = attributeId => becca.getAttribute(attributeId); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} attachmentId |  | ||||||
|      * @returns {BAttachment|null} |  | ||||||
|      */ |  | ||||||
|     this.getAttachment = attachmentId => becca.getAttachment(attachmentId); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} revisionId |  | ||||||
|      * @returns {BRevision|null} |  | ||||||
|      */ |  | ||||||
|     this.getRevision = revisionId => becca.getRevision(revisionId); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} etapiTokenId |  | ||||||
|      * @returns {BEtapiToken|null} |  | ||||||
|      */ |  | ||||||
|     this.getEtapiToken = etapiTokenId => becca.getEtapiToken(etapiTokenId); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @returns {BEtapiToken[]} |  | ||||||
|      */ |  | ||||||
|     this.getEtapiTokens = () => becca.getEtapiTokens(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} optionName |  | ||||||
|      * @returns {BOption|null} |  | ||||||
|      */ |  | ||||||
|     this.getOption = optionName => becca.getOption(optionName); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @returns {BOption[]} |  | ||||||
|      */ |  | ||||||
|     this.getOptions = () => optionsService.getOptions(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} attributeId |  | ||||||
|      * @returns {BAttribute|null} |  | ||||||
|      */ |  | ||||||
|     this.getAttribute = attributeId => becca.getAttribute(attributeId); |  | ||||||
|      |      | ||||||
|     /** |     /** | ||||||
|      * This is a powerful search method - you can search by attributes and their values, e.g.: |      * This is a powerful search method - you can search by attributes and their values, e.g.: | ||||||
|      * "#dateModified =* MONTH AND #log". See {@link https://github.com/zadam/trilium/wiki/Search} for full documentation for all options
 |      * "#dateModified =* MONTH AND #log". See {@link https://github.com/zadam/trilium/wiki/Search} for full documentation for all options
 | ||||||
|      * |      * | ||||||
|      * @method |  | ||||||
|      * @param {string} query |      * @param {string} query | ||||||
|      * @param {Object} [searchParams] |      * @param {Object} [searchParams] | ||||||
|      * @returns {BNote[]} |  | ||||||
|      */ |      */ | ||||||
|  |     searchForNote(query: string, searchParams: SearchParams): BNote | null; | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Retrieves notes with given label name & value | ||||||
|  |      * | ||||||
|  |      * @param  name - attribute name | ||||||
|  |      * @param  value - attribute value | ||||||
|  |      */ | ||||||
|  |     getNotesWithLabel(name: string, value?: string): BNote[]; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Retrieves first note with given label name & value | ||||||
|  |      * | ||||||
|  |      * @param name - attribute name | ||||||
|  |      * @param value - attribute value | ||||||
|  |      */ | ||||||
|  |     getNoteWithLabel(name: string, value?: string): BNote | null; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * If there's no branch between note and parent note, create one. Otherwise, do nothing. Returns the new or existing branch. | ||||||
|  |      * | ||||||
|  |      * @param prefix - if branch is created between note and parent note, set this prefix | ||||||
|  |      */ | ||||||
|  |     ensureNoteIsPresentInParent(noteId: string, parentNoteId: string, prefix: string): { | ||||||
|  |         branch: BBranch | null | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * If there's a branch between note and parent note, remove it. Otherwise, do nothing. | ||||||
|  |      */ | ||||||
|  |     ensureNoteIsAbsentFromParent(noteId: string, parentNoteId: string): void; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Based on the value, either create or remove branch between note and parent note. | ||||||
|  |      * | ||||||
|  |      * @param present - true if we want the branch to exist, false if we want it gone | ||||||
|  |      * @param prefix - if branch is created between note and parent note, set this prefix | ||||||
|  |      */ | ||||||
|  |     toggleNoteInParent(present: true, noteId: string, parentNoteId: string, prefix: string): void; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Create text note. See also createNewNote() for more options. | ||||||
|  |      */ | ||||||
|  |     createTextNote(parentNoteId: string, title: string, content: string): NoteAndBranch; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Create data note - data in this context means object serializable to JSON. Created note will be of type 'code' and | ||||||
|  |      * JSON MIME type. See also createNewNote() for more options. | ||||||
|  |      */ | ||||||
|  |     createDataNote(parentNoteId: string, title: string, content: {}): NoteAndBranch; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @returns object contains newly created entities note and branch | ||||||
|  |      */ | ||||||
|  |     createNewNote(params: NoteParams): NoteAndBranch; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @deprecated please use createTextNote() with similar API for simpler use cases or createNewNote() for more complex needs | ||||||
|  |      * @param parentNoteId - create new note under this parent | ||||||
|  |      * @returns object contains newly created entities note and branch | ||||||
|  |      */ | ||||||
|  |     createNote(parentNoteId: string, title: string, content: string, extraOptions: Omit<NoteParams, "title" | "content" | "type" | "parentNoteId"> & { | ||||||
|  |         /** should the note be JSON */ | ||||||
|  |         json?: boolean; | ||||||
|  |         attributes?: AttributeRow[] | ||||||
|  |     }): NoteAndBranch; | ||||||
|  | 
 | ||||||
|  |     logMessages: Record<string, string[]>; | ||||||
|  |     logSpacedUpdates: Record<string, SpacedUpdate>; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Log given message to trilium logs and log pane in UI | ||||||
|  |      */ | ||||||
|  |     log(message: string): void; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns root note of the calendar. | ||||||
|  |      */ | ||||||
|  |     getRootCalendarNote(): BNote | null; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns day note for given date. If such note doesn't exist, it is created. | ||||||
|  |      * | ||||||
|  |      * @method | ||||||
|  |      * @param date in YYYY-MM-DD format | ||||||
|  |      * @param rootNote - specify calendar root note, normally leave empty to use the default calendar | ||||||
|  |      */ | ||||||
|  |     getDayNote(date: string, rootNote?: BNote): BNote | null; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns today's day note. If such note doesn't exist, it is created. | ||||||
|  |      *  | ||||||
|  |      * @param rootNote specify calendar root note, normally leave empty to use the default calendar | ||||||
|  |      */ | ||||||
|  |     getTodayNote(rootNote?: BNote): BNote | null; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns note for the first date of the week of the given date. | ||||||
|  |      * | ||||||
|  |      * @param date in YYYY-MM-DD format | ||||||
|  |      * @param rootNote - specify calendar root note, normally leave empty to use the default calendar | ||||||
|  |      */ | ||||||
|  |     getWeekNote(date: string, options: { | ||||||
|  |         // TODO: Deduplicate type with date_notes.ts once ES modules are added.
 | ||||||
|  |         /** either "monday" (default) or "sunday" */ | ||||||
|  |         startOfTheWeek: "monday" | "sunday"; | ||||||
|  |     }, rootNote: BNote): BNote | null; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns month note for given date. If such a note doesn't exist, it is created. | ||||||
|  |      * | ||||||
|  |      * @param date in YYYY-MM format | ||||||
|  |      * @param rootNote - specify calendar root note, normally leave empty to use the default calendar | ||||||
|  |      */ | ||||||
|  |     getMonthNote(date: string, rootNote: BNote): BNote | null; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns year note for given year. If such a note doesn't exist, it is created. | ||||||
|  |      * | ||||||
|  |      * @param year in YYYY format | ||||||
|  |      * @param rootNote - specify calendar root note, normally leave empty to use the default calendar | ||||||
|  |      */ | ||||||
|  |     getYearNote(year: string, rootNote?: BNote): BNote | null; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Sort child notes of a given note. | ||||||
|  |      */ | ||||||
|  |     sortNotes(parentNoteId: string, sortConfig: { | ||||||
|  |         /** 'title', 'dateCreated', 'dateModified' or a label name | ||||||
|  |           * See {@link https://github.com/zadam/trilium/wiki/Sorting} for details. */
 | ||||||
|  |         sortBy?: string; | ||||||
|  |         reverse?: boolean; | ||||||
|  |         foldersFirst?: boolean; | ||||||
|  |     }): void; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * This method finds note by its noteId and prefix and either sets it to the given parentNoteId | ||||||
|  |      * or removes the branch (if parentNoteId is not given). | ||||||
|  |      * | ||||||
|  |      * This method looks similar to toggleNoteInParent() but differs because we're looking up branch by prefix. | ||||||
|  |      * | ||||||
|  |      * @deprecated this method is pretty confusing and serves specialized purpose only | ||||||
|  |      */ | ||||||
|  |     setNoteToParent(noteId: string, prefix: string, parentNoteId: string | null): void; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * This functions wraps code which is supposed to be running in transaction. If transaction already | ||||||
|  |      * exists, then we'll use that transaction. | ||||||
|  |      * | ||||||
|  |      * @param func | ||||||
|  |      * @returns result of func callback | ||||||
|  |      */ | ||||||
|  |     transactional(func: () => void): any; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Return randomly generated string of given length. This random string generation is NOT cryptographically secure. | ||||||
|  |      * | ||||||
|  |      * @param length of the string | ||||||
|  |      * @returns random string | ||||||
|  |      */ | ||||||
|  |     randomString(length: number): string; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @param to escape | ||||||
|  |      * @returns escaped string | ||||||
|  |      */ | ||||||
|  |     escapeHtml(string: string): string; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @param string to unescape | ||||||
|  |      * @returns unescaped string | ||||||
|  |      */ | ||||||
|  |     unescapeHtml(string: string): string; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * sql | ||||||
|  |      * @type {module:sql} | ||||||
|  |      */ | ||||||
|  |     sql: any; | ||||||
|  | 
 | ||||||
|  |     getAppInfo(): typeof appInfo; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Creates a new launcher to the launchbar. If the launcher (id) already exists, it will be updated. | ||||||
|  |      */ | ||||||
|  |     createOrUpdateLauncher(opts: { | ||||||
|  |         /** id of the launcher, only alphanumeric at least 6 characters long */ | ||||||
|  |         id: string; | ||||||
|  |         /** one of | ||||||
|  |          * - "note" - activating the launcher will navigate to the target note (specified in targetNoteId param) | ||||||
|  |          * - "script" -  activating the launcher will execute the script (specified in scriptNoteId param) | ||||||
|  |          * - "customWidget" - the launcher will be rendered with a custom widget (specified in widgetNoteId param) | ||||||
|  |          */ | ||||||
|  |         type: "note" | "script" | "customWidget"; | ||||||
|  |         title: string; | ||||||
|  |         /** if true, will be created in the "Visible launchers", otherwise in "Available launchers" */ | ||||||
|  |         isVisible: boolean; | ||||||
|  |         /** name of the boxicon to be used (e.g. "bx-time") */ | ||||||
|  |         icon: string; | ||||||
|  |         /** will activate the target note/script upon pressing, e.g. "ctrl+e" */ | ||||||
|  |         keyboardShortcut: string; | ||||||
|  |         /** for type "note" */ | ||||||
|  |         targetNoteId: string; | ||||||
|  |         /** for type "script" */ | ||||||
|  |         scriptNoteId: string; | ||||||
|  |         /** for type "customWidget" */ | ||||||
|  |         widgetNoteId?: string; | ||||||
|  |     }): { note: BNote }; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @param format - either 'html' or 'markdown' | ||||||
|  |      */ | ||||||
|  |     exportSubtreeToZipFile(noteId: string, format: "markdown" | "html", zipFilePath: string): Promise<void>; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Executes given anonymous function on the frontend(s). | ||||||
|  |      * Internally, this serializes the anonymous function into string and sends it to frontend(s) via WebSocket. | ||||||
|  |      * Note that there can be multiple connected frontend instances (e.g. in different tabs). In such case, all | ||||||
|  |      * instances execute the given function. | ||||||
|  |      * | ||||||
|  |      * @param script - script to be executed on the frontend | ||||||
|  |      * @param params - list of parameters to the anonymous function to be sent to frontend | ||||||
|  |      * @returns no return value is provided. | ||||||
|  |      */ | ||||||
|  |     runOnFrontend(script: () => void | string, params: []): void; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Sync process can make data intermittently inconsistent. Scripts which require strong data consistency | ||||||
|  |      * can use this function to wait for a possible sync process to finish and prevent new sync process from starting | ||||||
|  |      * while it is running. | ||||||
|  |      * | ||||||
|  |      * Because this is an async process, the inner callback doesn't have automatic transaction handling, so in case | ||||||
|  |      * you need to make some DB changes, you need to surround your call with api.transactional(...) | ||||||
|  |      * | ||||||
|  |      * @param callback - function to be executed while sync process is not running | ||||||
|  |      * @returns resolves once the callback is finished (callback is awaited) | ||||||
|  |      */ | ||||||
|  |     runOutsideOfSync(callback: () => void): Promise<void>; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @param backupName - If the backupName is e.g. "now", then the backup will be written to "backup-now.db" file | ||||||
|  |      * @returns resolves once the backup is finished | ||||||
|  |      */ | ||||||
|  |     backupNow(backupName: string): Promise<string>; | ||||||
|  | 
 | ||||||
|  |      /** | ||||||
|  |      * This object contains "at your risk" and "no BC guarantees" objects for advanced use cases. | ||||||
|  |      */ | ||||||
|  |     __private: { | ||||||
|  |         /** provides access to the backend in-memory object graph, see {@link https://github.com/zadam/trilium/blob/master/src/becca/becca.js} */ | ||||||
|  |         becca: Becca; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TODO: Convert to class.
 | ||||||
|  | /** | ||||||
|  |  * <p>This is the main backend API interface for scripts. All the properties and methods are published in the "api" object | ||||||
|  |  * available in the JS backend notes. You can use e.g. <code>api.log(api.startNote.title);</code></p> | ||||||
|  |  * | ||||||
|  |  * @constructor | ||||||
|  |  */ | ||||||
|  | function BackendScriptApi(this: Api, currentNote: BNote, apiParams: ApiParams) { | ||||||
|  |     this.startNote = apiParams.startNote; | ||||||
|  |      | ||||||
|  |     this.currentNote = currentNote; | ||||||
|  |      | ||||||
|  |     this.originEntity = apiParams.originEntity; | ||||||
|  | 
 | ||||||
|  |     for (const key in apiParams) { | ||||||
|  |         (this as any)[key] = apiParams[key as keyof ApiParams]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     this.axios = axios; | ||||||
|  |     this.dayjs = dayjs; | ||||||
|  |     this.xml2js = xml2js; | ||||||
|  |     this.getInstanceName = () => config.General ? config.General.instanceName : null; | ||||||
|  |     this.getNote = noteId => becca.getNote(noteId); | ||||||
|  |     this.getBranch = branchId => becca.getBranch(branchId); | ||||||
|  |     this.getAttribute = attributeId => becca.getAttribute(attributeId); | ||||||
|  |     this.getAttachment = attachmentId => becca.getAttachment(attachmentId); | ||||||
|  |     this.getRevision = revisionId => becca.getRevision(revisionId); | ||||||
|  |     this.getEtapiToken = etapiTokenId => becca.getEtapiToken(etapiTokenId); | ||||||
|  |     this.getEtapiTokens = () => becca.getEtapiTokens(); | ||||||
|  |     this.getOption = optionName => becca.getOption(optionName); | ||||||
|  |     this.getOptions = () => optionsService.getOptions(); | ||||||
|  |     this.getAttribute = attributeId => becca.getAttribute(attributeId); | ||||||
|  |      | ||||||
|     this.searchForNotes = (query, searchParams = {}) => { |     this.searchForNotes = (query, searchParams = {}) => { | ||||||
|         if (searchParams.includeArchivedNotes === undefined) { |         if (searchParams.includeArchivedNotes === undefined) { | ||||||
|             searchParams.includeArchivedNotes = true; |             searchParams.includeArchivedNotes = true; | ||||||
| @ -183,83 +428,18 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|         return becca.getNotes(noteIds); |         return becca.getNotes(noteIds); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     /** |      | ||||||
|      * This is a powerful search method - you can search by attributes and their values, e.g.: |  | ||||||
|      * "#dateModified =* MONTH AND #log". See {@link https://github.com/zadam/trilium/wiki/Search} for full documentation for all options
 |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} query |  | ||||||
|      * @param {Object} [searchParams] |  | ||||||
|      * @returns {BNote|null} |  | ||||||
|      */ |  | ||||||
|     this.searchForNote = (query, searchParams = {}) => { |     this.searchForNote = (query, searchParams = {}) => { | ||||||
|         const notes = this.searchForNotes(query, searchParams); |         const notes = this.searchForNotes(query, searchParams); | ||||||
| 
 | 
 | ||||||
|         return notes.length > 0 ? notes[0] : null; |         return notes.length > 0 ? notes[0] : null; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Retrieves notes with given label name & value |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} name - attribute name |  | ||||||
|      * @param {string} [value] - attribute value |  | ||||||
|      * @returns {BNote[]} |  | ||||||
|      */ |  | ||||||
|     this.getNotesWithLabel = attributeService.getNotesWithLabel;     |     this.getNotesWithLabel = attributeService.getNotesWithLabel;     | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Retrieves first note with given label name & value |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} name - attribute name |  | ||||||
|      * @param {string} [value] - attribute value |  | ||||||
|      * @returns {BNote|null} |  | ||||||
|      */ |  | ||||||
|     this.getNoteWithLabel = attributeService.getNoteWithLabel; |     this.getNoteWithLabel = attributeService.getNoteWithLabel; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * If there's no branch between note and parent note, create one. Otherwise, do nothing. Returns the new or existing branch. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} noteId |  | ||||||
|      * @param {string} parentNoteId |  | ||||||
|      * @param {string} prefix - if branch is created between note and parent note, set this prefix |  | ||||||
|      * @returns {{branch: BBranch|null}} |  | ||||||
|      */ |  | ||||||
|     this.ensureNoteIsPresentInParent = cloningService.ensureNoteIsPresentInParent; |     this.ensureNoteIsPresentInParent = cloningService.ensureNoteIsPresentInParent; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * If there's a branch between note and parent note, remove it. Otherwise, do nothing. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} noteId |  | ||||||
|      * @param {string} parentNoteId |  | ||||||
|      * @returns {void} |  | ||||||
|      */ |  | ||||||
|     this.ensureNoteIsAbsentFromParent = cloningService.ensureNoteIsAbsentFromParent; |     this.ensureNoteIsAbsentFromParent = cloningService.ensureNoteIsAbsentFromParent; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Based on the value, either create or remove branch between note and parent note. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {boolean} present - true if we want the branch to exist, false if we want it gone |  | ||||||
|      * @param {string} noteId |  | ||||||
|      * @param {string} parentNoteId |  | ||||||
|      * @param {string} prefix - if branch is created between note and parent note, set this prefix |  | ||||||
|      * @returns {void} |  | ||||||
|      */ |  | ||||||
|     this.toggleNoteInParent = cloningService.toggleNoteInParent; |     this.toggleNoteInParent = cloningService.toggleNoteInParent; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Create text note. See also createNewNote() for more options. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} parentNoteId |  | ||||||
|      * @param {string} title |  | ||||||
|      * @param {string} content |  | ||||||
|      * @returns {{note: BNote, branch: BBranch}} - object having "note" and "branch" keys representing respective objects |  | ||||||
|      */ |  | ||||||
|     this.createTextNote = (parentNoteId, title, content = '') => noteService.createNewNote({ |     this.createTextNote = (parentNoteId, title, content = '') => noteService.createNewNote({ | ||||||
|         parentNoteId, |         parentNoteId, | ||||||
|         title, |         title, | ||||||
| @ -267,16 +447,6 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|         type: 'text' |         type: 'text' | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Create data note - data in this context means object serializable to JSON. Created note will be of type 'code' and |  | ||||||
|      * JSON MIME type. See also createNewNote() for more options. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} parentNoteId |  | ||||||
|      * @param {string} title |  | ||||||
|      * @param {object} content |  | ||||||
|      * @returns {{note: BNote, branch: BBranch}} object having "note" and "branch" keys representing respective objects |  | ||||||
|      */ |  | ||||||
|     this.createDataNote = (parentNoteId, title, content = {}) => noteService.createNewNote({ |     this.createDataNote = (parentNoteId, title, content = {}) => noteService.createNewNote({ | ||||||
|         parentNoteId, |         parentNoteId, | ||||||
|         title, |         title, | ||||||
| @ -285,52 +455,27 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|         mime: 'application/json' |         mime: 'application/json' | ||||||
|     }); |     }); | ||||||
|      |      | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * |  | ||||||
|      * @param {object} params |  | ||||||
|      * @param {string} params.parentNoteId |  | ||||||
|      * @param {string} params.title |  | ||||||
|      * @param {string|Buffer} params.content |  | ||||||
|      * @param {NoteType} params.type - text, code, file, image, search, book, relationMap, canvas, webView |  | ||||||
|      * @param {string} [params.mime] - value is derived from default mimes for type |  | ||||||
|      * @param {boolean} [params.isProtected=false] |  | ||||||
|      * @param {boolean} [params.isExpanded=false] |  | ||||||
|      * @param {string} [params.prefix=''] |  | ||||||
|      * @param {int} [params.notePosition] - default is last existing notePosition in a parent + 10 |  | ||||||
|      * @returns {{note: BNote, branch: BBranch}} object contains newly created entities note and branch |  | ||||||
|      */ |  | ||||||
|     this.createNewNote = noteService.createNewNote; |     this.createNewNote = noteService.createNewNote; | ||||||
|      |      | ||||||
|     /** |     this.createNote = (parentNoteId, title, content = "", _extraOptions = {}) => { | ||||||
|      * @method |  | ||||||
|      * @deprecated please use createTextNote() with similar API for simpler use cases or createNewNote() for more complex needs |  | ||||||
|      * |  | ||||||
|      * @param {string} parentNoteId - create new note under this parent |  | ||||||
|      * @param {string} title |  | ||||||
|      * @param {string} [content=""] |  | ||||||
|      * @param {object} [extraOptions={}] |  | ||||||
|      * @param {boolean} [extraOptions.json=false] - should the note be JSON |  | ||||||
|      * @param {boolean} [extraOptions.isProtected=false] - should the note be protected |  | ||||||
|      * @param {string} [extraOptions.type='text'] - note type |  | ||||||
|      * @param {string} [extraOptions.mime='text/html'] - MIME type of the note |  | ||||||
|      * @param {object[]} [extraOptions.attributes=[]] - attributes to be created for this note |  | ||||||
|      * @param {AttributeType} extraOptions.attributes.type - attribute type - label, relation etc. |  | ||||||
|      * @param {string} extraOptions.attributes.name - attribute name |  | ||||||
|      * @param {string} [extraOptions.attributes.value] - attribute value |  | ||||||
|      * @returns {{note: BNote, branch: BBranch}} object contains newly created entities note and branch |  | ||||||
|      */ |  | ||||||
|     this.createNote = (parentNoteId, title, content = "", extraOptions = {}) => { |  | ||||||
|         extraOptions.parentNoteId = parentNoteId; |  | ||||||
|         extraOptions.title = title; |  | ||||||
| 
 |  | ||||||
|         const parentNote = becca.getNote(parentNoteId); |         const parentNote = becca.getNote(parentNoteId); | ||||||
|  |         if (!parentNote) { | ||||||
|  |             throw new Error(`Unable to find parent note with ID ${parentNote}.`); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let extraOptions: NoteParams = { | ||||||
|  |             ..._extraOptions, | ||||||
|  |             content: "", | ||||||
|  |             type: "text", | ||||||
|  |             parentNoteId, | ||||||
|  |             title | ||||||
|  |         }; | ||||||
| 
 | 
 | ||||||
|         // code note type can be inherited, otherwise "text" is the default
 |         // code note type can be inherited, otherwise "text" is the default
 | ||||||
|         extraOptions.type = parentNote.type === 'code' ? 'code' : 'text'; |         extraOptions.type = parentNote.type === 'code' ? 'code' : 'text'; | ||||||
|         extraOptions.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html'; |         extraOptions.mime = parentNote.type === 'code' ? parentNote.mime : 'text/html'; | ||||||
| 
 | 
 | ||||||
|         if (extraOptions.json) { |         if (_extraOptions.json) { | ||||||
|             extraOptions.content = JSON.stringify(content || {}, null, '\t'); |             extraOptions.content = JSON.stringify(content || {}, null, '\t'); | ||||||
|             extraOptions.type = 'code'; |             extraOptions.type = 'code'; | ||||||
|             extraOptions.mime = 'application/json'; |             extraOptions.mime = 'application/json'; | ||||||
| @ -342,7 +487,7 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|         return sql.transactional(() => { |         return sql.transactional(() => { | ||||||
|             const { note, branch } = noteService.createNewNote(extraOptions); |             const { note, branch } = noteService.createNewNote(extraOptions); | ||||||
| 
 | 
 | ||||||
|             for (const attr of extraOptions.attributes || []) { |             for (const attr of _extraOptions.attributes || []) { | ||||||
|                 attributeService.createAttribute({ |                 attributeService.createAttribute({ | ||||||
|                     noteId: note.noteId, |                     noteId: note.noteId, | ||||||
|                     type: attr.type, |                     type: attr.type, | ||||||
| @ -359,16 +504,13 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|     this.logMessages = {}; |     this.logMessages = {}; | ||||||
|     this.logSpacedUpdates = {}; |     this.logSpacedUpdates = {}; | ||||||
|      |      | ||||||
|     /** |  | ||||||
|      * Log given message to trilium logs and log pane in UI |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param message |  | ||||||
|      * @returns {void} |  | ||||||
|      */ |  | ||||||
|     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] || []; | ||||||
| @ -387,77 +529,13 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|         this.logSpacedUpdates[noteId].scheduleUpdate(); |         this.logSpacedUpdates[noteId].scheduleUpdate(); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Returns root note of the calendar. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @returns {BNote|null} |  | ||||||
|      */ |  | ||||||
|     this.getRootCalendarNote = dateNoteService.getRootCalendarNote; |     this.getRootCalendarNote = dateNoteService.getRootCalendarNote; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Returns day note for given date. If such note doesn't exist, it is created. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} date in YYYY-MM-DD format |  | ||||||
|      * @param {BNote} [rootNote] - specify calendar root note, normally leave empty to use the default calendar |  | ||||||
|      * @returns {BNote|null} |  | ||||||
|      */ |  | ||||||
|     this.getDayNote = dateNoteService.getDayNote; |     this.getDayNote = dateNoteService.getDayNote; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Returns today's day note. If such note doesn't exist, it is created. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {BNote} [rootNote] - specify calendar root note, normally leave empty to use the default calendar |  | ||||||
|      * @returns {BNote|null} |  | ||||||
|      */ |  | ||||||
|     this.getTodayNote = dateNoteService.getTodayNote; |     this.getTodayNote = dateNoteService.getTodayNote; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Returns note for the first date of the week of the given date. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} date in YYYY-MM-DD format |  | ||||||
|      * @param {object} [options] |  | ||||||
|      * @param {string} [options.startOfTheWeek=monday] - either "monday" (default) or "sunday" |  | ||||||
|      * @param {BNote} [rootNote] - specify calendar root note, normally leave empty to use the default calendar |  | ||||||
|      * @returns {BNote|null} |  | ||||||
|      */ |  | ||||||
|     this.getWeekNote = dateNoteService.getWeekNote; |     this.getWeekNote = dateNoteService.getWeekNote; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Returns month note for given date. If such a note doesn't exist, it is created. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} date in YYYY-MM format |  | ||||||
|      * @param {BNote} [rootNote] - specify calendar root note, normally leave empty to use the default calendar |  | ||||||
|      * @returns {BNote|null} |  | ||||||
|      */ |  | ||||||
|     this.getMonthNote = dateNoteService.getMonthNote; |     this.getMonthNote = dateNoteService.getMonthNote; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Returns year note for given year. If such a note doesn't exist, it is created. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} year in YYYY format |  | ||||||
|      * @param {BNote} [rootNote] - specify calendar root note, normally leave empty to use the default calendar |  | ||||||
|      * @returns {BNote|null} |  | ||||||
|      */ |  | ||||||
|     this.getYearNote = dateNoteService.getYearNote; |     this.getYearNote = dateNoteService.getYearNote; | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Sort child notes of a given note. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {string} parentNoteId - this note's child notes will be sorted |  | ||||||
|      * @param {object} [sortConfig] |  | ||||||
|      * @param {string} [sortConfig.sortBy=title] - 'title', 'dateCreated', 'dateModified' or a label name |  | ||||||
|      *                                See {@link https://github.com/zadam/trilium/wiki/Sorting} for details.
 |  | ||||||
|      * @param {boolean} [sortConfig.reverse=false] |  | ||||||
|      * @param {boolean} [sortConfig.foldersFirst=false] |  | ||||||
|      * @returns {void} |  | ||||||
|      */ |  | ||||||
|     this.sortNotes = (parentNoteId, sortConfig = {}) => treeService.sortNotes( |     this.sortNotes = (parentNoteId, sortConfig = {}) => treeService.sortNotes( | ||||||
|         parentNoteId, |         parentNoteId, | ||||||
|         sortConfig.sortBy || "title", |         sortConfig.sortBy || "title", | ||||||
| @ -465,85 +543,15 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|         !!sortConfig.foldersFirst |         !!sortConfig.foldersFirst | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * This method finds note by its noteId and prefix and either sets it to the given parentNoteId |  | ||||||
|      * or removes the branch (if parentNoteId is not given). |  | ||||||
|      * |  | ||||||
|      * This method looks similar to toggleNoteInParent() but differs because we're looking up branch by prefix. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @deprecated this method is pretty confusing and serves specialized purpose only |  | ||||||
|      * @param {string} noteId |  | ||||||
|      * @param {string} prefix |  | ||||||
|      * @param {string|null} parentNoteId |  | ||||||
|      * @returns {void} |  | ||||||
|      */ |  | ||||||
|     this.setNoteToParent = treeService.setNoteToParent; |     this.setNoteToParent = treeService.setNoteToParent; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This functions wraps code which is supposed to be running in transaction. If transaction already |  | ||||||
|      * exists, then we'll use that transaction. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {function} func |  | ||||||
|      * @returns {any} result of func callback |  | ||||||
|      */ |  | ||||||
|     this.transactional = sql.transactional; |     this.transactional = sql.transactional; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Return randomly generated string of given length. This random string generation is NOT cryptographically secure. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {int} length of the string |  | ||||||
|      * @returns {string} random string |  | ||||||
|      */ |  | ||||||
|     this.randomString = utils.randomString; |     this.randomString = utils.randomString; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} string to escape |  | ||||||
|      * @returns {string} escaped string |  | ||||||
|      */ |  | ||||||
|     this.escapeHtml = utils.escapeHtml; |     this.escapeHtml = utils.escapeHtml; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} string to unescape |  | ||||||
|      * @returns {string} unescaped string |  | ||||||
|      */ |  | ||||||
|     this.unescapeHtml = utils.unescapeHtml; |     this.unescapeHtml = utils.unescapeHtml; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * sql |  | ||||||
|      * @type {module:sql} |  | ||||||
|      */ |  | ||||||
|     this.sql = sql; |     this.sql = sql; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @returns {{syncVersion, appVersion, buildRevision, dbVersion, dataDirectory, buildDate}|*} - object representing basic info about running Trilium version |  | ||||||
|      */ |  | ||||||
|     this.getAppInfo = () => appInfo; |     this.getAppInfo = () => appInfo; | ||||||
| 
 | 
 | ||||||
|     /** |      | ||||||
|      * Creates a new launcher to the launchbar. If the launcher (id) already exists, it will be updated. |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {object} opts |  | ||||||
|      * @param {string} opts.id - id of the launcher, only alphanumeric at least 6 characters long |  | ||||||
|      * @param {"note" | "script" | "customWidget"} opts.type - one of |  | ||||||
|      *                          * "note" - activating the launcher will navigate to the target note (specified in targetNoteId param) |  | ||||||
|      *                          * "script" -  activating the launcher will execute the script (specified in scriptNoteId param) |  | ||||||
|      *                          * "customWidget" - the launcher will be rendered with a custom widget (specified in widgetNoteId param) |  | ||||||
|      * @param {string} opts.title |  | ||||||
|      * @param {boolean} [opts.isVisible=false] - if true, will be created in the "Visible launchers", otherwise in "Available launchers" |  | ||||||
|      * @param {string} [opts.icon] - name of the boxicon to be used (e.g. "bx-time") |  | ||||||
|      * @param {string} [opts.keyboardShortcut] - will activate the target note/script upon pressing, e.g. "ctrl+e" |  | ||||||
|      * @param {string} [opts.targetNoteId] - for type "note" |  | ||||||
|      * @param {string} [opts.scriptNoteId] - for type "script" |  | ||||||
|      * @param {string} [opts.widgetNoteId] - for type "customWidget" |  | ||||||
|      * @returns {{note: BNote}} |  | ||||||
|      */ |  | ||||||
|     this.createOrUpdateLauncher = opts => { |     this.createOrUpdateLauncher = opts => { | ||||||
|         if (!opts.id) { throw new Error("ID is a mandatory parameter for api.createOrUpdateLauncher(opts)"); } |         if (!opts.id) { throw new Error("ID is a mandatory parameter for api.createOrUpdateLauncher(opts)"); } | ||||||
|         if (!opts.id.match(/[a-z0-9]{6,1000}/i)) { throw new Error(`ID must be an alphanumeric string at least 6 characters long.`); } |         if (!opts.id.match(/[a-z0-9]{6,1000}/i)) { throw new Error(`ID must be an alphanumeric string at least 6 characters long.`); } | ||||||
| @ -603,42 +611,27 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|         return { note: launcherNote }; |         return { note: launcherNote }; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} noteId |  | ||||||
|      * @param {string} format - either 'html' or 'markdown' |  | ||||||
|      * @param {string} zipFilePath |  | ||||||
|      * @returns {Promise<void>} |  | ||||||
|      */ |  | ||||||
|     this.exportSubtreeToZipFile = async (noteId, format, zipFilePath) => await exportService.exportToZipFile(noteId, format, zipFilePath); |     this.exportSubtreeToZipFile = async (noteId, format, zipFilePath) => await exportService.exportToZipFile(noteId, format, zipFilePath); | ||||||
| 
 | 
 | ||||||
|     /** |     this.runOnFrontend = async (_script, params = []) => { | ||||||
|      * Executes given anonymous function on the frontend(s). |         let script: string; | ||||||
|      * Internally, this serializes the anonymous function into string and sends it to frontend(s) via WebSocket. |         if (typeof _script === "string") { | ||||||
|      * Note that there can be multiple connected frontend instances (e.g. in different tabs). In such case, all |             script = _script; | ||||||
|      * instances execute the given function. |         } else { | ||||||
|      * |             script = _script.toString(); | ||||||
|      * @method |  | ||||||
|      * @param {string} script - script to be executed on the frontend |  | ||||||
|      * @param {Array.<?>} params - list of parameters to the anonymous function to be sent to frontend |  | ||||||
|      * @returns {undefined} - no return value is provided. |  | ||||||
|      */ |  | ||||||
|     this.runOnFrontend = async (script, params = []) => { |  | ||||||
|         if (typeof script === "function") { |  | ||||||
|             script = script.toString(); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         ws.sendMessageToAllClients({ |         ws.sendMessageToAllClients({ | ||||||
|             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: this.originEntity?.noteId || null |             originEntityId: (this.originEntity && "noteId" in this.originEntity && (this.originEntity as BNote)?.noteId) || null | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         function prepareParams(params) { |         function prepareParams(params: any[]) { | ||||||
|             if (!params) { |             if (!params) { | ||||||
|                 return params; |                 return params; | ||||||
|             } |             } | ||||||
| @ -654,35 +647,14 @@ function BackendScriptApi(currentNote, apiParams) { | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
|     /** |  | ||||||
|      * Sync process can make data intermittently inconsistent. Scripts which require strong data consistency |  | ||||||
|      * can use this function to wait for a possible sync process to finish and prevent new sync process from starting |  | ||||||
|      * while it is running. |  | ||||||
|      * |  | ||||||
|      * Because this is an async process, the inner callback doesn't have automatic transaction handling, so in case |  | ||||||
|      * you need to make some DB changes, you need to surround your call with api.transactional(...) |  | ||||||
|      * |  | ||||||
|      * @method |  | ||||||
|      * @param {function} callback - function to be executed while sync process is not running |  | ||||||
|      * @returns {Promise} - resolves once the callback is finished (callback is awaited) |  | ||||||
|      */ |  | ||||||
|     this.runOutsideOfSync = syncMutex.doExclusively; |     this.runOutsideOfSync = syncMutex.doExclusively; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @method |  | ||||||
|      * @param {string} backupName - If the backupName is e.g. "now", then the backup will be written to "backup-now.db" file |  | ||||||
|      * @returns {Promise} - resolves once the backup is finished |  | ||||||
|      */ |  | ||||||
|     this.backupNow = backupService.backupNow; |     this.backupNow = backupService.backupNow; | ||||||
|     |     | ||||||
|     /** |  | ||||||
|      * This object contains "at your risk" and "no BC guarantees" objects for advanced use cases. |  | ||||||
|      * |  | ||||||
|      * @property {Becca} becca - provides access to the backend in-memory object graph, see {@link https://github.com/zadam/trilium/blob/master/src/becca/becca.js}
 |  | ||||||
|      */ |  | ||||||
|     this.__private = { |     this.__private = { | ||||||
|         becca |         becca | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = BackendScriptApi; | export = BackendScriptApi as any as { | ||||||
|  |     new (currentNote: BNote, apiParams: ApiParams): Api | ||||||
|  | }; | ||||||
							
								
								
									
										7
									
								
								src/services/backend_script_api_interface.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/services/backend_script_api_interface.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | import AbstractBeccaEntity = require("../becca/entities/abstract_becca_entity"); | ||||||
|  | import BNote = require("../becca/entities/bnote"); | ||||||
|  | 
 | ||||||
|  | export interface ApiParams { | ||||||
|  |     startNote?: BNote; | ||||||
|  |     originEntity?: AbstractBeccaEntity<any>; | ||||||
|  | } | ||||||
| @ -191,7 +191,7 @@ function getDayNote(dateStr: string, _rootNote: BNote | null = null): BNote { | |||||||
|     return dateNote as unknown as BNote; |     return dateNote as unknown as BNote; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getTodayNote(rootNote = null) { | function getTodayNote(rootNote: BNote | null = null) { | ||||||
|     return getDayNote(dateUtils.localNowDate(), rootNote); |     return getDayNote(dateUtils.localNowDate(), rootNote); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -216,7 +216,7 @@ interface WeekNoteOpts { | |||||||
|     startOfTheWeek?: StartOfWeek |     startOfTheWeek?: StartOfWeek | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getWeekNote(dateStr: string, options: WeekNoteOpts = {}, rootNote = null) { | function getWeekNote(dateStr: string, options: WeekNoteOpts = {}, rootNote: BNote | null = null) { | ||||||
|     const startOfTheWeek = options.startOfTheWeek || "monday"; |     const startOfTheWeek = options.startOfTheWeek || "monday"; | ||||||
| 
 | 
 | ||||||
|     const dateObj = getStartOfTheWeek(dateUtils.parseLocalDate(dateStr), startOfTheWeek); |     const dateObj = getStartOfTheWeek(dateUtils.parseLocalDate(dateStr), startOfTheWeek); | ||||||
|  | |||||||
| @ -1,13 +1,18 @@ | |||||||
| const eventService = require('./events'); | import eventService = require('./events'); | ||||||
| const scriptService = require('./script.js'); | import scriptService = require('./script'); | ||||||
| const treeService = require('./tree'); | import treeService = require('./tree'); | ||||||
| const noteService = require('./notes'); | import noteService = require('./notes'); | ||||||
| const becca = require('../becca/becca'); | import becca = require('../becca/becca'); | ||||||
| const BAttribute = require('../becca/entities/battribute'); | import BAttribute = require('../becca/entities/battribute'); | ||||||
| const hiddenSubtreeService = require('./hidden_subtree'); | import hiddenSubtreeService = require('./hidden_subtree'); | ||||||
| const oneTimeTimer = require('./one_time_timer'); | import oneTimeTimer = require('./one_time_timer'); | ||||||
|  | import BNote = require('../becca/entities/bnote'); | ||||||
|  | import AbstractBeccaEntity = require('../becca/entities/abstract_becca_entity'); | ||||||
|  | import { DefinitionObject } from './promoted_attribute_definition_interface'; | ||||||
| 
 | 
 | ||||||
| function runAttachedRelations(note, relationName, originEntity) { | type Handler = (definition: DefinitionObject, note: BNote, targetNote: BNote) => void; | ||||||
|  | 
 | ||||||
|  | function runAttachedRelations(note: BNote, relationName: string, originEntity: AbstractBeccaEntity<any>) { | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @ -16,7 +21,7 @@ function runAttachedRelations(note, relationName, originEntity) { | |||||||
|     const notesToRun = new Set( |     const notesToRun = new Set( | ||||||
|         note.getRelations(relationName) |         note.getRelations(relationName) | ||||||
|             .map(relation => relation.getTargetNote()) |             .map(relation => relation.getTargetNote()) | ||||||
|             .filter(note => !!note) |             .filter(note => !!note) as BNote[] | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     for (const noteToRun of notesToRun) { |     for (const noteToRun of notesToRun) { | ||||||
| @ -42,7 +47,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 +63,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 +79,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); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| @ -84,6 +89,9 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) => | |||||||
| 
 | 
 | ||||||
|         if (entity.type === 'relation' && entity.name === 'template') { |         if (entity.type === 'relation' && entity.name === 'template') { | ||||||
|             const note = becca.getNote(entity.noteId); |             const note = becca.getNote(entity.noteId); | ||||||
|  |             if (!note) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             const templateNote = becca.getNote(entity.value); |             const templateNote = becca.getNote(entity.value); | ||||||
| 
 | 
 | ||||||
| @ -94,6 +102,7 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) => | |||||||
|             const content = note.getContent(); |             const content = note.getContent(); | ||||||
| 
 | 
 | ||||||
|             if (["text", "code"].includes(note.type) |             if (["text", "code"].includes(note.type) | ||||||
|  |                 && typeof content === "string" | ||||||
|                 // if the note has already content we're not going to overwrite it with template's one
 |                 // if the note has already content we're not going to overwrite it with template's one
 | ||||||
|                 && (!content || content.trim().length === 0) |                 && (!content || content.trim().length === 0) | ||||||
|                 && templateNote.hasStringContent()) { |                 && templateNote.hasStringContent()) { | ||||||
| @ -138,7 +147,7 @@ eventService.subscribe(eventService.CHILD_NOTE_CREATED, ({ parentNote, childNote | |||||||
|     runAttachedRelations(parentNote, 'runOnChildNoteCreation', childNote); |     runAttachedRelations(parentNote, 'runOnChildNoteCreation', childNote); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| function processInverseRelations(entityName, entity, handler) { | function processInverseRelations(entityName: string, entity: BAttribute, handler: Handler) { | ||||||
|     if (entityName === 'attributes' && entity.type === 'relation') { |     if (entityName === 'attributes' && entity.type === 'relation') { | ||||||
|         const note = entity.getNote(); |         const note = entity.getNote(); | ||||||
|         const relDefinitions = note.getLabels(`relation:${entity.name}`); |         const relDefinitions = note.getLabels(`relation:${entity.name}`); | ||||||
| @ -149,13 +158,15 @@ function processInverseRelations(entityName, entity, handler) { | |||||||
|             if (definition.inverseRelation && definition.inverseRelation.trim()) { |             if (definition.inverseRelation && definition.inverseRelation.trim()) { | ||||||
|                 const targetNote = entity.getTargetNote(); |                 const targetNote = entity.getTargetNote(); | ||||||
| 
 | 
 | ||||||
|                 handler(definition, note, targetNote); |                 if (targetNote) { | ||||||
|  |                     handler(definition, note, targetNote); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function handleSortedAttribute(entity) { | function handleSortedAttribute(entity: BAttribute) { | ||||||
|     treeService.sortNotesIfNeeded(entity.noteId); |     treeService.sortNotesIfNeeded(entity.noteId); | ||||||
| 
 | 
 | ||||||
|     if (entity.isInheritable) { |     if (entity.isInheritable) { | ||||||
| @ -169,7 +180,7 @@ function handleSortedAttribute(entity) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function handleMaybeSortingLabel(entity) { | function handleMaybeSortingLabel(entity: BAttribute) { | ||||||
|     // check if this label is used for sorting, if yes force re-sort
 |     // check if this label is used for sorting, if yes force re-sort
 | ||||||
|     const note = becca.notes[entity.noteId]; |     const note = becca.notes[entity.noteId]; | ||||||
| 
 | 
 | ||||||
| @ -203,7 +214,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, ({ entityName, entity }) => | |||||||
|             new BAttribute({ |             new BAttribute({ | ||||||
|                 noteId: targetNote.noteId, |                 noteId: targetNote.noteId, | ||||||
|                 type: 'relation', |                 type: 'relation', | ||||||
|                 name: definition.inverseRelation, |                 name: definition.inverseRelation || "", | ||||||
|                 value: note.noteId, |                 value: note.noteId, | ||||||
|                 isInheritable: entity.isInheritable |                 isInheritable: entity.isInheritable | ||||||
|             }).save(); |             }).save(); | ||||||
| @ -215,7 +226,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, ({ entityName, entity }) => | |||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| eventService.subscribe(eventService.ENTITY_DELETED, ({ entityName, entity }) => { | eventService.subscribe(eventService.ENTITY_DELETED, ({ entityName, entity }) => { | ||||||
|     processInverseRelations(entityName, entity, (definition, note, targetNote) => { |     processInverseRelations(entityName, entity, (definition: DefinitionObject, note: BNote, targetNote: BNote) => { | ||||||
|         // if one inverse attribute is deleted, then the other should be deleted as well
 |         // if one inverse attribute is deleted, then the other should be deleted as well
 | ||||||
|         const relations = targetNote.getOwnedRelations(definition.inverseRelation); |         const relations = targetNote.getOwnedRelations(definition.inverseRelation); | ||||||
| 
 | 
 | ||||||
| @ -238,6 +249,6 @@ eventService.subscribe(eventService.ENTITY_DELETED, ({ entityName, entity }) => | |||||||
|     } |     } | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| module.exports = { | export = { | ||||||
|     runAttachedRelations |     runAttachedRelations | ||||||
| }; | }; | ||||||
| @ -239,8 +239,8 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo | |||||||
|             noteId: noteId, |             noteId: noteId, | ||||||
|             type: resolveNoteType(noteMeta?.type), |             type: resolveNoteType(noteMeta?.type), | ||||||
|             mime: noteMeta ? noteMeta.mime : 'text/html', |             mime: noteMeta ? noteMeta.mime : 'text/html', | ||||||
|             prefix: noteMeta ? noteMeta.prefix : '', |             prefix: noteMeta?.prefix || '', | ||||||
|             isExpanded: noteMeta ? noteMeta.isExpanded : false, |             isExpanded: !!noteMeta?.isExpanded, | ||||||
|             notePosition: (noteMeta && firstNote) ? noteMeta.notePosition : undefined, |             notePosition: (noteMeta && firstNote) ? noteMeta.notePosition : undefined, | ||||||
|             isProtected: importRootNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), |             isProtected: importRootNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), | ||||||
|         }); |         }); | ||||||
| @ -510,8 +510,8 @@ async function importZip(taskContext: TaskContext, fileBuffer: Buffer, importRoo | |||||||
|                 noteId, |                 noteId, | ||||||
|                 type, |                 type, | ||||||
|                 mime, |                 mime, | ||||||
|                 prefix: noteMeta ? noteMeta.prefix : '', |                 prefix: noteMeta?.prefix || '', | ||||||
|                 isExpanded: noteMeta ? noteMeta.isExpanded : false, |                 isExpanded: !!noteMeta?.isExpanded, | ||||||
|                 // root notePosition should be ignored since it relates to the original document
 |                 // root notePosition should be ignored since it relates to the original document
 | ||||||
|                 // now import root should be placed after existing notes into new parent
 |                 // now import root should be placed after existing notes into new parent
 | ||||||
|                 notePosition: (noteMeta && firstNote) ? noteMeta.notePosition : undefined, |                 notePosition: (noteMeta && firstNote) ? noteMeta.notePosition : undefined, | ||||||
|  | |||||||
| @ -3,10 +3,12 @@ import { NoteType } from "../becca/entities/rows"; | |||||||
| export interface NoteParams { | export interface NoteParams { | ||||||
|     /** optionally can force specific noteId */ |     /** optionally can force specific noteId */ | ||||||
|     noteId?: string; |     noteId?: string; | ||||||
|  |     branchId?: string; | ||||||
|     parentNoteId: string; |     parentNoteId: string; | ||||||
|     templateNoteId?: string; |     templateNoteId?: string; | ||||||
|     title: string; |     title: string; | ||||||
|     content: string; |     content: string; | ||||||
|  |     /** text, code, file, image, search, book, relationMap, canvas, webView */ | ||||||
|     type: NoteType; |     type: NoteType; | ||||||
|     /** default value is derived from default mimes for type */ |     /** default value is derived from default mimes for type */ | ||||||
|     mime?: string; |     mime?: string; | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ import ws = require('./ws'); | |||||||
| import html2plaintext = require('html2plaintext'); | import html2plaintext = require('html2plaintext'); | ||||||
| import { AttachmentRow, AttributeRow, BranchRow, NoteRow, NoteType } from '../becca/entities/rows'; | import { AttachmentRow, AttributeRow, BranchRow, NoteRow, NoteType } from '../becca/entities/rows'; | ||||||
| import TaskContext = require('./task_context'); | import TaskContext = require('./task_context'); | ||||||
|  | import { NoteParams } from './note-interface'; | ||||||
| 
 | 
 | ||||||
| interface FoundLink { | interface FoundLink { | ||||||
|     name: "imageLink" | "internalLink" | "includeNoteLink" | "relationMapLink", |     name: "imageLink" | "internalLink" | "includeNoteLink" | "relationMapLink", | ||||||
| @ -152,31 +153,6 @@ function getAndValidateParent(params: GetValidateParams) { | |||||||
|     return parentNote; |     return parentNote; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface NoteParams { |  | ||||||
|     /** optionally can force specific noteId */ |  | ||||||
|     noteId?: string; |  | ||||||
|     branchId?: string; |  | ||||||
|     parentNoteId: string; |  | ||||||
|     templateNoteId?: string; |  | ||||||
|     title: string; |  | ||||||
|     content: string; |  | ||||||
|     type: NoteType; |  | ||||||
|     /** default value is derived from default mimes for type */ |  | ||||||
|     mime?: string; |  | ||||||
|     /** default is false */ |  | ||||||
|     isProtected?: boolean; |  | ||||||
|     /** default is false */ |  | ||||||
|     isExpanded?: boolean; |  | ||||||
|     /** default is empty string */ |  | ||||||
|     prefix?: string | null; |  | ||||||
|     /** default is the last existing notePosition in a parent + 10 */ |  | ||||||
|     notePosition?: number; |  | ||||||
|     dateCreated?: string; |  | ||||||
|     utcDateCreated?: string; |  | ||||||
|     ignoreForbiddenParents?: boolean; |  | ||||||
|     target?: "into"; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function createNewNote(params: NoteParams): { | function createNewNote(params: NoteParams): { | ||||||
|     note: BNote; |     note: BNote; | ||||||
|     branch: BBranch; |     branch: BBranch; | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								src/services/promoted_attribute_definition_interface.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/services/promoted_attribute_definition_interface.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | export interface DefinitionObject { | ||||||
|  |     isPromoted?: boolean; | ||||||
|  |     labelType?: string; | ||||||
|  |     multiplicity?: string; | ||||||
|  |     numberPrecision?: number; | ||||||
|  |     promotedAlias?: string; | ||||||
|  |     inverseRelation?: string; | ||||||
|  | } | ||||||
| @ -1,11 +1,4 @@ | |||||||
| interface DefinitionObject { | import { DefinitionObject } from "./promoted_attribute_definition_interface"; | ||||||
|     isPromoted?: boolean; |  | ||||||
|     labelType?: string; |  | ||||||
|     multiplicity?: string; |  | ||||||
|     numberPrecision?: number; |  | ||||||
|     promotedAlias?: string; |  | ||||||
|     inverseRelation?: string; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| function parse(value: string): DefinitionObject { | function parse(value: string): DefinitionObject { | ||||||
|     const tokens = value.split(',').map(t => t.trim()); |     const tokens = value.split(',').map(t => t.trim()); | ||||||
|  | |||||||
| @ -1,28 +1,24 @@ | |||||||
| const scriptService = require('./script.js'); | import scriptService = require('./script'); | ||||||
| const cls = require('./cls'); | import cls = require('./cls'); | ||||||
| const sqlInit = require('./sql_init'); | import sqlInit = require('./sql_init'); | ||||||
| const config = require('./config'); | import config = require('./config'); | ||||||
| const log = require('./log'); | import log = require('./log'); | ||||||
| const attributeService = require('../services/attributes'); | import attributeService = require('../services/attributes'); | ||||||
| const protectedSessionService = require('../services/protected_session'); | import protectedSessionService = require('../services/protected_session'); | ||||||
| const hiddenSubtreeService = require('./hidden_subtree'); | import hiddenSubtreeService = require('./hidden_subtree'); | ||||||
|  | import BNote = require('../becca/entities/bnote'); | ||||||
| 
 | 
 | ||||||
| /** | function getRunAtHours(note: BNote): number[] { | ||||||
|  * @param {BNote} note |  | ||||||
|  * @return {int[]} |  | ||||||
|  */ |  | ||||||
| function getRunAtHours(note) { |  | ||||||
|     try { |     try { | ||||||
|         return note.getLabelValues('runAtHour').map(hour => parseInt(hour)); |         return note.getLabelValues('runAtHour').map(hour => parseInt(hour)); | ||||||
|     } |     } catch (e: any) { | ||||||
|     catch (e) { |  | ||||||
|         log.error(`Could not parse runAtHour for note ${note.noteId}: ${e.message}`); |         log.error(`Could not parse runAtHour for note ${note.noteId}: ${e.message}`); | ||||||
| 
 | 
 | ||||||
|         return []; |         return []; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function runNotesWithLabel(runAttrValue) { | function runNotesWithLabel(runAttrValue: string) { | ||||||
|     const instanceName = config.General ? config.General.instanceName : null; |     const instanceName = config.General ? config.General.instanceName : null; | ||||||
|     const currentHours = new Date().getHours(); |     const currentHours = new Date().getHours(); | ||||||
|     const notes = attributeService.getNotesWithLabel('run', runAttrValue); |     const notes = attributeService.getNotesWithLabel('run', runAttrValue); | ||||||
| @ -34,7 +30,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.js'); | 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: ScriptContext, 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, | ||||||
| @ -1,22 +0,0 @@ | |||||||
| const utils = require('./utils'); |  | ||||||
| const BackendScriptApi = require('./backend_script_api.js'); |  | ||||||
| 
 |  | ||||||
| function ScriptContext(allNotes, apiParams = {}) { |  | ||||||
|     this.modules = {}; |  | ||||||
|     this.notes = utils.toObject(allNotes, note => [note.noteId, note]); |  | ||||||
|     this.apis = utils.toObject(allNotes, note => [note.noteId, new BackendScriptApi(note, apiParams)]); |  | ||||||
|     this.require = moduleNoteIds => { |  | ||||||
|         return moduleName => { |  | ||||||
|             const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId)); |  | ||||||
|             const note = candidates.find(c => c.title === moduleName); |  | ||||||
| 
 |  | ||||||
|             if (!note) { |  | ||||||
|                 return require(moduleName); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return this.modules[note.noteId].exports; |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| module.exports = ScriptContext; |  | ||||||
							
								
								
									
										37
									
								
								src/services/script_context.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/services/script_context.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | import utils = require('./utils'); | ||||||
|  | import BackendScriptApi = require('./backend_script_api'); | ||||||
|  | import BNote = require('../becca/entities/bnote'); | ||||||
|  | import { ApiParams } from './backend_script_api_interface'; | ||||||
|  | 
 | ||||||
|  | type Module = { | ||||||
|  |     exports: any[]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class ScriptContext { | ||||||
|  |     modules: Record<string, Module>; | ||||||
|  |     notes: {}; | ||||||
|  |     apis: {}; | ||||||
|  |     allNotes: BNote[]; | ||||||
|  |      | ||||||
|  |     constructor(allNotes: BNote[], apiParams: ApiParams) { | ||||||
|  |         this.allNotes = allNotes; | ||||||
|  |         this.modules = {}; | ||||||
|  |         this.notes = utils.toObject(allNotes, note => [note.noteId, note]); | ||||||
|  |         this.apis = utils.toObject(allNotes, note => [note.noteId, new BackendScriptApi(note, apiParams)]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     require(moduleNoteIds: string[]) { | ||||||
|  |         return (moduleName: string) => { | ||||||
|  |             const candidates = this.allNotes.filter(note => moduleNoteIds.includes(note.noteId)); | ||||||
|  |             const note = candidates.find(c => c.title === moduleName); | ||||||
|  | 
 | ||||||
|  |             if (!note) { | ||||||
|  |                 return require(moduleName); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return this.modules[note.noteId].exports; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export = ScriptContext; | ||||||
| @ -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)) { | ||||||
|  | |||||||
| @ -42,6 +42,15 @@ interface Message { | |||||||
|     message?: string; |     message?: string; | ||||||
|     reason?: string;     |     reason?: string;     | ||||||
|     result?: string; |     result?: string; | ||||||
|  | 
 | ||||||
|  |     script?: string; | ||||||
|  |     params?: any[]; | ||||||
|  |     noteId?: string; | ||||||
|  |     messages?: string[]; | ||||||
|  |     startNoteId?: string; | ||||||
|  |     currentNoteId?: string; | ||||||
|  |     originEntityName?: "notes"; | ||||||
|  |     originEntityId?: string | null; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type SessionParser = (req: IncomingMessage, params: {}, cb: () => void) => void; | type SessionParser = (req: IncomingMessage, params: {}, cb: () => void) => void; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Elian Doran
						Elian Doran