Merge pull request #13 from TriliumNext/feature/typescript_backend

Convert backend to TypeScript (0% -> 19%)
This commit is contained in:
Elian Doran 2024-04-02 22:30:39 +03:00 committed by GitHub
commit adc384a971
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
187 changed files with 2447 additions and 1624 deletions

View File

@ -1,4 +1,5 @@
{
"editor.formatOnSave": true,
"files.eol": "\n"
"files.eol": "\n",
"typescript.tsdk": "node_modules/typescript/lib"
}

7
_check_ts_progress.sh Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
cloc HEAD \
--git --md \
--include-lang=javascript,typescript \
--found=filelist.txt \
--exclude-dir=public,libraries

View File

@ -1,6 +1,6 @@
module.exports = () => {
const sql = require('../../src/services/sql.js');
const utils = require('../../src/services/utils.js');
const sql = require('../../src/services/sql');
const utils = require('../../src/services/utils');
const existingBlobIds = new Set();

View File

@ -1,9 +1,9 @@
module.exports = () => {
const beccaLoader = require('../../src/becca/becca_loader.js');
const becca = require('../../src/becca/becca.js');
const cls = require('../../src/services/cls.js');
const log = require('../../src/services/log.js');
const sql = require('../../src/services/sql.js');
const becca = require('../../src/becca/becca');
const cls = require('../../src/services/cls');
const log = require('../../src/services/log');
const sql = require('../../src/services/sql');
cls.init(() => {
// emergency disabling of image compression since it appears to make problems in migration to 0.61
@ -13,7 +13,7 @@ module.exports = () => {
for (const note of Object.values(becca.notes)) {
try {
const attachment = note.convertToParentAttachment({autoConversion: true});
const attachment = note.convertToParentAttachment({ autoConversion: true });
if (attachment) {
log.info(`Auto-converted note '${note.noteId}' into attachment '${attachment.attachmentId}'.`);

View File

@ -1,7 +1,7 @@
const http = require("http");
const ini = require("ini");
const fs = require("fs");
const dataDir = require('./src/services/data_dir.js');
const dataDir = require('./src/services/data_dir');
const config = ini.parse(fs.readFileSync(dataDir.CONFIG_INI_PATH, 'utf-8'));
if (config.Network.https) {

View File

@ -1,5 +1,5 @@
const crypto = require("crypto");
const sql = require('./sql.js');
const sql = require('./sql');
const decryptService = require('./decrypt.js');
function getDataKey(password) {

View File

@ -74,7 +74,7 @@ function dumpDocument(documentPath, targetPath, options) {
return;
}
let {content} = sql.getRow("SELECT content FROM blobs WHERE blobId = ?", [noteRow.blobId]);
let { content } = sql.getRow("SELECT content FROM blobs WHERE blobId = ?", [noteRow.blobId]);
if (content !== null && noteRow.isProtected && dataKey) {
content = decryptService.decrypt(dataKey, content);
@ -108,7 +108,7 @@ function dumpDocument(documentPath, targetPath, options) {
}
try {
fs.mkdirSync(childTargetPath, {recursive: true});
fs.mkdirSync(childTargetPath, { recursive: true });
}
catch (e) {
console.error(`DUMPERROR: Creating directory ${childTargetPath} failed with error '${e.message}'`);
@ -157,7 +157,7 @@ function validatePaths(documentPath, targetPath) {
}
if (!fs.existsSync(targetPath)) {
const ret = fs.mkdirSync(targetPath, {recursive: true});
const ret = fs.mkdirSync(targetPath, { recursive: true });
if (!ret) {
console.error(`Target path '${targetPath}' could not be created. Run with --help to see usage.`);

View File

@ -2,12 +2,11 @@
"restartable": "rs",
"ignore": [".git", "node_modules/**/node_modules", "src/public/"],
"verbose": false,
"execMap": {
"js": "node --harmony"
},
"exec": "ts-node",
"watch": ["src/"],
"signal": "SIGTERM",
"env": {
"NODE_ENV": "development"
},
"ext": "js,json"
"ext": "ts,js,json"
}

589
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "trilium",
"version": "0.63.3",
"version": "0.63.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "trilium",
"version": "0.63.3",
"version": "0.63.5",
"hasInstallScript": true,
"license": "AGPL-3.0-only",
"dependencies": {
@ -88,6 +88,14 @@
"trilium": "src/www.js"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.9",
"@types/cls-hooked": "^4.3.8",
"@types/escape-html": "^1.0.4",
"@types/express": "^4.17.21",
"@types/ini": "^4.1.0",
"@types/mime-types": "^2.1.4",
"@types/node": "^20.11.19",
"@types/ws": "^8.5.10",
"cross-env": "7.0.3",
"electron": "25.9.8",
"electron-builder": "24.13.3",
@ -99,6 +107,9 @@
"lorem-ipsum": "2.0.8",
"nodemon": "3.1.0",
"rcedit": "4.0.1",
"ts-node": "^10.9.2",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"webpack": "5.90.3",
"webpack-cli": "5.1.4"
},
@ -139,6 +150,28 @@
"resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz",
"integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@develar/schema-utils": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
@ -1105,11 +1138,54 @@
"node": ">= 10"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
"dev": true
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true
},
"node_modules/@tweenjs/tween.js": {
"version": "21.0.0",
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-21.0.0.tgz",
"integrity": "sha512-qVfOiFh0U8ZSkLgA6tf7kj2MciqRbSCWaJZRwftVO7UbtVDNsZAXpWXqvCDtIefvjC83UJB+vHTDOGm5ibXjEA=="
},
"node_modules/@types/better-sqlite3": {
"version": "7.6.9",
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.9.tgz",
"integrity": "sha512-FvktcujPDj9XKMJQWFcl2vVl7OdRIqsSRX9b0acWwTmwLK9CF2eqo/FRcmMLNpugKoX/avA6pb7TorDLmpgTnQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
"dev": true,
"dependencies": {
"@types/connect": "*",
"@types/node": "*"
}
},
"node_modules/@types/cacheable-request": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
@ -1121,6 +1197,24 @@
"@types/responselike": "*"
}
},
"node_modules/@types/cls-hooked": {
"version": "4.3.8",
"resolved": "https://registry.npmjs.org/@types/cls-hooked/-/cls-hooked-4.3.8.tgz",
"integrity": "sha512-tf/7H883gFA6MPlWI15EQtfNZ+oPL0gLKkOlx9UHFrun1fC/FkuyNBpTKq1B5E3T4fbvjId6WifHUdSGsMMuPg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/connect": {
"version": "3.4.38",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/d3-scale": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
@ -1147,6 +1241,12 @@
"@types/ms": "*"
}
},
"node_modules/@types/escape-html": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.4.tgz",
"integrity": "sha512-qZ72SFTgUAZ5a7Tj6kf2SHLetiH5S6f8G5frB2SPQ3EyF02kxdyBFf4Tz4banE3xCgGnKgWLt//a6VuYHKYJTg==",
"dev": true
},
"node_modules/@types/eslint": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz",
@ -1173,6 +1273,30 @@
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
},
"node_modules/@types/express": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"dev": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"node_modules/@types/express-serve-static-core": {
"version": "4.17.43",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz",
"integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==",
"dev": true,
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*",
"@types/send": "*"
}
},
"node_modules/@types/fs-extra": {
"version": "9.0.13",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
@ -1197,6 +1321,18 @@
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
},
"node_modules/@types/http-errors": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
"dev": true
},
"node_modules/@types/ini": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@types/ini/-/ini-4.1.0.tgz",
"integrity": "sha512-mTehMtc+xtnWBBvqizcqYCktKDBH2WChvx1GU3Sfe4PysFDXiNe+1YwtpVX1MDtCa4NQrSPw2+3HmvXHY3gt1w==",
"dev": true
},
"node_modules/@types/json-schema": {
"version": "7.0.9",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
@ -1241,6 +1377,18 @@
"integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==",
"dev": true
},
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
"dev": true
},
"node_modules/@types/mime-types": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz",
"integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==",
"dev": true
},
"node_modules/@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@ -1253,9 +1401,12 @@
"integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g=="
},
"node_modules/@types/node": {
"version": "18.16.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.18.tgz",
"integrity": "sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw=="
"version": "20.11.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz",
"integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/plist": {
"version": "3.0.5",
@ -1268,6 +1419,18 @@
"xmlbuilder": ">=11.0.1"
}
},
"node_modules/@types/qs": {
"version": "6.9.11",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz",
"integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==",
"dev": true
},
"node_modules/@types/range-parser": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
"dev": true
},
"node_modules/@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
@ -1276,6 +1439,27 @@
"@types/node": "*"
}
},
"node_modules/@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"dev": true,
"dependencies": {
"@types/mime": "^1",
"@types/node": "*"
}
},
"node_modules/@types/serve-static": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz",
"integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==",
"dev": true,
"dependencies": {
"@types/http-errors": "*",
"@types/mime": "*",
"@types/node": "*"
}
},
"node_modules/@types/unist": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
@ -1288,6 +1472,15 @@
"dev": true,
"optional": true
},
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/yauzl": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz",
@ -1568,6 +1761,15 @@
"acorn": "^8"
}
},
"node_modules/acorn-walk": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@ -2028,6 +2230,12 @@
"streamx": "^2.15.0"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -3626,6 +3834,12 @@
"safe-buffer": "~5.2.0"
}
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/cross-env": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
@ -5449,6 +5663,14 @@
"node": ">=8.0.0"
}
},
"node_modules/electron/node_modules/@types/node": {
"version": "18.19.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.17.tgz",
"integrity": "sha512-SzyGKgwPzuWp2SHhlpXKzCX0pIOfcI4V2eF37nNBJOhwlegQ83omtVQ1XxZpDE06V/d6AQvfQdPfnw0tRC//Ng==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/elkjs": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.2.tgz",
@ -8140,6 +8362,12 @@
"node": ">= 6"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/make-fetch-happen": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.0.tgz",
@ -11925,10 +12153,62 @@
"node": ">=6.10"
}
},
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/ts-node/node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/tslib": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA=="
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/tsscmp": {
"version": "1.0.6",
@ -12041,6 +12321,11 @@
"integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==",
"dev": true
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/unescape": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unescape/-/unescape-1.0.1.tgz",
@ -12217,6 +12502,12 @@
"node": ">=8"
}
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true
},
"node_modules/validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@ -12782,6 +13073,15 @@
"node": ">=12"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/zip-stream": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.0.tgz",
@ -12888,6 +13188,27 @@
"resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz",
"integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="
},
"@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"requires": {
"@jridgewell/trace-mapping": "0.3.9"
},
"dependencies": {
"@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"requires": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
}
}
},
"@develar/schema-utils": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
@ -13608,11 +13929,54 @@
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
"dev": true
},
"@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
"dev": true
},
"@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true
},
"@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true
},
"@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true
},
"@tweenjs/tween.js": {
"version": "21.0.0",
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-21.0.0.tgz",
"integrity": "sha512-qVfOiFh0U8ZSkLgA6tf7kj2MciqRbSCWaJZRwftVO7UbtVDNsZAXpWXqvCDtIefvjC83UJB+vHTDOGm5ibXjEA=="
},
"@types/better-sqlite3": {
"version": "7.6.9",
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.9.tgz",
"integrity": "sha512-FvktcujPDj9XKMJQWFcl2vVl7OdRIqsSRX9b0acWwTmwLK9CF2eqo/FRcmMLNpugKoX/avA6pb7TorDLmpgTnQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/cacheable-request": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
@ -13624,6 +13988,24 @@
"@types/responselike": "*"
}
},
"@types/cls-hooked": {
"version": "4.3.8",
"resolved": "https://registry.npmjs.org/@types/cls-hooked/-/cls-hooked-4.3.8.tgz",
"integrity": "sha512-tf/7H883gFA6MPlWI15EQtfNZ+oPL0gLKkOlx9UHFrun1fC/FkuyNBpTKq1B5E3T4fbvjId6WifHUdSGsMMuPg==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/connect": {
"version": "3.4.38",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/d3-scale": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
@ -13650,6 +14032,12 @@
"@types/ms": "*"
}
},
"@types/escape-html": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.4.tgz",
"integrity": "sha512-qZ72SFTgUAZ5a7Tj6kf2SHLetiH5S6f8G5frB2SPQ3EyF02kxdyBFf4Tz4banE3xCgGnKgWLt//a6VuYHKYJTg==",
"dev": true
},
"@types/eslint": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz",
@ -13676,6 +14064,30 @@
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
},
"@types/express": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"dev": true,
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.17.43",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz",
"integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*",
"@types/send": "*"
}
},
"@types/fs-extra": {
"version": "9.0.13",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
@ -13700,6 +14112,18 @@
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
},
"@types/http-errors": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
"dev": true
},
"@types/ini": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@types/ini/-/ini-4.1.0.tgz",
"integrity": "sha512-mTehMtc+xtnWBBvqizcqYCktKDBH2WChvx1GU3Sfe4PysFDXiNe+1YwtpVX1MDtCa4NQrSPw2+3HmvXHY3gt1w==",
"dev": true
},
"@types/json-schema": {
"version": "7.0.9",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
@ -13744,6 +14168,18 @@
"integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==",
"dev": true
},
"@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
"dev": true
},
"@types/mime-types": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz",
"integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==",
"dev": true
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@ -13756,9 +14192,12 @@
"integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g=="
},
"@types/node": {
"version": "18.16.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.18.tgz",
"integrity": "sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw=="
"version": "20.11.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz",
"integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==",
"requires": {
"undici-types": "~5.26.4"
}
},
"@types/plist": {
"version": "3.0.5",
@ -13771,6 +14210,18 @@
"xmlbuilder": ">=11.0.1"
}
},
"@types/qs": {
"version": "6.9.11",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz",
"integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==",
"dev": true
},
"@types/range-parser": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
"dev": true
},
"@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
@ -13779,6 +14230,27 @@
"@types/node": "*"
}
},
"@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"dev": true,
"requires": {
"@types/mime": "^1",
"@types/node": "*"
}
},
"@types/serve-static": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz",
"integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==",
"dev": true,
"requires": {
"@types/http-errors": "*",
"@types/mime": "*",
"@types/node": "*"
}
},
"@types/unist": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
@ -13791,6 +14263,15 @@
"dev": true,
"optional": true
},
"@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/yauzl": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz",
@ -14031,6 +14512,12 @@
"dev": true,
"requires": {}
},
"acorn-walk": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
"dev": true
},
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@ -14381,6 +14868,12 @@
}
}
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -15586,6 +16079,12 @@
}
}
},
"create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"cross-env": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
@ -16410,6 +16909,16 @@
"@electron/get": "^2.0.0",
"@types/node": "^18.11.18",
"extract-zip": "^2.0.1"
},
"dependencies": {
"@types/node": {
"version": "18.19.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.17.tgz",
"integrity": "sha512-SzyGKgwPzuWp2SHhlpXKzCX0pIOfcI4V2eF37nNBJOhwlegQ83omtVQ1XxZpDE06V/d6AQvfQdPfnw0tRC//Ng==",
"requires": {
"undici-types": "~5.26.4"
}
}
}
},
"electron-builder": {
@ -19033,6 +19542,12 @@
}
}
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"make-fetch-happen": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.0.tgz",
@ -21840,10 +22355,39 @@
"resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
"integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="
},
"ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"requires": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"dependencies": {
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
}
}
},
"tslib": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA=="
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"tsscmp": {
"version": "1.0.6",
@ -21931,6 +22475,11 @@
"integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==",
"dev": true
},
"undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"unescape": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unescape/-/unescape-1.0.1.tgz",
@ -22057,6 +22606,12 @@
"sade": "^1.7.3"
}
},
"v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@ -22463,6 +23018,12 @@
"pend": "~1.2.0"
}
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true
},
"zip-stream": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.0.tgz",

View File

@ -109,6 +109,14 @@
"yauzl": "3.1.2"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.9",
"@types/cls-hooked": "^4.3.8",
"@types/escape-html": "^1.0.4",
"@types/express": "^4.17.21",
"@types/ini": "^4.1.0",
"@types/mime-types": "^2.1.4",
"@types/node": "^20.11.19",
"@types/ws": "^8.5.10",
"cross-env": "7.0.3",
"electron": "25.9.8",
"electron-builder": "24.13.3",
@ -120,6 +128,9 @@
"lorem-ipsum": "2.0.8",
"nodemon": "3.1.0",
"rcedit": "4.0.1",
"ts-node": "^10.9.2",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"webpack": "5.90.3",
"webpack-cli": "5.1.4"
},

View File

@ -2,7 +2,7 @@ const searchService = require('../../src/services/search/services/search.js');
const BNote = require('../../src/becca/entities/bnote.js');
const BBranch = require('../../src/becca/entities/bbranch.js');
const SearchContext = require('../../src/services/search/search_context.js');
const dateUtils = require('../../src/services/date_utils.js');
const dateUtils = require('../../src/services/date_utils');
const becca = require('../../src/becca/becca.js');
const {NoteBuilder, findNoteByTitle, note} = require('./becca_mocking.js');

View File

@ -5,7 +5,7 @@ const cookieParser = require('cookie-parser');
const helmet = require('helmet');
const compression = require('compression');
const sessionParser = require('./routes/session_parser.js');
const utils = require('./services/utils.js');
const utils = require('./services/utils');
require('./services/handlers.js');
require('./becca/becca_loader.js');

View File

@ -1,32 +1,52 @@
"use strict";
import sql = require('../services/sql');
import NoteSet = require('../services/search/note_set');
import NotFoundError = require('../errors/not_found_error');
import BOption = require('./entities/boption');
import BNote = require('./entities/bnote');
import BEtapiToken = require('./entities/betapi_token');
import BAttribute = require('./entities/battribute');
import BBranch = require('./entities/bbranch');
import BRevision = require('./entities/brevision');
import BAttachment = require('./entities/battachment');
import { AttachmentRow, RevisionRow } from './entities/rows';
import BBlob = require('./entities/bblob');
import BRecentNote = require('./entities/brecent_note');
import AbstractBeccaEntity = require('./entities/abstract_becca_entity');
const sql = require('../services/sql.js');
const NoteSet = require('../services/search/note_set.js');
const NotFoundError = require('../errors/not_found_error.js');
interface AttachmentOpts {
includeContentLength?: boolean;
}
/**
* Becca is a backend cache of all notes, branches, and attributes.
* There's a similar frontend cache Froca, and share cache Shaca.
*/
class Becca {
loaded!: boolean;
notes!: Record<string, BNote>;
branches!: Record<string, BBranch>;
childParentToBranch!: Record<string, BBranch>;
attributes!: Record<string, BAttribute>;
/** Points from attribute type-name to list of attributes */
attributeIndex!: Record<string, BAttribute[]>;
options!: Record<string, BOption>;
etapiTokens!: Record<string, BEtapiToken>;
allNoteSetCache: NoteSet | null;
constructor() {
this.reset();
this.allNoteSetCache = null;
}
reset() {
/** @type {Object.<String, BNote>} */
this.notes = {};
/** @type {Object.<String, BBranch>} */
this.branches = {};
/** @type {Object.<String, BBranch>} */
this.childParentToBranch = {};
/** @type {Object.<String, BAttribute>} */
this.attributes = {};
/** @type {Object.<String, BAttribute[]>} Points from attribute type-name to list of attributes */
this.attributeIndex = {};
/** @type {Object.<String, BOption>} */
this.options = {};
/** @type {Object.<String, BEtapiToken>} */
this.etapiTokens = {};
this.dirtyNoteSetCache();
@ -38,8 +58,7 @@ class Becca {
return this.getNote('root');
}
/** @returns {BAttribute[]} */
findAttributes(type, name) {
findAttributes(type: string, name: string): BAttribute[] {
name = name.trim().toLowerCase();
if (name.startsWith('#') || name.startsWith('~')) {
@ -49,9 +68,8 @@ class Becca {
return this.attributeIndex[`${type}-${name}`] || [];
}
/** @returns {BAttribute[]} */
findAttributesWithPrefix(type, name) {
const resArr = [];
findAttributesWithPrefix(type: string, name: string): BAttribute[] {
const resArr: BAttribute[][] = [];
const key = `${type}-${name}`;
for (const idx in this.attributeIndex) {
@ -69,18 +87,16 @@ class Becca {
}
}
addNote(noteId, note) {
addNote(noteId: string, note: BNote) {
this.notes[noteId] = note;
this.dirtyNoteSetCache();
}
/** @returns {BNote|null} */
getNote(noteId) {
getNote(noteId: string): BNote | null {
return this.notes[noteId];
}
/** @returns {BNote|null} */
getNoteOrThrow(noteId) {
getNoteOrThrow(noteId: string): BNote {
const note = this.notes[noteId];
if (!note) {
throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
@ -89,9 +105,8 @@ class Becca {
return note;
}
/** @returns {BNote[]} */
getNotes(noteIds, ignoreMissing = false) {
const filteredNotes = [];
getNotes(noteIds: string[], ignoreMissing: boolean = false): BNote[] {
const filteredNotes: BNote[] = [];
for (const noteId of noteIds) {
const note = this.notes[noteId];
@ -110,13 +125,11 @@ class Becca {
return filteredNotes;
}
/** @returns {BBranch|null} */
getBranch(branchId) {
getBranch(branchId: string): BBranch | null {
return this.branches[branchId];
}
/** @returns {BBranch|null} */
getBranchOrThrow(branchId) {
getBranchOrThrow(branchId: string): BBranch | null {
const branch = this.getBranch(branchId);
if (!branch) {
throw new NotFoundError(`Branch '${branchId}' was not found in becca.`);
@ -124,13 +137,11 @@ class Becca {
return branch;
}
/** @returns {BAttribute|null} */
getAttribute(attributeId) {
getAttribute(attributeId: string): BAttribute | null {
return this.attributes[attributeId];
}
/** @returns {BAttribute} */
getAttributeOrThrow(attributeId) {
getAttributeOrThrow(attributeId: string): BAttribute {
const attribute = this.getAttribute(attributeId);
if (!attribute) {
throw new NotFoundError(`Attribute '${attributeId}' does not exist.`);
@ -139,21 +150,18 @@ class Becca {
return attribute;
}
/** @returns {BBranch|null} */
getBranchFromChildAndParent(childNoteId, parentNoteId) {
getBranchFromChildAndParent(childNoteId: string, parentNoteId: string): BBranch | null {
return this.childParentToBranch[`${childNoteId}-${parentNoteId}`];
}
/** @returns {BRevision|null} */
getRevision(revisionId) {
getRevision(revisionId: string): BRevision | null {
const row = sql.getRow("SELECT * FROM revisions WHERE revisionId = ?", [revisionId]);
const BRevision = require('./entities/brevision.js'); // avoiding circular dependency problems
const BRevision = require('./entities/brevision'); // avoiding circular dependency problems
return row ? new BRevision(row) : null;
}
/** @returns {BAttachment|null} */
getAttachment(attachmentId, opts = {}) {
getAttachment(attachmentId: string, opts: AttachmentOpts = {}): BAttachment | null {
opts.includeContentLength = !!opts.includeContentLength;
const query = opts.includeContentLength
@ -163,14 +171,13 @@ class Becca {
WHERE attachmentId = ? AND isDeleted = 0`
: `SELECT * FROM attachments WHERE attachmentId = ? AND isDeleted = 0`;
const BAttachment = require('./entities/battachment.js'); // avoiding circular dependency problems
const BAttachment = require('./entities/battachment'); // avoiding circular dependency problems
return sql.getRows(query, [attachmentId])
.map(row => new BAttachment(row))[0];
}
/** @returns {BAttachment} */
getAttachmentOrThrow(attachmentId, opts = {}) {
getAttachmentOrThrow(attachmentId: string, opts: AttachmentOpts = {}): BAttachment {
const attachment = this.getAttachment(attachmentId, opts);
if (!attachment) {
throw new NotFoundError(`Attachment '${attachmentId}' has not been found.`);
@ -178,38 +185,36 @@ class Becca {
return attachment;
}
/** @returns {BAttachment[]} */
getAttachments(attachmentIds) {
const BAttachment = require('./entities/battachment.js'); // avoiding circular dependency problems
return sql.getManyRows("SELECT * FROM attachments WHERE attachmentId IN (???) AND isDeleted = 0", attachmentIds)
getAttachments(attachmentIds: string[]): BAttachment[] {
const BAttachment = require('./entities/battachment'); // avoiding circular dependency problems
return sql.getManyRows<AttachmentRow>("SELECT * FROM attachments WHERE attachmentId IN (???) AND isDeleted = 0", attachmentIds)
.map(row => new BAttachment(row));
}
/** @returns {BBlob|null} */
getBlob(entity) {
getBlob(entity: { blobId?: string }): BBlob | null {
if (!entity.blobId) {
return null;
}
const row = sql.getRow("SELECT *, LENGTH(content) AS contentLength FROM blobs WHERE blobId = ?", [entity.blobId]);
const BBlob = require('./entities/bblob.js'); // avoiding circular dependency problems
const BBlob = require('./entities/bblob'); // avoiding circular dependency problems
return row ? new BBlob(row) : null;
}
/** @returns {BOption|null} */
getOption(name) {
getOption(name: string): BOption | null {
return this.options[name];
}
/** @returns {BEtapiToken[]} */
getEtapiTokens() {
getEtapiTokens(): BEtapiToken[] {
return Object.values(this.etapiTokens);
}
/** @returns {BEtapiToken|null} */
getEtapiToken(etapiTokenId) {
getEtapiToken(etapiTokenId: string): BEtapiToken | null {
return this.etapiTokens[etapiTokenId];
}
/** @returns {AbstractBeccaEntity|null} */
getEntity(entityName, entityId) {
getEntity<T extends AbstractBeccaEntity<T>>(entityName: string, entityId: string): AbstractBeccaEntity<T> | null {
if (!entityName || !entityId) {
return null;
}
@ -231,22 +236,20 @@ class Becca {
throw new Error(`Unknown entity name '${camelCaseEntityName}' (original argument '${entityName}')`);
}
return this[camelCaseEntityName][entityId];
return (this as any)[camelCaseEntityName][entityId];
}
/** @returns {BRecentNote[]} */
getRecentNotesFromQuery(query, params = []) {
getRecentNotesFromQuery(query: string, params = []): BRecentNote[] {
const rows = sql.getRows(query, params);
const BRecentNote = require('./entities/brecent_note.js'); // avoiding circular dependency problems
const BRecentNote = require('./entities/brecent_note'); // avoiding circular dependency problems
return rows.map(row => new BRecentNote(row));
}
/** @returns {BRevision[]} */
getRevisionsFromQuery(query, params = []) {
const rows = sql.getRows(query, params);
getRevisionsFromQuery(query: string, params = []): BRevision[] {
const rows = sql.getRows<RevisionRow>(query, params);
const BRevision = require('./entities/brevision.js'); // avoiding circular dependency problems
const BRevision = require('./entities/brevision'); // avoiding circular dependency problems
return rows.map(row => new BRevision(row));
}
@ -260,8 +263,8 @@ class Becca {
if (!this.allNoteSetCache) {
const allNotes = [];
for (const noteId in becca.notes) {
const note = becca.notes[noteId];
for (const noteId in this.notes) {
const note = this.notes[noteId];
// in the process of loading data sometimes we create "skeleton" note instances which are expected to be filled later
// in case of inconsistent data this might not work and search will then crash on these
@ -277,6 +280,4 @@ class Becca {
}
}
const becca = new Becca();
module.exports = becca;
export = Becca;

7
src/becca/becca.ts Normal file
View File

@ -0,0 +1,7 @@
"use strict";
import Becca = require("./becca-interface");
const becca = new Becca();
export = becca;

View File

@ -1,17 +1,17 @@
"use strict";
const sql = require('../services/sql.js');
const eventService = require('../services/events.js');
const becca = require('./becca.js');
const sqlInit = require('../services/sql_init.js');
const log = require('../services/log.js');
const BNote = require('./entities/bnote.js');
const BBranch = require('./entities/bbranch.js');
const BAttribute = require('./entities/battribute.js');
const BOption = require('./entities/boption.js');
const BEtapiToken = require('./entities/betapi_token.js');
const cls = require('../services/cls.js');
const entityConstructor = require('../becca/entity_constructor.js');
const sql = require('../services/sql');
const eventService = require('../services/events');
const becca = require('./becca');
const sqlInit = require('../services/sql_init');
const log = require('../services/log');
const BNote = require('./entities/bnote');
const BBranch = require('./entities/bbranch');
const BAttribute = require('./entities/battribute');
const BOption = require('./entities/boption');
const BEtapiToken = require('./entities/betapi_token');
const cls = require('../services/cls');
const entityConstructor = require('../becca/entity_constructor');
const beccaLoaded = new Promise((res, rej) => {
sqlInit.dbReady.then(() => {
@ -71,10 +71,10 @@ function load() {
function reload(reason) {
load();
require('../services/ws.js').reloadFrontend(reason || "becca reloaded");
require('../services/ws').reloadFrontend(reason || "becca reloaded");
}
eventService.subscribeBeccaLoader([eventService.ENTITY_CHANGE_SYNCED], ({entityName, entityRow}) => {
eventService.subscribeBeccaLoader([eventService.ENTITY_CHANGE_SYNCED], ({ entityName, entityRow }) => {
if (!becca.loaded) {
return;
}
@ -97,7 +97,7 @@ eventService.subscribeBeccaLoader([eventService.ENTITY_CHANGE_SYNCED], ({entity
postProcessEntityUpdate(entityName, entityRow);
});
eventService.subscribeBeccaLoader(eventService.ENTITY_CHANGED, ({entityName, entity}) => {
eventService.subscribeBeccaLoader(eventService.ENTITY_CHANGED, ({ entityName, entity }) => {
if (!becca.loaded) {
return;
}
@ -124,7 +124,7 @@ function postProcessEntityUpdate(entityName, entityRow) {
}
}
eventService.subscribeBeccaLoader([eventService.ENTITY_DELETED, eventService.ENTITY_DELETE_SYNCED], ({entityName, entityId}) => {
eventService.subscribeBeccaLoader([eventService.ENTITY_DELETED, eventService.ENTITY_DELETE_SYNCED], ({ entityName, entityId }) => {
if (!becca.loaded) {
return;
}

View File

@ -1,8 +1,8 @@
"use strict";
const becca = require('./becca.js');
const cls = require('../services/cls.js');
const log = require('../services/log.js');
const becca = require('./becca');
const cls = require('../services/cls');
const log = require('../services/log');
function isNotePathArchived(notePath) {
const noteId = notePath[notePath.length - 1];

View File

@ -1,66 +1,86 @@
"use strict";
const utils = require('../../services/utils.js');
const sql = require('../../services/sql.js');
const entityChangesService = require('../../services/entity_changes.js');
const eventService = require('../../services/events.js');
const dateUtils = require('../../services/date_utils.js');
const cls = require('../../services/cls.js');
const log = require('../../services/log.js');
const protectedSessionService = require('../../services/protected_session.js');
const blobService = require('../../services/blob.js');
import utils = require('../../services/utils');
import sql = require('../../services/sql');
import entityChangesService = require('../../services/entity_changes');
import eventService = require('../../services/events');
import dateUtils = require('../../services/date_utils');
import cls = require('../../services/cls');
import log = require('../../services/log');
import protectedSessionService = require('../../services/protected_session');
import blobService = require('../../services/blob');
import Becca = require('../becca-interface');
let becca = null;
let becca: Becca | null = null;
interface ContentOpts {
forceSave?: boolean;
forceFrontendReload?: boolean;
}
/**
* This interface contains the data that is shared across all the objects of a given derived class of {@link AbstractBeccaEntity}.
* For example, all BAttributes will share their content, but all BBranches will have another set of this data.
*/
interface ConstructorData<T extends AbstractBeccaEntity<T>> {
primaryKeyName: string;
entityName: string;
hashedProperties: (keyof T)[];
}
/**
* Base class for all backend entities.
*
* @type T the same entity type needed for self-reference in {@link ConstructorData}.
*/
class AbstractBeccaEntity {
/** @protected */
beforeSaving() {
if (!this[this.constructor.primaryKeyName]) {
this[this.constructor.primaryKeyName] = utils.newEntityId();
abstract class AbstractBeccaEntity<T extends AbstractBeccaEntity<T>> {
protected utcDateCreated?: string;
protected utcDateModified?: string;
protected dateCreated?: string;
protected dateModified?: string;
isProtected?: boolean;
isSynced?: boolean;
blobId?: string;
protected beforeSaving() {
const constructorData = (this.constructor as unknown as ConstructorData<T>);
if (!(this as any)[constructorData.primaryKeyName]) {
(this as any)[constructorData.primaryKeyName] = utils.newEntityId();
}
}
/** @protected */
getUtcDateChanged() {
return this.utcDateModified || this.utcDateCreated;
}
/**
* @protected
* @returns {Becca}
*/
get becca() {
protected get becca(): Becca {
if (!becca) {
becca = require('../becca.js');
becca = require('../becca');
}
return becca;
return becca as Becca;
}
/** @protected */
putEntityChange(isDeleted) {
protected putEntityChange(isDeleted: boolean) {
const constructorData = (this.constructor as unknown as ConstructorData<T>);
entityChangesService.putEntityChange({
entityName: this.constructor.entityName,
entityId: this[this.constructor.primaryKeyName],
entityName: constructorData.entityName,
entityId: (this as any)[constructorData.primaryKeyName],
hash: this.generateHash(isDeleted),
isErased: false,
utcDateChanged: this.getUtcDateChanged(),
isSynced: this.constructor.entityName !== 'options' || !!this.isSynced
isSynced: constructorData.entityName !== 'options' || !!this.isSynced
});
}
/**
* @protected
* @returns {string}
*/
generateHash(isDeleted) {
generateHash(isDeleted?: boolean): string {
const constructorData = (this.constructor as unknown as ConstructorData<T>);
let contentToHash = "";
for (const propertyName of this.constructor.hashedProperties) {
contentToHash += `|${this[propertyName]}`;
for (const propertyName of constructorData.hashedProperties) {
contentToHash += `|${(this as any)[propertyName]}`;
}
if (isDeleted) {
@ -70,31 +90,33 @@ class AbstractBeccaEntity {
return utils.hash(contentToHash).substr(0, 10);
}
/** @protected */
getPojoToSave() {
protected getPojoToSave() {
return this.getPojo();
}
/**
* @protected
* @abstract
*/
getPojo() {
throw new Error(`Unimplemented getPojo() for entity '${this.constructor.name}'`)
hasStringContent(): boolean {
// TODO: Not sure why some entities don't implement it.
return true;
}
abstract getPojo(): {};
get isDeleted(): boolean {
// TODO: Not sure why some entities don't implement it.
return false;
}
/**
* Saves entity - executes SQL, but doesn't commit the transaction on its own
*
* @returns {this}
*/
save(opts = {}) {
const entityName = this.constructor.entityName;
const primaryKeyName = this.constructor.primaryKeyName;
save(): this {
const constructorData = (this.constructor as unknown as ConstructorData<T>);
const entityName = constructorData.entityName;
const primaryKeyName = constructorData.primaryKeyName;
const isNewEntity = !this[primaryKeyName];
const isNewEntity = !(this as any)[primaryKeyName];
this.beforeSaving(opts);
this.beforeSaving();
const pojo = this.getPojoToSave();
@ -124,14 +146,14 @@ class AbstractBeccaEntity {
return this;
}
/** @protected */
_setContent(content, opts = {}) {
protected _setContent(content: string | Buffer, opts: ContentOpts = {}) {
// client code asks to save entity even if blobId didn't change (something else was changed)
opts.forceSave = !!opts.forceSave;
opts.forceFrontendReload = !!opts.forceFrontendReload;
if (content === null || content === undefined) {
throw new Error(`Cannot set null content to ${this.constructor.primaryKeyName} '${this[this.constructor.primaryKeyName]}'`);
const constructorData = (this.constructor as unknown as ConstructorData<T>);
throw new Error(`Cannot set null content to ${constructorData.primaryKeyName} '${(this as any)[constructorData.primaryKeyName]}'`);
}
if (this.hasStringContent()) {
@ -140,32 +162,36 @@ class AbstractBeccaEntity {
content = Buffer.isBuffer(content) ? content : Buffer.from(content);
}
const unencryptedContentForHashCalculation = this.#getUnencryptedContentForHashCalculation(content);
const unencryptedContentForHashCalculation = this.getUnencryptedContentForHashCalculation(content);
if (this.isProtected) {
if (protectedSessionService.isProtectedSessionAvailable()) {
content = protectedSessionService.encrypt(content);
const encryptedContent = protectedSessionService.encrypt(content);
if (!encryptedContent) {
throw new Error(`Unable to encrypt the content of the entity.`);
}
content = encryptedContent;
} else {
throw new Error(`Cannot update content of blob since protected session is not available.`);
}
}
sql.transactional(() => {
const newBlobId = this.#saveBlob(content, unencryptedContentForHashCalculation, opts);
const newBlobId = this.saveBlob(content, unencryptedContentForHashCalculation, opts);
const oldBlobId = this.blobId;
if (newBlobId !== oldBlobId || opts.forceSave) {
this.blobId = newBlobId;
this.save();
if (newBlobId !== oldBlobId) {
this.#deleteBlobIfNotUsed(oldBlobId);
if (oldBlobId && newBlobId !== oldBlobId) {
this.deleteBlobIfNotUsed(oldBlobId);
}
}
});
}
#deleteBlobIfNotUsed(oldBlobId) {
private deleteBlobIfNotUsed(oldBlobId: string) {
if (sql.getValue("SELECT 1 FROM notes WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
@ -184,7 +210,7 @@ class AbstractBeccaEntity {
sql.execute("DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId = ?", [oldBlobId]);
}
#getUnencryptedContentForHashCalculation(unencryptedContent) {
private getUnencryptedContentForHashCalculation(unencryptedContent: Buffer | string) {
if (this.isProtected) {
// a "random" prefix makes sure that the calculated hash/blobId is different for a decrypted/encrypted content
const encryptedPrefixSuffix = "t$[nvQg7q)&_ENCRYPTED_?M:Bf&j3jr_";
@ -196,7 +222,7 @@ class AbstractBeccaEntity {
}
}
#saveBlob(content, unencryptedContentForHashCalculation, opts = {}) {
private saveBlob(content: string | Buffer, unencryptedContentForHashCalculation: string | Buffer, opts: ContentOpts = {}) {
/*
* We're using the unencrypted blob for the hash calculation, because otherwise the random IV would
* cause every content blob to be unique which would balloon the database size (esp. with revisioning).
@ -243,41 +269,37 @@ class AbstractBeccaEntity {
return newBlobId;
}
/**
* @protected
* @returns {string|Buffer}
*/
_getContent() {
const row = sql.getRow(`SELECT content FROM blobs WHERE blobId = ?`, [this.blobId]);
protected _getContent(): string | Buffer {
const row = sql.getRow<{ content: string | Buffer }>(`SELECT content FROM blobs WHERE blobId = ?`, [this.blobId]);
if (!row) {
throw new Error(`Cannot find content for ${this.constructor.primaryKeyName} '${this[this.constructor.primaryKeyName]}', blobId '${this.blobId}'`);
const constructorData = (this.constructor as unknown as ConstructorData<T>);
throw new Error(`Cannot find content for ${constructorData.primaryKeyName} '${(this as any)[constructorData.primaryKeyName]}', blobId '${this.blobId}'`);
}
return blobService.processContent(row.content, this.isProtected, this.hasStringContent());
return blobService.processContent(row.content, this.isProtected || false, this.hasStringContent());
}
/**
* Mark the entity as (soft) deleted. It will be completely erased later.
*
* This is a low-level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead.
*
* @param [deleteId=null]
*/
markAsDeleted(deleteId = null) {
const entityId = this[this.constructor.primaryKeyName];
const entityName = this.constructor.entityName;
markAsDeleted(deleteId: string | null = null) {
const constructorData = (this.constructor as unknown as ConstructorData<T>);
const entityId = (this as any)[constructorData.primaryKeyName];
const entityName = constructorData.entityName;
this.utcDateModified = dateUtils.utcNowDateTime();
sql.execute(`UPDATE ${entityName} SET isDeleted = 1, deleteId = ?, utcDateModified = ?
WHERE ${this.constructor.primaryKeyName} = ?`,
WHERE ${constructorData.primaryKeyName} = ?`,
[deleteId, this.utcDateModified, entityId]);
if (this.dateModified) {
this.dateModified = dateUtils.localNowDateTime();
sql.execute(`UPDATE ${entityName} SET dateModified = ? WHERE ${this.constructor.primaryKeyName} = ?`,
sql.execute(`UPDATE ${entityName} SET dateModified = ? WHERE ${constructorData.primaryKeyName} = ?`,
[this.dateModified, entityId]);
}
@ -289,13 +311,14 @@ class AbstractBeccaEntity {
}
markAsDeletedSimple() {
const entityId = this[this.constructor.primaryKeyName];
const entityName = this.constructor.entityName;
const constructorData = (this.constructor as unknown as ConstructorData<T>);
const entityId = (this as any)[constructorData.primaryKeyName];
const entityName = constructorData.entityName;
this.utcDateModified = dateUtils.utcNowDateTime();
sql.execute(`UPDATE ${entityName} SET isDeleted = 1, utcDateModified = ?
WHERE ${this.constructor.primaryKeyName} = ?`,
WHERE ${constructorData.primaryKeyName} = ?`,
[this.utcDateModified, entityId]);
log.info(`Marking ${entityName} ${entityId} as deleted`);
@ -306,4 +329,4 @@ class AbstractBeccaEntity {
}
}
module.exports = AbstractBeccaEntity;
export = AbstractBeccaEntity;

View File

@ -1,29 +1,57 @@
"use strict";
const utils = require('../../services/utils.js');
const dateUtils = require('../../services/date_utils.js');
const AbstractBeccaEntity = require('./abstract_becca_entity.js');
const sql = require('../../services/sql.js');
const protectedSessionService = require('../../services/protected_session.js');
const log = require('../../services/log.js');
import utils = require('../../services/utils');
import dateUtils = require('../../services/date_utils');
import AbstractBeccaEntity = require('./abstract_becca_entity');
import sql = require('../../services/sql');
import protectedSessionService = require('../../services/protected_session');
import log = require('../../services/log');
import { AttachmentRow } from './rows';
import BNote = require('./bnote');
import BBranch = require('./bbranch');
const attachmentRoleToNoteTypeMapping = {
'image': 'image',
'file': 'file'
};
interface ContentOpts {
// TODO: Found in bnote.ts, to check if it's actually used and not a typo.
forceSave?: boolean;
/** will also save this BAttachment entity */
forceFullSave?: boolean;
/** override frontend heuristics on when to reload, instruct to reload */
forceFrontendReload?: boolean;
}
/**
* Attachment represent data related/attached to the note. Conceptually similar to attributes, but intended for
* larger amounts of data and generally not accessible to the user.
*
* @extends AbstractBeccaEntity
*/
class BAttachment extends AbstractBeccaEntity {
class BAttachment extends AbstractBeccaEntity<BAttachment> {
static get entityName() { return "attachments"; }
static get primaryKeyName() { return "attachmentId"; }
static get hashedProperties() { return ["attachmentId", "ownerId", "role", "mime", "title", "blobId", "utcDateScheduledForErasureSince"]; }
constructor(row) {
noteId?: number;
attachmentId?: string;
/** either noteId or revisionId to which this attachment belongs */
ownerId: string;
role: string;
mime: string;
title: string;
type?: keyof typeof attachmentRoleToNoteTypeMapping;
position?: number;
blobId?: string;
isProtected?: boolean;
dateModified?: string;
utcDateScheduledForErasureSince?: string;
/** optionally added to the entity */
contentLength?: number;
isDecrypted?: boolean;
constructor(row: AttachmentRow) {
super();
if (!row.ownerId?.trim()) {
@ -36,43 +64,23 @@ class BAttachment extends AbstractBeccaEntity {
throw new Error("'title' must be given to initialize a Attachment entity");
}
/** @type {string} */
this.attachmentId = row.attachmentId;
/**
* either noteId or revisionId to which this attachment belongs
* @type {string}
*/
this.ownerId = row.ownerId;
/** @type {string} */
this.role = row.role;
/** @type {string} */
this.mime = row.mime;
/** @type {string} */
this.title = row.title;
/** @type {int} */
this.position = row.position;
/** @type {string} */
this.blobId = row.blobId;
/** @type {boolean} */
this.isProtected = !!row.isProtected;
/** @type {string} */
this.dateModified = row.dateModified;
/** @type {string} */
this.utcDateModified = row.utcDateModified;
/** @type {string} */
this.utcDateScheduledForErasureSince = row.utcDateScheduledForErasureSince;
/**
* optionally added to the entity
* @type {int}
*/
this.contentLength = row.contentLength;
this.decrypt();
}
/** @returns {BAttachment} */
copy() {
copy(): BAttachment {
return new BAttachment({
ownerId: this.ownerId,
role: this.role,
@ -83,14 +91,13 @@ class BAttachment extends AbstractBeccaEntity {
});
}
/** @returns {BNote} */
getNote() {
getNote(): BNote {
return this.becca.notes[this.ownerId];
}
/** @returns {boolean} true if the note has string content (not binary) */
hasStringContent() {
return utils.isStringNote(this.type, this.mime);
/** @returns true if the note has string content (not binary) */
hasStringContent(): boolean {
return this.type !== undefined && utils.isStringNote(this.type, this.mime);
}
isContentAvailable() {
@ -111,33 +118,26 @@ class BAttachment extends AbstractBeccaEntity {
if (!this.isDecrypted && protectedSessionService.isProtectedSessionAvailable()) {
try {
this.title = protectedSessionService.decryptString(this.title);
this.title = protectedSessionService.decryptString(this.title) || "";
this.isDecrypted = true;
}
catch (e) {
catch (e: any) {
log.error(`Could not decrypt attachment ${this.attachmentId}: ${e.message} ${e.stack}`);
}
}
}
/** @returns {string|Buffer} */
getContent() {
getContent(): string | Buffer {
return this._getContent();
}
/**
* @param content
* @param {object} [opts]
* @param {object} [opts.forceSave=false] - will also save this BAttachment entity
* @param {object} [opts.forceFrontendReload=false] - override frontend heuristics on when to reload, instruct to reload
*/
setContent(content, opts) {
setContent(content: string | Buffer, opts: ContentOpts) {
this._setContent(content, opts);
}
/** @returns {{note: BNote, branch: BBranch}} */
convertToNote() {
if (this.type === 'search') {
convertToNote(): { note: BNote, branch: BBranch } {
// TODO: can this ever be "search"?
if (this.type as string === 'search') {
throw new Error(`Note of type search cannot have child notes`);
}
@ -154,12 +154,12 @@ class BAttachment extends AbstractBeccaEntity {
throw new Error(`Cannot convert protected attachment outside of protected session`);
}
const noteService = require('../../services/notes.js');
const noteService = require('../../services/notes');
const { note, branch } = noteService.createNewNote({
parentNoteId: this.ownerId,
title: this.title,
type: attachmentRoleToNoteTypeMapping[this.role],
type: (attachmentRoleToNoteTypeMapping as any)[this.role],
mime: this.mime,
content: this.getContent(),
isProtected: this.isProtected
@ -196,9 +196,9 @@ class BAttachment extends AbstractBeccaEntity {
super.beforeSaving();
if (this.position === undefined || this.position === null) {
this.position = 10 + sql.getValue(`SELECT COALESCE(MAX(position), 0)
FROM attachments
WHERE ownerId = ?`, [this.noteId]);
this.position = 10 + sql.getValue<number>(`SELECT COALESCE(MAX(position), 0)
FROM attachments
WHERE ownerId = ?`, [this.noteId]);
}
this.dateModified = dateUtils.localNowDateTime();
@ -211,7 +211,7 @@ class BAttachment extends AbstractBeccaEntity {
ownerId: this.ownerId,
role: this.role,
mime: this.mime,
title: this.title,
title: this.title || undefined,
position: this.position,
blobId: this.blobId,
isProtected: !!this.isProtected,
@ -229,7 +229,7 @@ class BAttachment extends AbstractBeccaEntity {
if (pojo.isProtected) {
if (this.isDecrypted) {
pojo.title = protectedSessionService.encrypt(pojo.title);
pojo.title = protectedSessionService.encrypt(pojo.title || "") || undefined;
}
else {
// updating protected note outside of protected session means we will keep original ciphertexts
@ -241,4 +241,4 @@ class BAttachment extends AbstractBeccaEntity {
}
}
module.exports = BAttachment;
export = BAttachment;

View File

@ -1,30 +1,34 @@
"use strict";
const BNote = require('./bnote.js');
const AbstractBeccaEntity = require('./abstract_becca_entity.js');
const sql = require('../../services/sql.js');
const dateUtils = require('../../services/date_utils.js');
const promotedAttributeDefinitionParser = require('../../services/promoted_attribute_definition_parser.js');
const {sanitizeAttributeName} = require('../../services/sanitize_attribute_name.js');
import BNote = require('./bnote');
import AbstractBeccaEntity = require('./abstract_becca_entity');
import dateUtils = require('../../services/date_utils');
import promotedAttributeDefinitionParser = require('../../services/promoted_attribute_definition_parser');
import sanitizeAttributeName = require('../../services/sanitize_attribute_name');
import { AttributeRow, AttributeType } from './rows';
/**
* There are currently only two types of attributes, labels or relations.
* @typedef {"label" | "relation"} AttributeType
*/
interface SavingOpts {
skipValidation?: boolean;
}
/**
* Attribute is an abstract concept which has two real uses - label (key - value pair)
* and relation (representing named relationship between source and target note)
*
* @extends AbstractBeccaEntity
*/
class BAttribute extends AbstractBeccaEntity {
class BAttribute extends AbstractBeccaEntity<BAttribute> {
static get entityName() { return "attributes"; }
static get primaryKeyName() { return "attributeId"; }
static get hashedProperties() { return ["attributeId", "noteId", "type", "name", "value", "isInheritable"]; }
constructor(row) {
attributeId!: string;
noteId!: string;
type!: AttributeType;
name!: string;
position!: number;
value!: string;
isInheritable!: boolean;
constructor(row: AttributeRow) {
super();
if (!row) {
@ -35,7 +39,7 @@ class BAttribute extends AbstractBeccaEntity {
this.init();
}
updateFromRow(row) {
updateFromRow(row: AttributeRow) {
this.update([
row.attributeId,
row.noteId,
@ -48,22 +52,14 @@ class BAttribute extends AbstractBeccaEntity {
]);
}
update([attributeId, noteId, type, name, value, isInheritable, position, utcDateModified]) {
/** @type {string} */
update([attributeId, noteId, type, name, value, isInheritable, position, utcDateModified]: any[]) {
this.attributeId = attributeId;
/** @type {string} */
this.noteId = noteId;
/** @type {AttributeType} */
this.type = type;
/** @type {string} */
this.name = name;
/** @type {int} */
this.position = position;
/** @type {string} */
this.value = value || "";
/** @type {boolean} */
this.isInheritable = !!isInheritable;
/** @type {string} */
this.utcDateModified = utcDateModified;
return this;
@ -182,12 +178,12 @@ class BAttribute extends AbstractBeccaEntity {
return !(this.attributeId in this.becca.attributes);
}
beforeSaving(opts = {}) {
beforeSaving(opts: SavingOpts = {}) {
if (!opts.skipValidation) {
this.validate();
}
this.name = sanitizeAttributeName(this.name);
this.name = sanitizeAttributeName.sanitizeAttributeName(this.name);
if (!this.value) {
// null value isn't allowed
@ -226,7 +222,7 @@ class BAttribute extends AbstractBeccaEntity {
};
}
createClone(type, name, value, isInheritable) {
createClone(type: AttributeType, name: string, value: string, isInheritable: boolean) {
return new BAttribute({
noteId: this.noteId,
type: type,
@ -239,4 +235,4 @@ class BAttribute extends AbstractBeccaEntity {
}
}
module.exports = BAttribute;
export = BAttribute;

View File

@ -1,25 +1,29 @@
import { BlobRow } from "./rows";
// TODO: Why this does not extend the abstract becca?
class BBlob {
static get entityName() { return "blobs"; }
static get primaryKeyName() { return "blobId"; }
static get hashedProperties() { return ["blobId", "content"]; }
constructor(row) {
/** @type {string} */
blobId: string;
content: string | Buffer;
contentLength: number;
dateModified: string;
utcDateModified: string;
constructor(row: BlobRow) {
this.blobId = row.blobId;
/** @type {string|Buffer} */
this.content = row.content;
/** @type {int} */
this.contentLength = row.contentLength;
/** @type {string} */
this.dateModified = row.dateModified;
/** @type {string} */
this.utcDateModified = row.utcDateModified;
}
getPojo() {
return {
blobId: this.blobId,
content: this.content,
content: this.content || null,
contentLength: this.contentLength,
dateModified: this.dateModified,
utcDateModified: this.utcDateModified
@ -27,4 +31,4 @@ class BBlob {
}
}
module.exports = BBlob;
export = BBlob;

View File

@ -1,12 +1,13 @@
"use strict";
const BNote = require('./bnote.js');
const AbstractBeccaEntity = require('./abstract_becca_entity.js');
const dateUtils = require('../../services/date_utils.js');
const utils = require('../../services/utils.js');
const TaskContext = require('../../services/task_context.js');
const cls = require('../../services/cls.js');
const log = require('../../services/log.js');
import BNote = require('./bnote');
import AbstractBeccaEntity = require('./abstract_becca_entity');
import dateUtils = require('../../services/date_utils');
import utils = require('../../services/utils');
import TaskContext = require('../../services/task_context');
import cls = require('../../services/cls');
import log = require('../../services/log');
import { BranchRow } from './rows';
/**
* Branch represents a relationship between a child note and its parent note. Trilium allows a note to have multiple
@ -14,16 +15,22 @@ const log = require('../../services/log.js');
*
* Note that you should not rely on the branch's identity, since it can change easily with a note's move.
* Always check noteId instead.
*
* @extends AbstractBeccaEntity
*/
class BBranch extends AbstractBeccaEntity {
class BBranch extends AbstractBeccaEntity<BBranch> {
static get entityName() { return "branches"; }
static get primaryKeyName() { return "branchId"; }
// notePosition is not part of hash because it would produce a lot of updates in case of reordering
static get hashedProperties() { return ["branchId", "noteId", "parentNoteId", "prefix"]; }
constructor(row) {
branchId?: string;
noteId!: string;
parentNoteId!: string;
prefix!: string | null;
notePosition!: number;
isExpanded!: boolean;
utcDateModified?: string;
constructor(row: BranchRow) {
super();
if (!row) {
@ -34,7 +41,7 @@ class BBranch extends AbstractBeccaEntity {
this.init();
}
updateFromRow(row) {
updateFromRow(row: BranchRow) {
this.update([
row.branchId,
row.noteId,
@ -46,20 +53,13 @@ class BBranch extends AbstractBeccaEntity {
]);
}
update([branchId, noteId, parentNoteId, prefix, notePosition, isExpanded, utcDateModified]) {
/** @type {string} */
update([branchId, noteId, parentNoteId, prefix, notePosition, isExpanded, utcDateModified]: any) {
this.branchId = branchId;
/** @type {string} */
this.noteId = noteId;
/** @type {string} */
this.parentNoteId = parentNoteId;
/** @type {string|null} */
this.prefix = prefix;
/** @type {int} */
this.notePosition = notePosition;
/** @type {boolean} */
this.isExpanded = !!isExpanded;
/** @type {string} */
this.utcDateModified = utcDateModified;
return this;
@ -83,18 +83,18 @@ class BBranch extends AbstractBeccaEntity {
}
const parentNote = this.parentNote;
if (parentNote) {
if (!childNote.parents.includes(parentNote)) {
childNote.parents.push(parentNote);
}
if (!childNote.parents.includes(parentNote)) {
childNote.parents.push(parentNote);
}
if (!parentNote.children.includes(childNote)) {
parentNote.children.push(childNote);
if (!parentNote.children.includes(childNote)) {
parentNote.children.push(childNote);
}
}
}
/** @returns {BNote} */
get childNote() {
get childNote(): BNote {
if (!(this.noteId in this.becca.notes)) {
// entities can come out of order in sync/import, create skeleton which will be filled later
this.becca.addNote(this.noteId, new BNote({noteId: this.noteId}));
@ -103,13 +103,12 @@ class BBranch extends AbstractBeccaEntity {
return this.becca.notes[this.noteId];
}
/** @returns {BNote} */
getNote() {
getNote(): BNote {
return this.childNote;
}
/** @returns {BNote|undefined} - root branch will have undefined parent, all other branches have to have a parent note */
get parentNote() {
/** @returns root branch will have undefined parent, all other branches have to have a parent note */
get parentNote(): BNote | undefined {
if (!(this.parentNoteId in this.becca.notes) && this.parentNoteId !== 'none') {
// entities can come out of order in sync/import, create skeleton which will be filled later
this.becca.addNote(this.parentNoteId, new BNote({noteId: this.parentNoteId}));
@ -119,7 +118,7 @@ class BBranch extends AbstractBeccaEntity {
}
get isDeleted() {
return !(this.branchId in this.becca.branches);
return (this.branchId == undefined || !(this.branchId in this.becca.branches));
}
/**
@ -138,12 +137,11 @@ class BBranch extends AbstractBeccaEntity {
/**
* Delete a branch. If this is a last note's branch, delete the note as well.
*
* @param {string} [deleteId] - optional delete identified
* @param {TaskContext} [taskContext]
* @param deleteId - optional delete identified
*
* @returns {boolean} - true if note has been deleted, false otherwise
* @returns true if note has been deleted, false otherwise
*/
deleteBranch(deleteId, taskContext) {
deleteBranch(deleteId: string, taskContext: TaskContext): boolean {
if (!deleteId) {
deleteId = utils.randomString(10);
}
@ -161,7 +159,7 @@ class BBranch extends AbstractBeccaEntity {
if (parentBranches.length === 1 && parentBranches[0] === this) {
// needs to be run before branches and attributes are deleted and thus attached relations disappear
const handlers = require('../../services/handlers.js');
const handlers = require('../../services/handlers');
handlers.runAttachedRelations(note, 'runOnNoteDeletion', note);
}
}
@ -182,7 +180,9 @@ class BBranch extends AbstractBeccaEntity {
}
for (const childBranch of note.getChildBranches()) {
childBranch.deleteBranch(deleteId, taskContext);
if (childBranch) {
childBranch.deleteBranch(deleteId, taskContext);
}
}
// first delete children and then parent - this will show up better in recent changes
@ -222,11 +222,17 @@ class BBranch extends AbstractBeccaEntity {
if (this.notePosition === undefined || this.notePosition === null) {
let maxNotePos = 0;
for (const childBranch of this.parentNote.getChildBranches()) {
if (maxNotePos < childBranch.notePosition
&& childBranch.noteId !== '_hidden' // hidden has a very large notePosition to always stay last
) {
maxNotePos = childBranch.notePosition;
if (this.parentNote) {
for (const childBranch of this.parentNote.getChildBranches()) {
if (!childBranch) {
continue;
}
if (maxNotePos < childBranch.notePosition
&& childBranch.noteId !== '_hidden' // hidden has a very large notePosition to always stay last
) {
maxNotePos = childBranch.notePosition;
}
}
}
@ -261,7 +267,7 @@ class BBranch extends AbstractBeccaEntity {
};
}
createClone(parentNoteId, notePosition) {
createClone(parentNoteId: string, notePosition: number) {
const existingBranch = this.becca.getBranchFromChildAndParent(this.noteId, parentNoteId);
if (existingBranch) {
@ -279,4 +285,4 @@ class BBranch extends AbstractBeccaEntity {
}
}
module.exports = BBranch;
export = BBranch;

View File

@ -1,7 +1,9 @@
"use strict";
const dateUtils = require('../../services/date_utils.js');
const AbstractBeccaEntity = require('./abstract_becca_entity.js');
import { EtapiTokenRow } from "./rows";
import dateUtils = require('../../services/date_utils');
import AbstractBeccaEntity = require('./abstract_becca_entity');
/**
* EtapiToken is an entity representing token used to authenticate against Trilium REST API from client applications.
@ -11,15 +13,18 @@ const AbstractBeccaEntity = require('./abstract_becca_entity.js');
*
* The format user is presented with is "<etapiTokenId>_<tokenHash>". This is also called "authToken" to distinguish it
* from tokenHash and token.
*
* @extends AbstractBeccaEntity
*/
class BEtapiToken extends AbstractBeccaEntity {
class BEtapiToken extends AbstractBeccaEntity<BEtapiToken> {
static get entityName() { return "etapi_tokens"; }
static get primaryKeyName() { return "etapiTokenId"; }
static get hashedProperties() { return ["etapiTokenId", "name", "tokenHash", "utcDateCreated", "utcDateModified", "isDeleted"]; }
constructor(row) {
etapiTokenId!: string;
name!: string;
tokenHash!: string;
private _isDeleted!: boolean;
constructor(row: EtapiTokenRow) {
super();
if (!row) {
@ -30,19 +35,17 @@ class BEtapiToken extends AbstractBeccaEntity {
this.init();
}
updateFromRow(row) {
/** @type {string} */
get isDeleted() {
return this._isDeleted;
}
updateFromRow(row: EtapiTokenRow) {
this.etapiTokenId = row.etapiTokenId;
/** @type {string} */
this.name = row.name;
/** @type {string} */
this.tokenHash = row.tokenHash;
/** @type {string} */
this.utcDateCreated = row.utcDateCreated || dateUtils.utcNowDateTime();
/** @type {string} */
this.utcDateModified = row.utcDateModified || this.utcDateCreated;
/** @type {boolean} */
this.isDeleted = !!row.isDeleted;
this._isDeleted = !!row.isDeleted;
if (this.etapiTokenId) {
this.becca.etapiTokens[this.etapiTokenId] = this;
@ -75,4 +78,4 @@ class BEtapiToken extends AbstractBeccaEntity {
}
}
module.exports = BEtapiToken;
export = BEtapiToken;

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +1,32 @@
"use strict";
const dateUtils = require('../../services/date_utils.js');
const AbstractBeccaEntity = require('./abstract_becca_entity.js');
import dateUtils = require('../../services/date_utils');
import AbstractBeccaEntity = require('./abstract_becca_entity');
import { OptionRow } from './rows';
/**
* Option represents a name-value pair, either directly configurable by the user or some system property.
*
* @extends AbstractBeccaEntity
*/
class BOption extends AbstractBeccaEntity {
class BOption extends AbstractBeccaEntity<BOption> {
static get entityName() { return "options"; }
static get primaryKeyName() { return "name"; }
static get hashedProperties() { return ["name", "value"]; }
constructor(row) {
name!: string;
value!: string;
isSynced!: boolean;
constructor(row: OptionRow) {
super();
this.updateFromRow(row);
this.becca.options[this.name] = this;
}
updateFromRow(row) {
/** @type {string} */
updateFromRow(row: OptionRow) {
this.name = row.name;
/** @type {string} */
this.value = row.value;
/** @type {boolean} */
this.isSynced = !!row.isSynced;
/** @type {string} */
this.utcDateModified = row.utcDateModified;
}
@ -47,4 +46,4 @@ class BOption extends AbstractBeccaEntity {
}
}
module.exports = BOption;
export = BOption;

View File

@ -1,25 +1,26 @@
"use strict";
const dateUtils = require('../../services/date_utils.js');
const AbstractBeccaEntity = require('./abstract_becca_entity.js');
import { RecentNoteRow } from "./rows";
import dateUtils = require('../../services/date_utils');
import AbstractBeccaEntity = require('./abstract_becca_entity');
/**
* RecentNote represents recently visited note.
*
* @extends AbstractBeccaEntity
*/
class BRecentNote extends AbstractBeccaEntity {
class BRecentNote extends AbstractBeccaEntity<BRecentNote> {
static get entityName() { return "recent_notes"; }
static get primaryKeyName() { return "noteId"; }
constructor(row) {
noteId: string;
notePath: string;
utcDateCreated: string;
constructor(row: RecentNoteRow) {
super();
/** @type {string} */
this.noteId = row.noteId;
/** @type {string} */
this.notePath = row.notePath;
/** @type {string} */
this.utcDateCreated = row.utcDateCreated || dateUtils.utcNowDateTime();
}
@ -32,4 +33,4 @@ class BRecentNote extends AbstractBeccaEntity {
}
}
module.exports = BRecentNote;
export = BRecentNote;

View File

@ -1,59 +1,67 @@
"use strict";
const protectedSessionService = require('../../services/protected_session.js');
const utils = require('../../services/utils.js');
const dateUtils = require('../../services/date_utils.js');
const becca = require('../becca.js');
const AbstractBeccaEntity = require('./abstract_becca_entity.js');
const sql = require('../../services/sql.js');
const BAttachment = require('./battachment.js');
import protectedSessionService = require('../../services/protected_session');
import utils = require('../../services/utils');
import dateUtils = require('../../services/date_utils');
import becca = require('../becca');
import AbstractBeccaEntity = require('./abstract_becca_entity');
import sql = require('../../services/sql');
import BAttachment = require('./battachment');
import { AttachmentRow, RevisionRow } from './rows';
interface ContentOpts {
/** will also save this BRevision entity */
forceSave?: boolean;
}
interface GetByIdOpts {
includeContentLength?: boolean;
}
/**
* Revision represents a snapshot of note's title and content at some point in the past.
* It's used for seamless note versioning.
*
* @extends AbstractBeccaEntity
*/
class BRevision extends AbstractBeccaEntity {
class BRevision extends AbstractBeccaEntity<BRevision> {
static get entityName() { return "revisions"; }
static get primaryKeyName() { return "revisionId"; }
static get hashedProperties() { return ["revisionId", "noteId", "title", "isProtected", "dateLastEdited", "dateCreated",
"utcDateLastEdited", "utcDateCreated", "utcDateModified", "blobId"]; }
constructor(row, titleDecrypted = false) {
revisionId?: string;
noteId: string;
type: string;
mime: string;
isProtected: boolean;
title: string;
blobId?: string;
dateLastEdited?: string;
dateCreated: string;
utcDateLastEdited?: string;
utcDateCreated: string;
contentLength?: number;
content?: string;
constructor(row: RevisionRow, titleDecrypted = false) {
super();
/** @type {string} */
this.revisionId = row.revisionId;
/** @type {string} */
this.noteId = row.noteId;
/** @type {string} */
this.type = row.type;
/** @type {string} */
this.mime = row.mime;
/** @type {boolean} */
this.isProtected = !!row.isProtected;
/** @type {string} */
this.title = row.title;
/** @type {string} */
this.blobId = row.blobId;
/** @type {string} */
this.dateLastEdited = row.dateLastEdited;
/** @type {string} */
this.dateCreated = row.dateCreated;
/** @type {string} */
this.utcDateLastEdited = row.utcDateLastEdited;
/** @type {string} */
this.utcDateCreated = row.utcDateCreated;
/** @type {string} */
this.utcDateModified = row.utcDateModified;
/** @type {int} */
this.contentLength = row.contentLength;
if (this.isProtected && !titleDecrypted) {
this.title = protectedSessionService.isProtectedSessionAvailable()
? protectedSessionService.decryptString(this.title)
: "[protected]";
const decryptedTitle = protectedSessionService.isProtectedSessionAvailable() ? protectedSessionService.decryptString(this.title) : null;
this.title = decryptedTitle || "[protected]";
}
}
@ -61,8 +69,8 @@ class BRevision extends AbstractBeccaEntity {
return becca.notes[this.noteId];
}
/** @returns {boolean} true if the note has string content (not binary) */
hasStringContent() {
/** @returns true if the note has string content (not binary) */
hasStringContent(): boolean {
return utils.isStringNote(this.type, this.mime);
}
@ -80,16 +88,14 @@ class BRevision extends AbstractBeccaEntity {
*
* This is the same approach as is used for Note's content.
*/
/** @returns {string|Buffer} */
getContent() {
return this._getContent();
// TODO: initial declaration included Buffer, but everywhere it's treated as a string.
getContent(): string {
return this._getContent() as string;
}
/**
* @returns {*}
* @throws Error in case of invalid JSON */
getJsonContent() {
getJsonContent(): {} | null {
const content = this.getContent();
if (!content || !content.trim()) {
@ -99,8 +105,8 @@ class BRevision extends AbstractBeccaEntity {
return JSON.parse(content);
}
/** @returns {*|null} valid object or null if the content cannot be parsed as JSON */
getJsonContentSafely() {
/** @returns valid object or null if the content cannot be parsed as JSON */
getJsonContentSafely(): {} | null {
try {
return this.getJsonContent();
}
@ -109,18 +115,12 @@ class BRevision extends AbstractBeccaEntity {
}
}
/**
* @param content
* @param {object} [opts]
* @param {object} [opts.forceSave=false] - will also save this BRevision entity
*/
setContent(content, opts) {
setContent(content: string | Buffer, opts: ContentOpts = {}) {
this._setContent(content, opts);
}
/** @returns {BAttachment[]} */
getAttachments() {
return sql.getRows(`
getAttachments(): BAttachment[] {
return sql.getRows<AttachmentRow>(`
SELECT attachments.*
FROM attachments
WHERE ownerId = ?
@ -128,8 +128,7 @@ class BRevision extends AbstractBeccaEntity {
.map(row => new BAttachment(row));
}
/** @returns {BAttachment|null} */
getAttachmentById(attachmentId, opts = {}) {
getAttachmentById(attachmentId: String, opts: GetByIdOpts = {}): BAttachment | null {
opts.includeContentLength = !!opts.includeContentLength;
const query = opts.includeContentLength
@ -139,13 +138,12 @@ class BRevision extends AbstractBeccaEntity {
WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`
: `SELECT * FROM attachments WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`;
return sql.getRows(query, [this.revisionId, attachmentId])
return sql.getRows<AttachmentRow>(query, [this.revisionId, attachmentId])
.map(row => new BAttachment(row))[0];
}
/** @returns {BAttachment[]} */
getAttachmentsByRole(role) {
return sql.getRows(`
getAttachmentsByRole(role: string): BAttachment[] {
return sql.getRows<AttachmentRow>(`
SELECT attachments.*
FROM attachments
WHERE ownerId = ?
@ -155,8 +153,7 @@ class BRevision extends AbstractBeccaEntity {
.map(row => new BAttachment(row));
}
/** @returns {BAttachment} */
getAttachmentByTitle(title) {
getAttachmentByTitle(title: string): BAttachment {
// cannot use SQL to filter by title since it can be encrypted
return this.getAttachments().filter(attachment => attachment.title === title)[0];
}
@ -181,7 +178,7 @@ class BRevision extends AbstractBeccaEntity {
type: this.type,
mime: this.mime,
isProtected: this.isProtected,
title: this.title,
title: this.title || undefined,
blobId: this.blobId,
dateLastEdited: this.dateLastEdited,
dateCreated: this.dateCreated,
@ -200,7 +197,7 @@ class BRevision extends AbstractBeccaEntity {
if (pojo.isProtected) {
if (protectedSessionService.isProtectedSessionAvailable()) {
pojo.title = protectedSessionService.encrypt(this.title);
pojo.title = protectedSessionService.encrypt(this.title) || undefined;
}
else {
// updating protected note outside of protected session means we will keep original ciphertexts
@ -212,4 +209,4 @@ class BRevision extends AbstractBeccaEntity {
}
}
module.exports = BRevision;
export = BRevision;

106
src/becca/entities/rows.ts Normal file
View File

@ -0,0 +1,106 @@
// TODO: Booleans should probably be numbers instead (as SQLite does not have booleans.);
export interface AttachmentRow {
attachmentId?: string;
ownerId?: string;
role: string;
mime: string;
title?: string;
position?: number;
blobId?: string;
isProtected?: boolean;
dateModified?: string;
utcDateModified?: string;
utcDateScheduledForErasureSince?: string;
contentLength?: number;
content?: string;
}
export interface RevisionRow {
revisionId?: string;
noteId: string;
type: string;
mime: string;
isProtected?: boolean;
title: string;
blobId?: string;
dateLastEdited?: string;
dateCreated: string;
utcDateLastEdited?: string;
utcDateCreated: string;
utcDateModified: string;
contentLength?: number;
}
export interface RecentNoteRow {
noteId: string;
notePath: string;
utcDateCreated?: string;
}
export interface OptionRow {
name: string;
value: string;
isSynced: boolean;
utcDateModified: string;
}
export interface EtapiTokenRow {
etapiTokenId: string;
name: string;
tokenHash: string;
utcDateCreated?: string;
utcDateModified?: string;
isDeleted: boolean;
}
export interface BlobRow {
blobId: string;
content: string | Buffer;
contentLength: number;
dateModified: string;
utcDateModified: string;
}
export type AttributeType = "label" | "relation";
export interface AttributeRow {
attributeId?: string;
noteId: string;
type: AttributeType;
name: string;
position: number;
value: string;
isInheritable: boolean;
utcDateModified?: string;
}
export interface BranchRow {
branchId?: string;
noteId: string;
parentNoteId: string;
prefix: string | null;
notePosition: number;
isExpanded: boolean;
utcDateModified?: string;
}
/**
* There are many different Note types, some of which are entirely opaque to the
* end user. Those types should be used only for checking against, they are
* not for direct use.
*/
export type NoteType = ("file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code");
export interface NoteRow {
noteId: string;
title: string;
type: NoteType;
mime: string;
isProtected: boolean;
blobId: string;
dateCreated: string;
dateModified: string;
utcDateCreated: string;
utcDateModified: string;
}

View File

@ -1,12 +1,12 @@
const BAttachment = require('./entities/battachment.js');
const BAttribute = require('./entities/battribute.js');
const BBlob = require('./entities/bblob.js');
const BBranch = require('./entities/bbranch.js');
const BEtapiToken = require('./entities/betapi_token.js');
const BNote = require('./entities/bnote.js');
const BOption = require('./entities/boption.js');
const BRecentNote = require('./entities/brecent_note.js');
const BRevision = require('./entities/brevision.js');
const BAttachment = require('./entities/battachment');
const BAttribute = require('./entities/battribute');
const BBlob = require('./entities/bblob');
const BBranch = require('./entities/bbranch');
const BEtapiToken = require('./entities/betapi_token');
const BNote = require('./entities/bnote');
const BOption = require('./entities/boption');
const BRecentNote = require('./entities/brecent_note');
const BRevision = require('./entities/brevision');
const ENTITY_NAME_TO_ENTITY = {
"attachments": BAttachment,

View File

@ -1,7 +1,7 @@
const becca = require('./becca.js');
const log = require('../services/log.js');
const beccaService = require('./becca_service.js');
const dateUtils = require('../services/date_utils.js');
const becca = require('./becca');
const log = require('../services/log');
const beccaService = require('./becca_service');
const dateUtils = require('../services/date_utils');
const {JSDOM} = require("jsdom");
const DEBUG = false;

View File

@ -1,7 +0,0 @@
class NotFoundError {
constructor(message) {
this.message = message;
}
}
module.exports = NotFoundError;

View File

@ -0,0 +1,9 @@
class NotFoundError {
message: string;
constructor(message: string) {
this.message = message;
}
}
export = NotFoundError;

View File

@ -1,5 +1,7 @@
class ValidationError {
constructor(message) {
message: string;
constructor(message: string) {
this.message = message;
}
}

View File

@ -1,5 +1,5 @@
const appInfo = require('../services/app_info.js');
const eu = require('./etapi_utils.js');
const eu = require('./etapi_utils');
function register(router) {
eu.route(router, 'get', '/etapi/app-info', (req, res, next) => {

View File

@ -1,8 +1,8 @@
const becca = require('../becca/becca.js');
const eu = require('./etapi_utils.js');
const becca = require('../becca/becca');
const eu = require('./etapi_utils');
const mappers = require('./mappers.js');
const v = require('./validators.js');
const utils = require('../services/utils.js');
const utils = require('../services/utils');
function register(router) {
const ALLOWED_PROPERTIES_FOR_CREATE_ATTACHMENT = {

View File

@ -1,5 +1,5 @@
const becca = require('../becca/becca.js');
const eu = require('./etapi_utils.js');
const becca = require('../becca/becca');
const eu = require('./etapi_utils');
const mappers = require('./mappers.js');
const attributeService = require('../services/attributes.js');
const v = require('./validators.js');

View File

@ -1,6 +1,6 @@
const becca = require('../becca/becca.js');
const eu = require('./etapi_utils.js');
const passwordEncryptionService = require('../services/encryption/password_encryption.js');
const becca = require('../becca/becca');
const eu = require('./etapi_utils');
const passwordEncryptionService = require('../services/encryption/password_encryption');
const etapiTokenService = require('../services/etapi_tokens.js');
function register(router, loginMiddleware) {

View File

@ -1,4 +1,4 @@
const eu = require('./etapi_utils.js');
const eu = require('./etapi_utils');
const backupService = require('../services/backup.js');
function register(router) {

View File

@ -1,8 +1,8 @@
const becca = require('../becca/becca.js');
const eu = require('./etapi_utils.js');
const becca = require('../becca/becca');
const eu = require('./etapi_utils');
const mappers = require('./mappers.js');
const BBranch = require('../becca/entities/bbranch.js');
const entityChangesService = require('../services/entity_changes.js');
const BBranch = require('../becca/entities/bbranch');
const entityChangesService = require('../services/entity_changes');
const v = require('./validators.js');
function register(router) {

View File

@ -1,9 +1,9 @@
const cls = require('../services/cls.js');
const sql = require('../services/sql.js');
const log = require('../services/log.js');
const becca = require('../becca/becca.js');
const cls = require('../services/cls');
const sql = require('../services/sql');
const log = require('../services/log');
const becca = require('../becca/becca');
const etapiTokenService = require('../services/etapi_tokens.js');
const config = require('../services/config.js');
const config = require('../services/config');
const GENERIC_CODE = "GENERIC";
const noAuthentication = config.General && config.General.noAuthentication === true;

View File

@ -1,9 +1,9 @@
const becca = require('../becca/becca.js');
const utils = require('../services/utils.js');
const eu = require('./etapi_utils.js');
const becca = require('../becca/becca');
const utils = require('../services/utils');
const eu = require('./etapi_utils');
const mappers = require('./mappers.js');
const noteService = require('../services/notes.js');
const TaskContext = require('../services/task_context.js');
const TaskContext = require('../services/task_context');
const v = require('./validators.js');
const searchService = require('../services/search/services/search.js');
const SearchContext = require('../services/search/search_context.js');

View File

@ -1,6 +1,6 @@
const specialNotesService = require('../services/special_notes.js');
const dateNotesService = require('../services/date_notes.js');
const eu = require('./etapi_utils.js');
const eu = require('./etapi_utils');
const mappers = require('./mappers.js');
const getDateInvalidError = date => new eu.EtapiError(400, "DATE_INVALID", `Date "${date}" is not valid.`);

View File

@ -1,5 +1,5 @@
const noteTypeService = require('../services/note_types.js');
const dateUtils = require('../services/date_utils.js');
const dateUtils = require('../services/date_utils');
function mandatory(obj) {
if (obj === undefined ) {
@ -64,7 +64,7 @@ function isNoteId(obj) {
return;
}
const becca = require('../becca/becca.js');
const becca = require('../becca/becca');
if (typeof obj !== 'string') {
return `'${obj}' is not a valid noteId`;

View File

@ -1,6 +1,6 @@
const becca = require('../../becca/becca.js');
const blobService = require('../../services/blob.js');
const ValidationError = require('../../errors/validation_error.js');
const becca = require('../../becca/becca');
const blobService = require('../../services/blob');
const ValidationError = require('../../errors/validation_error');
const imageService = require("../../services/image.js");
function getAttachmentBlob(req) {

View File

@ -1,11 +1,11 @@
"use strict";
const sql = require('../../services/sql.js');
const log = require('../../services/log.js');
const sql = require('../../services/sql');
const log = require('../../services/log');
const attributeService = require('../../services/attributes.js');
const BAttribute = require('../../becca/entities/battribute.js');
const becca = require('../../becca/becca.js');
const ValidationError = require('../../errors/validation_error.js');
const BAttribute = require('../../becca/entities/battribute');
const becca = require('../../becca/becca');
const ValidationError = require('../../errors/validation_error');
function getEffectiveNoteAttributes(req) {
const note = becca.getNote(req.params.noteId);

View File

@ -2,10 +2,10 @@
const beccaService = require('../../becca/becca_service.js');
const searchService = require('../../services/search/services/search.js');
const log = require('../../services/log.js');
const utils = require('../../services/utils.js');
const cls = require('../../services/cls.js');
const becca = require('../../becca/becca.js');
const log = require('../../services/log');
const utils = require('../../services/utils');
const cls = require('../../services/cls');
const becca = require('../../becca/becca');
function getAutocomplete(req) {
const query = req.query.query.trim();

View File

@ -1,8 +1,8 @@
"use strict";
const fs = require('fs');
const dateUtils = require('../../services/date_utils.js');
const {LOG_DIR} = require('../../services/data_dir.js');
const dateUtils = require('../../services/date_utils');
const {LOG_DIR} = require('../../services/data_dir');
function getBackendLog() {
const file = `${LOG_DIR}/trilium-${dateUtils.localNowDate()}.log`;

View File

@ -1,16 +1,16 @@
"use strict";
const sql = require('../../services/sql.js');
const utils = require('../../services/utils.js');
const entityChangesService = require('../../services/entity_changes.js');
const sql = require('../../services/sql');
const utils = require('../../services/utils');
const entityChangesService = require('../../services/entity_changes');
const treeService = require('../../services/tree.js');
const eraseService = require('../../services/erase.js');
const becca = require('../../becca/becca.js');
const TaskContext = require('../../services/task_context.js');
const becca = require('../../becca/becca');
const TaskContext = require('../../services/task_context');
const branchService = require('../../services/branches.js');
const log = require('../../services/log.js');
const ValidationError = require('../../errors/validation_error.js');
const eventService = require("../../services/events.js");
const log = require('../../services/log');
const ValidationError = require('../../errors/validation_error');
const eventService = require("../../services/events");
/**
* Code in this file deals with moving and cloning branches. The relationship between note and parent note is unique

View File

@ -1,4 +1,4 @@
const becca = require('../../becca/becca.js');
const becca = require('../../becca/becca');
const bulkActionService = require('../../services/bulk_actions.js');
function execute(req) {

View File

@ -4,12 +4,12 @@ const attributeService = require('../../services/attributes.js');
const cloneService = require('../../services/cloning.js');
const noteService = require('../../services/notes.js');
const dateNoteService = require('../../services/date_notes.js');
const dateUtils = require('../../services/date_utils.js');
const dateUtils = require('../../services/date_utils');
const imageService = require('../../services/image.js');
const appInfo = require('../../services/app_info.js');
const ws = require('../../services/ws.js');
const log = require('../../services/log.js');
const utils = require('../../services/utils.js');
const ws = require('../../services/ws');
const log = require('../../services/log');
const utils = require('../../services/utils');
const path = require('path');
const htmlSanitizer = require('../../services/html_sanitizer.js');
const {formatAttrForSearch} = require('../../services/attribute_formatter.js');

View File

@ -1,7 +1,7 @@
"use strict";
const sql = require('../../services/sql.js');
const log = require('../../services/log.js');
const sql = require('../../services/sql');
const log = require('../../services/log');
const backupService = require('../../services/backup.js');
const anonymizationService = require('../../services/anonymization.js');
const consistencyChecksService = require('../../services/consistency_checks.js');

View File

@ -3,10 +3,10 @@
const zipExportService = require('../../services/export/zip.js');
const singleExportService = require('../../services/export/single.js');
const opmlExportService = require('../../services/export/opml.js');
const becca = require('../../becca/becca.js');
const TaskContext = require('../../services/task_context.js');
const log = require('../../services/log.js');
const NotFoundError = require('../../errors/not_found_error.js');
const becca = require('../../becca/becca');
const TaskContext = require('../../services/task_context');
const log = require('../../services/log');
const NotFoundError = require('../../errors/not_found_error');
function exportBranch(req, res) {
const {branchId, type, format, version, taskId} = req.params;

View File

@ -1,16 +1,16 @@
"use strict";
const protectedSessionService = require('../../services/protected_session.js');
const utils = require('../../services/utils.js');
const log = require('../../services/log.js');
const protectedSessionService = require('../../services/protected_session');
const utils = require('../../services/utils');
const log = require('../../services/log');
const noteService = require('../../services/notes.js');
const tmp = require('tmp');
const fs = require('fs');
const { Readable } = require('stream');
const chokidar = require('chokidar');
const ws = require('../../services/ws.js');
const becca = require('../../becca/becca.js');
const ValidationError = require('../../errors/validation_error.js');
const ws = require('../../services/ws');
const becca = require('../../becca/becca');
const ValidationError = require('../../errors/validation_error');
function updateFile(req) {
const note = becca.getNoteOrThrow(req.params.noteId);

View File

@ -1,4 +1,4 @@
const optionService = require('../../services/options.js');
const optionService = require('../../services/options');
function getFontCss(req, res) {
res.setHeader('Content-Type', 'text/css');

View File

@ -1,8 +1,8 @@
"use strict";
const imageService = require('../../services/image.js');
const becca = require('../../becca/becca.js');
const RESOURCE_DIR = require('../../services/resource_dir.js').RESOURCE_DIR;
const becca = require('../../becca/becca');
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
const fs = require('fs');
function returnImageFromNote(req, res) {

View File

@ -4,13 +4,13 @@ const enexImportService = require('../../services/import/enex.js');
const opmlImportService = require('../../services/import/opml.js');
const zipImportService = require('../../services/import/zip.js');
const singleImportService = require('../../services/import/single.js');
const cls = require('../../services/cls.js');
const cls = require('../../services/cls');
const path = require('path');
const becca = require('../../becca/becca.js');
const becca = require('../../becca/becca');
const beccaLoader = require('../../becca/becca_loader.js');
const log = require('../../services/log.js');
const TaskContext = require('../../services/task_context.js');
const ValidationError = require('../../errors/validation_error.js');
const log = require('../../services/log');
const TaskContext = require('../../services/task_context');
const ValidationError = require('../../errors/validation_error');
async function importNotesToBranch(req) {
const {parentNoteId} = req.params;

View File

@ -1,7 +1,7 @@
"use strict";
const keyboardActions = require('../../services/keyboard_actions.js');
const becca = require('../../becca/becca.js');
const becca = require('../../becca/becca');
function getKeyboardActions() {
return keyboardActions.getKeyboardActions();

View File

@ -1,16 +1,16 @@
"use strict";
const options = require('../../services/options.js');
const utils = require('../../services/utils.js');
const dateUtils = require('../../services/date_utils.js');
const instanceId = require('../../services/instance_id.js');
const passwordEncryptionService = require('../../services/encryption/password_encryption.js');
const protectedSessionService = require('../../services/protected_session.js');
const options = require('../../services/options');
const utils = require('../../services/utils');
const dateUtils = require('../../services/date_utils');
const instanceId = require('../../services/instance_id');
const passwordEncryptionService = require('../../services/encryption/password_encryption');
const protectedSessionService = require('../../services/protected_session');
const appInfo = require('../../services/app_info.js');
const eventService = require('../../services/events.js');
const eventService = require('../../services/events');
const sqlInit = require('../../services/sql_init.js');
const sql = require('../../services/sql.js');
const ws = require('../../services/ws.js');
const sql = require('../../services/sql');
const ws = require('../../services/ws');
const etapiTokenService = require('../../services/etapi_tokens.js');
function loginSync(req) {

View File

@ -1,6 +1,6 @@
"use strict";
const becca = require('../../becca/becca.js');
const becca = require('../../becca/becca');
const { JSDOM } = require("jsdom");
function buildDescendantCountMap(noteIdsToCount) {

View File

@ -3,13 +3,13 @@
const noteService = require('../../services/notes.js');
const eraseService = require('../../services/erase.js');
const treeService = require('../../services/tree.js');
const sql = require('../../services/sql.js');
const utils = require('../../services/utils.js');
const log = require('../../services/log.js');
const TaskContext = require('../../services/task_context.js');
const becca = require('../../becca/becca.js');
const ValidationError = require('../../errors/validation_error.js');
const blobService = require('../../services/blob.js');
const sql = require('../../services/sql');
const utils = require('../../services/utils');
const log = require('../../services/log');
const TaskContext = require('../../services/task_context');
const becca = require('../../becca/becca');
const ValidationError = require('../../errors/validation_error');
const blobService = require('../../services/blob');
function getNote(req) {
return becca.getNoteOrThrow(req.params.noteId);

View File

@ -1,9 +1,9 @@
"use strict";
const optionService = require('../../services/options.js');
const log = require('../../services/log.js');
const optionService = require('../../services/options');
const log = require('../../services/log');
const searchService = require('../../services/search/services/search.js');
const ValidationError = require('../../errors/validation_error.js');
const ValidationError = require('../../errors/validation_error');
// options allowed to be updated directly in the Options dialog
const ALLOWED_OPTIONS = new Set([

View File

@ -1,4 +1,4 @@
const becca = require('../../becca/becca.js');
const becca = require('../../becca/becca');
const markdownService = require('../../services/import/markdown.js');
function getIconUsage() {

View File

@ -1,7 +1,7 @@
"use strict";
const passwordService = require('../../services/encryption/password.js');
const ValidationError = require('../../errors/validation_error.js');
const passwordService = require('../../services/encryption/password');
const ValidationError = require('../../errors/validation_error');
function changePassword(req) {
if (passwordService.isPasswordSet()) {

View File

@ -1,9 +1,9 @@
"use strict";
const sql = require('../../services/sql.js');
const protectedSessionService = require('../../services/protected_session.js');
const sql = require('../../services/sql');
const protectedSessionService = require('../../services/protected_session');
const noteService = require('../../services/notes.js');
const becca = require('../../becca/becca.js');
const becca = require('../../becca/becca');
function getRecentChanges(req) {
const {ancestorNoteId} = req.params;

View File

@ -1,8 +1,8 @@
"use strict";
const BRecentNote = require('../../becca/entities/brecent_note.js');
const sql = require('../../services/sql.js');
const dateUtils = require('../../services/date_utils.js');
const BRecentNote = require('../../becca/entities/brecent_note');
const sql = require('../../services/sql');
const dateUtils = require('../../services/date_utils');
function addRecentNote(req) {
new BRecentNote({

View File

@ -1,5 +1,5 @@
const becca = require('../../becca/becca.js');
const sql = require('../../services/sql.js');
const becca = require('../../becca/becca');
const sql = require('../../services/sql');
function getRelationMap(req) {
const {relationMapNoteId, noteIds} = req.body;

View File

@ -2,12 +2,12 @@
const beccaService = require('../../becca/becca_service.js');
const revisionService = require('../../services/revisions.js');
const utils = require('../../services/utils.js');
const sql = require('../../services/sql.js');
const cls = require('../../services/cls.js');
const utils = require('../../services/utils');
const sql = require('../../services/sql');
const cls = require('../../services/cls');
const path = require('path');
const becca = require('../../becca/becca.js');
const blobService = require('../../services/blob.js');
const becca = require('../../becca/becca');
const blobService = require('../../services/blob');
const eraseService = require("../../services/erase.js");
function getRevisionBlob(req) {

View File

@ -2,9 +2,9 @@
const scriptService = require('../../services/script.js');
const attributeService = require('../../services/attributes.js');
const becca = require('../../becca/becca.js');
const becca = require('../../becca/becca');
const syncService = require('../../services/sync.js');
const sql = require('../../services/sql.js');
const sql = require('../../services/sql');
// The async/await here is very confusing, because the body.script may, but may not be async. If it is async, then we
// need to await it and make the complete response including metadata available in a Promise, so that the route detects

View File

@ -1,12 +1,12 @@
"use strict";
const becca = require('../../becca/becca.js');
const becca = require('../../becca/becca');
const SearchContext = require('../../services/search/search_context.js');
const searchService = require('../../services/search/services/search.js');
const bulkActionService = require('../../services/bulk_actions.js');
const cls = require('../../services/cls.js');
const cls = require('../../services/cls');
const {formatAttrForSearch} = require('../../services/attribute_formatter.js');
const ValidationError = require('../../errors/validation_error.js');
const ValidationError = require('../../errors/validation_error');
function searchFromNote(req) {
const note = becca.getNoteOrThrow(req.params.noteId);

View File

@ -3,7 +3,7 @@
const imageType = require('image-type');
const imageService = require('../../services/image.js');
const noteService = require('../../services/notes.js');
const {sanitizeAttributeName} = require('../../services/sanitize_attribute_name.js');
const {sanitizeAttributeName} = require('../../services/sanitize_attribute_name');
const specialNotesService = require('../../services/special_notes.js');
function uploadImage(req) {

View File

@ -2,7 +2,7 @@
const sqlInit = require('../../services/sql_init.js');
const setupService = require('../../services/setup.js');
const log = require('../../services/log.js');
const log = require('../../services/log');
const appInfo = require('../../services/app_info.js');
function getStatus() {

View File

@ -1,7 +1,7 @@
"use strict";
const similarityService = require('../../becca/similarity.js');
const becca = require('../../becca/becca.js');
const becca = require('../../becca/becca');
async function getSimilarNotes(req) {
const noteId = req.params.noteId;

View File

@ -1,10 +1,10 @@
"use strict";
const dateNoteService = require('../../services/date_notes.js');
const sql = require('../../services/sql.js');
const cls = require('../../services/cls.js');
const sql = require('../../services/sql');
const cls = require('../../services/cls');
const specialNotesService = require('../../services/special_notes.js');
const becca = require('../../becca/becca.js');
const becca = require('../../becca/becca');
function getInboxNote(req) {
return specialNotesService.getInboxNote(req.params.date);

View File

@ -1,7 +1,7 @@
"use strict";
const sql = require('../../services/sql.js');
const becca = require('../../becca/becca.js');
const sql = require('../../services/sql');
const becca = require('../../becca/becca');
function getSchema() {
const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`);

View File

@ -1,5 +1,5 @@
const sql = require('../../services/sql.js');
const becca = require('../../becca/becca.js');
const sql = require('../../services/sql');
const becca = require('../../becca/becca');
function getNoteSize(req) {
const {noteId} = req.params;

View File

@ -2,15 +2,15 @@
const syncService = require('../../services/sync.js');
const syncUpdateService = require('../../services/sync_update.js');
const entityChangesService = require('../../services/entity_changes.js');
const sql = require('../../services/sql.js');
const entityChangesService = require('../../services/entity_changes');
const sql = require('../../services/sql');
const sqlInit = require('../../services/sql_init.js');
const optionService = require('../../services/options.js');
const optionService = require('../../services/options');
const contentHashService = require('../../services/content_hash.js');
const log = require('../../services/log.js');
const log = require('../../services/log');
const syncOptions = require('../../services/sync_options.js');
const utils = require('../../services/utils.js');
const ws = require('../../services/ws.js');
const utils = require('../../services/utils');
const ws = require('../../services/ws');
async function testSync() {
try {

View File

@ -1,8 +1,8 @@
"use strict";
const becca = require('../../becca/becca.js');
const log = require('../../services/log.js');
const NotFoundError = require('../../errors/not_found_error.js');
const becca = require('../../becca/becca');
const log = require('../../services/log');
const NotFoundError = require('../../errors/not_found_error');
function getNotesAndBranchesAndAttributes(noteIds) {
noteIds = new Set(noteIds);

View File

@ -1,7 +1,7 @@
const assetPath = require('../services/asset_path.js');
const path = require("path");
const express = require("express");
const env = require('../services/env.js');
const env = require('../services/env');
const persistentCacheStatic = (root, options) => {
if (!env.isDev()) {

View File

@ -1,9 +1,9 @@
const log = require('../services/log.js');
const log = require('../services/log');
const fileService = require('./api/files.js');
const scriptService = require('../services/script.js');
const cls = require('../services/cls.js');
const sql = require('../services/sql.js');
const becca = require('../becca/becca.js');
const cls = require('../services/cls');
const sql = require('../services/sql');
const becca = require('../becca/becca');
function handleRequest(req, res) {
// express puts content after first slash into 0 index element

View File

@ -1,4 +1,4 @@
const log = require('../services/log.js');
const log = require('../services/log');
function register(app) {
app.use((err, req, res, next) => {

View File

@ -1,13 +1,13 @@
"use strict";
const sql = require('../services/sql.js');
const sql = require('../services/sql');
const attributeService = require('../services/attributes.js');
const config = require('../services/config.js');
const optionService = require('../services/options.js');
const log = require('../services/log.js');
const env = require('../services/env.js');
const utils = require('../services/utils.js');
const protectedSessionService = require('../services/protected_session.js');
const config = require('../services/config');
const optionService = require('../services/options');
const log = require('../services/log');
const env = require('../services/env');
const utils = require('../services/utils');
const protectedSessionService = require('../services/protected_session');
const packageJson = require('../../package.json');
const assetPath = require('../services/asset_path.js');
const appPath = require('../services/app_path.js');

View File

@ -1,13 +1,13 @@
"use strict";
const utils = require('../services/utils.js');
const optionService = require('../services/options.js');
const myScryptService = require('../services/encryption/my_scrypt.js');
const log = require('../services/log.js');
const passwordService = require('../services/encryption/password.js');
const utils = require('../services/utils');
const optionService = require('../services/options');
const myScryptService = require('../services/encryption/my_scrypt');
const log = require('../services/log');
const passwordService = require('../services/encryption/password');
const assetPath = require('../services/asset_path.js');
const appPath = require('../services/app_path.js');
const ValidationError = require('../errors/validation_error.js');
const ValidationError = require('../errors/validation_error');
function loginPage(req, res) {
res.render('login', {

View File

@ -1,20 +1,20 @@
"use strict";
const utils = require('../services/utils.js');
const utils = require('../services/utils');
const multer = require('multer');
const log = require('../services/log.js');
const log = require('../services/log');
const express = require('express');
const router = express.Router();
const auth = require('../services/auth.js');
const cls = require('../services/cls.js');
const sql = require('../services/sql.js');
const entityChangesService = require('../services/entity_changes.js');
const cls = require('../services/cls');
const sql = require('../services/sql');
const entityChangesService = require('../services/entity_changes');
const csurf = require('csurf');
const { createPartialContentHandler } = require("express-partial-content");
const rateLimit = require("express-rate-limit");
const AbstractBeccaEntity = require('../becca/entities/abstract_becca_entity.js');
const NotFoundError = require('../errors/not_found_error.js');
const ValidationError = require('../errors/validation_error.js');
const AbstractBeccaEntity = require('../becca/entities/abstract_becca_entity');
const NotFoundError = require('../errors/not_found_error');
const ValidationError = require('../errors/validation_error');
// page routes
const setupRoute = require('./setup.js');
@ -31,7 +31,7 @@ const cloningApiRoute = require('./api/cloning.js');
const revisionsApiRoute = require('./api/revisions.js');
const recentChangesApiRoute = require('./api/recent_changes.js');
const optionsApiRoute = require('./api/options.js');
const passwordApiRoute = require('./api/password.js');
const passwordApiRoute = require('./api/password');
const syncApiRoute = require('./api/sync.js');
const loginApiRoute = require('./api/login.js');
const recentNotesRoute = require('./api/recent_notes.js');
@ -39,7 +39,7 @@ const appInfoRoute = require('./api/app_info.js');
const exportRoute = require('./api/export.js');
const importRoute = require('./api/import.js');
const setupApiRoute = require('./api/setup.js');
const sqlRoute = require('./api/sql.js');
const sqlRoute = require('./api/sql');
const databaseRoute = require('./api/database.js');
const imageRoute = require('./api/image.js');
const attributesRoute = require('./api/attributes.js');

View File

@ -1,6 +1,6 @@
const session = require("express-session");
const sessionSecret = require('../services/session_secret.js');
const dataDir = require('../services/data_dir.js');
const dataDir = require('../services/data_dir');
const FileStore = require('session-file-store')(session);
const sessionParser = session({

View File

@ -2,7 +2,7 @@
const sqlInit = require('../services/sql_init.js');
const setupService = require('../services/setup.js');
const utils = require('../services/utils.js');
const utils = require('../services/utils');
const assetPath = require('../services/asset_path.js');
const appPath = require('../services/app_path.js');

View File

@ -1,9 +1,9 @@
const BUILTIN_ATTRIBUTES = require('./builtin_attributes.js');
const fs = require("fs-extra");
const dataDir = require('./data_dir.js');
const dateUtils = require('./date_utils.js');
const dataDir = require('./data_dir');
const dateUtils = require('./date_utils');
const Database = require("better-sqlite3");
const sql = require('./sql.js');
const sql = require('./sql');
const path = require("path");
function getFullAnonymizationScript() {

View File

@ -1,12 +1,12 @@
"use strict";
const path = require('path');
const {ELECTRON_APP_ROOT_DIR} = require('./resource_dir.js');
const log = require('./log.js');
const {ELECTRON_APP_ROOT_DIR} = require('./resource_dir');
const log = require('./log');
const os = require('os');
const fs = require('fs');
const config = require('./config.js');
const utils = require('./utils.js');
const config = require('./config');
const utils = require('./utils');
const template = `[Desktop Entry]
Type=Application

View File

@ -2,7 +2,7 @@
const build = require('./build.js');
const packageJson = require('../../package.json');
const {TRILIUM_DATA_DIR} = require('./data_dir.js');
const {TRILIUM_DATA_DIR} = require('./data_dir');
const APP_DB_VERSION = 228;
const SYNC_VERSION = 32;

View File

@ -1,5 +1,5 @@
const assetPath = require('./asset_path.js');
const env = require('./env.js');
const env = require('./env');
module.exports = env.isDev()
? assetPath + "/app"

View File

@ -1,9 +1,9 @@
"use strict";
const searchService = require('./search/services/search.js');
const sql = require('./sql.js');
const becca = require('../becca/becca.js');
const BAttribute = require('../becca/entities/battribute.js');
const sql = require('./sql');
const becca = require('../becca/becca');
const BAttribute = require('../becca/entities/battribute');
const {formatAttrForSearch} = require('./attribute_formatter.js');
const BUILTIN_ATTRIBUTES = require('./builtin_attributes.js');

View File

@ -1,12 +1,12 @@
"use strict";
const etapiTokenService = require('./etapi_tokens.js');
const log = require('./log.js');
const log = require('./log');
const sqlInit = require('./sql_init.js');
const utils = require('./utils.js');
const passwordEncryptionService = require('./encryption/password_encryption.js');
const config = require('./config.js');
const passwordService = require('./encryption/password.js');
const utils = require('./utils');
const passwordEncryptionService = require('./encryption/password_encryption');
const config = require('./config');
const passwordService = require('./encryption/password');
const noAuthentication = config.General && config.General.noAuthentication === true;

View File

@ -1,11 +1,11 @@
const log = require('./log.js');
const log = require('./log');
const noteService = require('./notes.js');
const sql = require('./sql.js');
const utils = require('./utils.js');
const sql = require('./sql');
const utils = require('./utils');
const attributeService = require('./attributes.js');
const dateNoteService = require('./date_notes.js');
const treeService = require('./tree.js');
const config = require('./config.js');
const config = require('./config');
const axios = require('axios');
const dayjs = require('dayjs');
const xml2js = require('xml2js');
@ -13,15 +13,15 @@ const cloningService = require('./cloning.js');
const appInfo = require('./app_info.js');
const searchService = require('./search/services/search.js');
const SearchContext = require('./search/search_context.js');
const becca = require('../becca/becca.js');
const ws = require('./ws.js');
const becca = require('../becca/becca');
const ws = require('./ws');
const SpacedUpdate = require('./spaced_update.js');
const specialNotesService = require('./special_notes.js');
const branchService = require('./branches.js');
const exportService = require('./export/zip.js');
const syncMutex = require('./sync_mutex.js');
const syncMutex = require('./sync_mutex');
const backupService = require('./backup.js');
const optionsService = require('./options.js');
const optionsService = require('./options');
/**

View File

@ -1,13 +1,13 @@
"use strict";
const dateUtils = require('./date_utils.js');
const optionService = require('./options.js');
const dateUtils = require('./date_utils');
const optionService = require('./options');
const fs = require('fs-extra');
const dataDir = require('./data_dir.js');
const log = require('./log.js');
const syncMutexService = require('./sync_mutex.js');
const cls = require('./cls.js');
const sql = require('./sql.js');
const dataDir = require('./data_dir');
const log = require('./log');
const syncMutexService = require('./sync_mutex');
const cls = require('./cls');
const sql = require('./sql');
const path = require('path');
function getExistingBackups() {

View File

@ -0,0 +1,5 @@
export interface Blob {
blobId: string;
content: string | Buffer;
utcDateModified: string;
}

View File

@ -1,9 +1,10 @@
const becca = require('../becca/becca.js');
const NotFoundError = require('../errors/not_found_error.js');
const protectedSessionService = require('./protected_session.js');
const utils = require('./utils.js');
import becca = require('../becca/becca');
import NotFoundError = require('../errors/not_found_error');
import protectedSessionService = require('./protected_session');
import utils = require('./utils');
import type { Blob } from "./blob-interface";
function getBlobPojo(entityName, entityId) {
function getBlobPojo(entityName: string, entityId: string) {
const entity = becca.getEntity(entityName, entityId);
if (!entity) {
throw new NotFoundError(`Entity ${entityName} '${entityId}' was not found.`);
@ -19,13 +20,13 @@ function getBlobPojo(entityName, entityId) {
if (!entity.hasStringContent()) {
pojo.content = null;
} else {
pojo.content = processContent(pojo.content, entity.isProtected, true);
pojo.content = processContent(pojo.content, !!entity.isProtected, true);
}
return pojo;
}
function processContent(content, isProtected, isStringContent) {
function processContent(content: Buffer | string | null, isProtected: boolean, isStringContent: boolean) {
if (isProtected) {
if (protectedSessionService.isProtectedSessionAvailable()) {
content = content === null ? null : protectedSessionService.decrypt(content);
@ -48,11 +49,11 @@ function processContent(content, isProtected, isStringContent) {
}
}
function calculateContentHash({blobId, content}) {
function calculateContentHash({blobId, content}: Blob) {
return utils.hash(`${blobId}|${content.toString()}`);
}
module.exports = {
export = {
getBlobPojo,
processContent,
calculateContentHash

View File

@ -1,5 +1,5 @@
const treeService = require('./tree.js');
const sql = require('./sql.js');
const sql = require('./sql');
function moveBranchToNote(branchToMove, targetParentNoteId) {
if (branchToMove.parentNoteId === targetParentNoteId) {

View File

@ -1,9 +1,9 @@
const log = require('./log.js');
const log = require('./log');
const revisionService = require('./revisions.js');
const becca = require('../becca/becca.js');
const becca = require('../becca/becca');
const cloningService = require('./cloning.js');
const branchService = require('./branches.js');
const utils = require('./utils.js');
const utils = require('./utils');
const eraseService = require("./erase.js");
const ACTION_HANDLERS = {

View File

@ -1,11 +1,11 @@
"use strict";
const sql = require('./sql.js');
const eventChangesService = require('./entity_changes.js');
const sql = require('./sql');
const eventChangesService = require('./entity_changes');
const treeService = require('./tree.js');
const BBranch = require('../becca/entities/bbranch.js');
const becca = require('../becca/becca.js');
const log = require('./log.js');
const BBranch = require('../becca/entities/bbranch');
const becca = require('../becca/becca');
const log = require('./log');
function cloneNoteToParentNote(noteId, parentNoteId, prefix = null) {
if (!(noteId in becca.notes) || !(parentNoteId in becca.notes)) {

View File

@ -1,26 +1,29 @@
const clsHooked = require('cls-hooked');
import clsHooked = require('cls-hooked');
import { EntityChange } from './entity_changes_interface';
const namespace = clsHooked.createNamespace("trilium");
function init(callback) {
type Callback = (...args: any[]) => any;
function init(callback: Callback) {
return namespace.runAndReturn(callback);
}
function wrap(callback) {
function wrap(callback: Callback) {
return () => {
try {
init(callback);
}
catch (e) {
catch (e: any) {
console.log(`Error occurred: ${e.message}: ${e.stack}`);
}
}
}
function get(key) {
function get(key: string) {
return namespace.get(key);
}
function set(key, value) {
function set(key: string, value: any) {
namespace.set(key, value);
}
@ -48,7 +51,7 @@ function isEntityEventsDisabled() {
return !!namespace.get('disableEntityEvents');
}
function setMigrationRunning(running) {
function setMigrationRunning(running: boolean) {
namespace.set('migrationRunning', !!running);
}
@ -56,7 +59,7 @@ function isMigrationRunning() {
return !!namespace.get('migrationRunning');
}
function disableSlowQueryLogging(disable) {
function disableSlowQueryLogging(disable: boolean) {
namespace.set('disableSlowQueryLogging', disable);
}
@ -72,7 +75,7 @@ function getAndClearEntityChangeIds() {
return entityChangeIds;
}
function putEntityChange(entityChange) {
function putEntityChange(entityChange: EntityChange) {
if (namespace.get('ignoreEntityChangeIds')) {
return;
}
@ -93,7 +96,7 @@ function ignoreEntityChangeIds() {
namespace.set('ignoreEntityChangeIds', true);
}
module.exports = {
export = {
init,
wrap,
get,

View File

@ -1,10 +1,10 @@
"use strict";
const ini = require('ini');
const fs = require('fs');
const dataDir = require('./data_dir.js');
const path = require('path');
const resourceDir = require('./resource_dir.js');
import ini = require('ini');
import fs = require('fs');
import dataDir = require('./data_dir');
import path = require('path');
import resourceDir = require('./resource_dir');
const configSampleFilePath = path.resolve(resourceDir.RESOURCE_DIR, "config-sample.ini");
@ -16,4 +16,4 @@ if (!fs.existsSync(dataDir.CONFIG_INI_PATH)) {
const config = ini.parse(fs.readFileSync(dataDir.CONFIG_INI_PATH, 'utf-8'));
module.exports = config;
export = config;

View File

@ -1,19 +1,19 @@
"use strict";
const sql = require('./sql.js');
const sql = require('./sql');
const sqlInit = require('./sql_init.js');
const log = require('./log.js');
const ws = require('./ws.js');
const syncMutexService = require('./sync_mutex.js');
const cls = require('./cls.js');
const entityChangesService = require('./entity_changes.js');
const optionsService = require('./options.js');
const BBranch = require('../becca/entities/bbranch.js');
const log = require('./log');
const ws = require('./ws');
const syncMutexService = require('./sync_mutex');
const cls = require('./cls');
const entityChangesService = require('./entity_changes');
const optionsService = require('./options');
const BBranch = require('../becca/entities/bbranch');
const revisionService = require('./revisions.js');
const becca = require('../becca/becca.js');
const utils = require('../services/utils.js');
const becca = require('../becca/becca');
const utils = require('../services/utils');
const eraseService = require('../services/erase.js');
const {sanitizeAttributeName} = require('./sanitize_attribute_name.js');
const {sanitizeAttributeName} = require('./sanitize_attribute_name');
const noteTypes = require('../services/note_types.js').getNoteTypeNames();
class ConsistencyChecks {

Some files were not shown because too many files have changed in this diff Show More