1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-01-29 07:46:13 +02:00

Compare commits

..

10 Commits

Author SHA1 Message Date
Laurent Cozic
051d628250 tsconfig 2023-06-13 11:10:07 +01:00
Laurent Cozic
d8f365834b update 2023-06-13 11:01:45 +01:00
Laurent Cozic
6dc6acc644 update 2023-06-13 10:54:44 +01:00
Laurent Cozic
30e1164df9 Merge branch 'dev' into user_data 2023-06-12 17:31:20 +01:00
Laurent Cozic
a8821dfcb5 test 2023-06-12 17:29:57 +01:00
Laurent Cozic
54b15a1917 Plugins: Updated types 2023-06-12 17:09:09 +01:00
Laurent Cozic
e2d27275db Plugins: Updated types 2023-06-12 17:04:46 +01:00
Laurent Cozic
b90cb89e0f fix 2023-06-12 17:02:49 +01:00
Laurent Cozic
a6516c5f14 Plugins: Updated types 2023-06-12 16:52:09 +01:00
Laurent Cozic
249a744e9d init 2023-06-12 16:49:54 +01:00
99 changed files with 749 additions and 853 deletions

View File

@@ -225,7 +225,6 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.j
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
@@ -357,7 +356,6 @@ packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.js
packages/app-desktop/tools/notarizeMacApp.js
packages/app-desktop/utils/markupLanguageUtils.js
packages/app-mobile/PluginAssetsLoader.js
packages/app-mobile/components/ActionButton.js

View File

@@ -33,5 +33,5 @@ jobs:
#custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign'
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
lock-pullrequest-aftermerge: false
#lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
#use-dco-flag: true - If you are using DCO instead of CLA

View File

@@ -176,35 +176,3 @@ jobs:
# Basic test to ensure that the created build is valid. It should exit with
# code 0 if it works.
docker run joplin/server:0.0.0-beta node dist/app.js migrate list
- name: Check HTTP request
run: |
# Need to pass environment variables:
docker run -p 22300:22300 joplin/server:0.0.0-beta node dist/app.js --env dev &
# Wait for server to start
sleep 10
# Check if status code is correct
# if the actual_status DOES NOT include the expected_status
# it exits the process with code 1
expected_status="HTTP/1.1 200 OK"
actual_status=$(curl -I -X GET http://localhost:22300/api/ping | head -n 1)
if [[ ! "$actual_status" =~ "$expected_status" ]]; then
echo 'Failed while checking the status code after request to /api/ping'
echo 'expected: ' $expected_status
echo 'actual: ' $actual_status
exit 1;
fi
# Check if the body response is correct
# if the actual_body is different of expected_body exit with code 1
expected_body='{"status":"ok","message":"Joplin Server is running"}'
actual_body=$(curl http://localhost:22300/api/ping)
if [[ "$actual_body" != "$expected_body" ]]; then
echo 'Failed while checking the body response after request to /api/ping'
exit 1;
fi

2
.gitignore vendored
View File

@@ -210,7 +210,6 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.j
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
@@ -342,7 +341,6 @@ packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.js
packages/app-desktop/tools/notarizeMacApp.js
packages/app-desktop/utils/markupLanguageUtils.js
packages/app-mobile/PluginAssetsLoader.js
packages/app-mobile/components/ActionButton.js

View File

@@ -36,7 +36,7 @@ index 6afcbbf0cc8ca2d69dd78077d61e59a90b2136bb..9f8d72b4ec5b2b3d290975d6a255917c
def kotlin_version = getExtOrDefault('kotlinVersion')
diff --git a/android/src/main/java/com/reactnativevosk/VoskModule.kt b/android/src/main/java/com/reactnativevosk/VoskModule.kt
index 0e2b6595b1b2cf1ee01c6c64239c4b0ea37fce19..5a8539b9cce8951967640dba755e29a4e3ff404a 100644
index 0e2b6595b1b2cf1ee01c6c64239c4b0ea37fce19..f3da440bc2863a59db6d2d1691c54d8d4870cb3f 100644
--- a/android/src/main/java/com/reactnativevosk/VoskModule.kt
+++ b/android/src/main/java/com/reactnativevosk/VoskModule.kt
@@ -19,13 +19,25 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
@@ -66,25 +66,7 @@ index 0e2b6595b1b2cf1ee01c6c64239c4b0ea37fce19..5a8539b9cce8951967640dba755e29a4
sendEvent("onResult", text)
}
}
@@ -93,12 +105,11 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
@ReactMethod
fun loadModel(path: String, promise: Promise) {
cleanModel();
- StorageService.unpack(context, path, "models",
- { model: Model? ->
- this.model = model
- promise.resolve("Model successfully loaded")
- }
- ) { e: IOException ->
+
+ try {
+ this.model = Model(path);
+ promise.resolve("Model successfully loaded")
+ } catch (e: IOException) {
this.model = null
promise.reject(e)
}
@@ -153,6 +164,25 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
@@ -153,6 +165,25 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
cleanRecognizer();
}

View File

@@ -353,43 +353,5 @@
"web/bundles/": true,
"web/env.php": true,
}
},
"tasks": {
"version": "2.0.0",
"tasks": [
{
"label": "transpile-server",
"type": "shell",
"command": "cd ${workspaceFolder}/packages/server && yarn tsc",
"group": "build",
}
]
},
"launch": {
"version": "0.2.0",
"configurations": [
{
"name": "Server",
"type": "node",
"request": "launch",
"preLaunchTask": "transpile-server",
"skipFiles": [
"<node_internals>/**"
],
"outFiles": [
"${workspaceFolder}/**/*.js",
"!**/node_modules/**"
],
"program": "${workspaceFolder}/packages/server/src/app.ts",
"args": [
"--env", "dev"
],
"env": {
"JOPLIN_IS_TESTING": "1",
"TERMS_URL": "https://example.com",
"PRIVACY_URL": "https://example.com",
}
}
]
}
}

View File

@@ -1 +0,0 @@
<a data-from-md title='&lt;style&gt;' href='&amp;#x22&amp;#x2c&amp;#x22&amp;#x61&amp;#x22&amp;#x29&amp;#x3b&amp;#x74&amp;#x6f&amp;#x70&amp;#x2e&amp;#x72&amp;#x65&amp;#x71&amp;#x75&amp;#x69&amp;#x72&amp;#x65&amp;#x28&amp;#x27&amp;#x63&amp;#x68&amp;#x69&amp;#x6c&amp;#x64&amp;#x5f&amp;#x70&amp;#x72&amp;#x6f&amp;#x63&amp;#x65&amp;#x73&amp;#x73&amp;#x27&amp;#x29&amp;#x2e&amp;#x65&amp;#x78&amp;#x65&amp;#x63&amp;#x28&amp;#x27&amp;#x6f&amp;#x70&amp;#x65&amp;#x6e&amp;#x20&amp;#x2f&amp;#x53&amp;#x79&amp;#x73&amp;#x74&amp;#x65&amp;#x6d&amp;#x2f&amp;#x41&amp;#x70&amp;#x70&amp;#x6c&amp;#x69&amp;#x63&amp;#x61&amp;#x74&amp;#x69&amp;#x6f&amp;#x6e&amp;#x73&amp;#x2f&amp;#x43&amp;#x61&amp;#x6c&amp;#x63&amp;#x75&amp;#x6c&amp;#x61&amp;#x74&amp;#x6f&amp;#x72&amp;#x2e&amp;#x61&amp;#x70&amp;#x70&amp;#x27&amp;#x29&amp;#x3b&amp;#x2f&amp;#x2f' onclick='postMessage(&quot;&amp;#x22&amp;#x2c&amp;#x22&amp;#x61&amp;#x22&amp;#x29&amp;#x3b&amp;#x74&amp;#x6f&amp;#x70&amp;#x2e&amp;#x72&amp;#x65&amp;#x71&amp;#x75&amp;#x69&amp;#x72&amp;#x65&amp;#x28&amp;#x27&amp;#x63&amp;#x68&amp;#x69&amp;#x6c&amp;#x64&amp;#x5f&amp;#x70&amp;#x72&amp;#x6f&amp;#x63&amp;#x65&amp;#x73&amp;#x73&amp;#x27&amp;#x29&amp;#x2e&amp;#x65&amp;#x78&amp;#x65&amp;#x63&amp;#x28&amp;#x27&amp;#x6f&amp;#x70&amp;#x65&amp;#x6e&amp;#x20&amp;#x2f&amp;#x53&amp;#x79&amp;#x73&amp;#x74&amp;#x65&amp;#x6d&amp;#x2f&amp;#x41&amp;#x70&amp;#x70&amp;#x6c&amp;#x69&amp;#x63&amp;#x61&amp;#x74&amp;#x69&amp;#x6f&amp;#x6e&amp;#x73&amp;#x2f&amp;#x43&amp;#x61&amp;#x6c&amp;#x63&amp;#x75&amp;#x6c&amp;#x61&amp;#x74&amp;#x6f&amp;#x72&amp;#x2e&amp;#x61&amp;#x70&amp;#x70&amp;#x27&amp;#x29&amp;#x3b&amp;#x2f&amp;#x2f&quot;, { resourceId: &quot;&quot; }); return false;'>xxxxx</a>

View File

@@ -1 +0,0 @@
[xxxxx](&#x22&#x2c&#x22&#x61&#x22&#x29&#x3b&#x74&#x6f&#x70&#x2e&#x72&#x65&#x71&#x75&#x69&#x72&#x65&#x28&#x27&#x63&#x68&#x69&#x6c&#x64&#x5f&#x70&#x72&#x6f&#x63&#x65&#x73&#x73&#x27&#x29&#x2e&#x65&#x78&#x65&#x63&#x28&#x27&#x6f&#x70&#x65&#x6e&#x20&#x2f&#x53&#x79&#x73&#x74&#x65&#x6d&#x2f&#x41&#x70&#x70&#x6c&#x69&#x63&#x61&#x74&#x69&#x6f&#x6e&#x73&#x2f&#x43&#x61&#x6c&#x63&#x75&#x6c&#x61&#x74&#x6f&#x72&#x2e&#x61&#x70&#x70&#x27&#x29&#x3b&#x2f&#x2f '<style>')

View File

@@ -34,7 +34,7 @@ import ShareFolderDialog from '../ShareFolderDialog/ShareFolderDialog';
import { ShareInvitation } from '@joplin/lib/services/share/reducer';
import removeKeylessItems from '../ResizableLayout/utils/removeKeylessItems';
import { localSyncInfoFromState } from '@joplin/lib/services/synchronizer/syncInfoUtils';
import { isCallbackUrl, parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import { parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import ElectronAppWrapper from '../../ElectronAppWrapper';
import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils';
import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
@@ -173,7 +173,6 @@ class MainScreenComponent extends React.Component<Props, State> {
}
private openCallbackUrl(url: string) {
if (!isCallbackUrl(url)) throw new Error(`Invalid callback URL: ${url}`);
const { command, params } = parseCallbackUrl(url);
void CommandService.instance().execute(command.toString(), params.id);
}

View File

@@ -26,7 +26,6 @@ import { loadScript } from '../../../utils/loadScript';
import bridge from '../../../../services/bridge';
import { TinyMceEditorEvents } from './utils/types';
import type { Editor } from 'tinymce';
import { joplinCommandToTinyMceCommands, TinyMceCommand } from './utils/joplinCommandToTinyMceCommands';
const { clipboard } = require('electron');
const supportedLocales = require('./supportedLocales');
@@ -79,6 +78,23 @@ function stripMarkup(markupLanguage: number, markup: string, options: any = null
return markupToHtml_.stripMarkup(markupLanguage, markup, options);
}
interface TinyMceCommand {
name: string;
value?: any;
ui?: boolean;
}
interface JoplinCommandToTinyMceCommands {
[key: string]: TinyMceCommand;
}
const joplinCommandToTinyMceCommands: JoplinCommandToTinyMceCommands = {
'textBold': { name: 'mceToggleFormat', value: 'bold' },
'textItalic': { name: 'mceToggleFormat', value: 'italic' },
'textLink': { name: 'mceLink' },
'search': { name: 'SearchReplace' },
};
interface LastOnChangeEventInfo {
content: string;
resourceInfos: ResourceInfos;
@@ -258,17 +274,11 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
return false;
}
if (joplinCommandToTinyMceCommands[cmd.name] === true) {
// Already handled in useWindowCommandHandlers.ts
} else if (joplinCommandToTinyMceCommands[cmd.name] === false) {
// Explicitely not supported
} else {
const tinyMceCmd: TinyMceCommand = { ...(joplinCommandToTinyMceCommands[cmd.name] as TinyMceCommand) };
if (!('ui' in tinyMceCmd)) tinyMceCmd.ui = false;
if (!('value' in tinyMceCmd)) tinyMceCmd.value = null;
const tinyMceCmd: TinyMceCommand = { ...joplinCommandToTinyMceCommands[cmd.name] };
if (!('ui' in tinyMceCmd)) tinyMceCmd.ui = false;
if (!('value' in tinyMceCmd)) tinyMceCmd.value = null;
editor.execCommand(tinyMceCmd.name, tinyMceCmd.ui, tinyMceCmd.value);
}
editor.execCommand(tinyMceCmd.name, tinyMceCmd.ui, tinyMceCmd.value);
return true;
},
@@ -593,15 +603,11 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
joplinSup: { inline: 'sup', remove: 'all' },
},
setup: (editor: Editor) => {
editor.addCommand('joplinAttach', () => {
insertResourcesIntoContentRef.current();
});
editor.ui.registry.addButton('joplinAttach', {
tooltip: _('Attach file'),
icon: 'paperclip',
onAction: async function() {
editor.execCommand('joplinAttach');
insertResourcesIntoContentRef.current();
},
});

View File

@@ -1,22 +0,0 @@
export interface TinyMceCommand {
name: string;
value?: any;
ui?: boolean;
}
interface JoplinCommandToTinyMceCommands {
[key: string]: TinyMceCommand | boolean;
}
// If the mapping is simply `true` it means that the command is supported via
// useWindowCommandHandlers.ts. We still add it here to have the complete list
// of supported commands.
export const joplinCommandToTinyMceCommands: JoplinCommandToTinyMceCommands = {
'textBold': { name: 'mceToggleFormat', value: 'bold' },
'textItalic': { name: 'mceToggleFormat', value: 'italic' },
'textCode': { name: 'mceToggleFormat', value: 'code' },
'textLink': { name: 'mceLink' },
'search': { name: 'SearchReplace' },
'attachFile': { name: 'joplinAttach' },
'insertDateTime': true,
};

View File

@@ -4,7 +4,6 @@ import editorCommandDeclarations from '../editorCommandDeclarations';
import CommandService, { CommandDeclaration, CommandRuntime, CommandContext } from '@joplin/lib/services/CommandService';
import time from '@joplin/lib/time';
import { reg } from '@joplin/lib/registry';
import { joplinCommandToTinyMceCommands } from '../NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands';
const commandsWithDependencies = [
require('../commands/showLocalSearch'),
@@ -25,8 +24,6 @@ interface HookDependencies {
}
function editorCommandRuntime(declaration: CommandDeclaration, editorRef: any, setFormNote: Function): CommandRuntime {
const markdownEditorOnly = !Object.keys(joplinCommandToTinyMceCommands).includes(declaration.name);
return {
execute: async (_context: CommandContext, ...args: any[]) => {
if (!editorRef.current) {
@@ -68,7 +65,7 @@ function editorCommandRuntime(declaration: CommandDeclaration, editorRef: any, s
// currently selected text.
//
// https://github.com/laurent22/joplin/issues/5707
enabledCondition: `(!modalDialogVisible || gotoAnythingVisible) ${markdownEditorOnly ? '&& markdownEditorPaneVisible' : ''} && oneNoteSelected && noteIsMarkdown`,
enabledCondition: '(!modalDialogVisible || gotoAnythingVisible) && markdownEditorPaneVisible && oneNoteSelected && noteIsMarkdown',
};
}

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "2.11.11",
"version": "2.11.9",
"description": "Joplin for Desktop",
"main": "main.js",
"private": true,
@@ -117,6 +117,7 @@
"@types/styled-components": "5.1.26",
"electron": "19.1.4",
"electron-builder": "22.11.7",
"electron-notarize": "1.2.2",
"electron-rebuild": "3.2.9",
"glob": "8.1.0",
"gulp": "4.0.2",
@@ -133,7 +134,6 @@
"7zip-bin-win": "^2.1.1"
},
"dependencies": {
"@electron/notarize": "1.2.4",
"@electron/remote": "2.0.9",
"@fortawesome/fontawesome-free": "5.15.4",
"@joeattardi/emoji-button": "4.6.4",
@@ -162,7 +162,7 @@
"react": "18.2.0",
"react-datetime": "3.2.0",
"react-dom": "18.2.0",
"react-redux": "8.0.7",
"react-redux": "8.0.5",
"react-select": "5.7.3",
"react-toggle-button": "2.2.0",
"react-tooltip": "4.5.1",
@@ -171,7 +171,7 @@
"roboto-fontface": "0.10.0",
"smalltalk": "2.5.1",
"sqlite3": "5.1.6",
"styled-components": "5.3.11",
"styled-components": "5.3.10",
"styled-system": "5.1.5",
"taboverride": "4.0.3",
"tinymce": "5.10.6"

View File

@@ -1,23 +1,14 @@
import { existsSync } from 'fs';
import { join } from 'path';
import { notarize } from '@electron/notarize';
const fs = require('fs');
const path = require('path');
const electron_notarize = require('electron-notarize');
const execCommand = require('./execCommand');
function isDesktopAppTag(tagName: string) {
function isDesktopAppTag(tagName) {
if (!tagName) return false;
return tagName[0] === 'v';
}
interface Params {
appOutDir: string;
packager: {
appInfo: {
productFilename: string;
};
};
}
export default async (params: Params) => {
module.exports = async function(params) {
if (process.platform !== 'darwin') return;
console.info('Checking if notarization should be done...');
@@ -35,8 +26,8 @@ export default async (params: Params) => {
// Same appId in electron-builder.
const appId = 'net.cozic.joplin-desktop';
const appPath = join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`);
if (!existsSync(appPath)) {
const appPath = path.join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`);
if (!fs.existsSync(appPath)) {
throw new Error(`Cannot find application at: ${appPath}`);
}
@@ -49,7 +40,7 @@ export default async (params: Params) => {
}, 60000);
try {
await notarize({
await electron_notarize.notarize({
appBundleId: appId,
appPath: appPath,
@@ -67,12 +58,7 @@ export default async (params: Params) => {
// Use this to get it:
//
// xcrun altool --list-providers -u APPLE_ID -p APPLE_ID_PASSWORD
// ascProvider: process.env.APPLE_ASC_PROVIDER,
// In our case, the team ID is the same as the legacy ASC_PROVIDER
teamId: process.env.APPLE_ASC_PROVIDER,
tool: 'notarytool',
ascProvider: process.env.APPLE_ASC_PROVIDER,
});
} catch (error) {
console.error(error);

View File

@@ -0,0 +1,7 @@
French small model for Vosk
WER
%WER 23.95 [ 37203 / 155330, 5373 ins, 4427 del, 27403 sub ] exp/chain_a/tdnn/decode_test_cv/wer_12_0.0
%WER 19.30 [ 2975 / 15412, 683 ins, 672 del, 1620 sub ] exp/chain_a/tdnn/decode_test_mtedx/wer_10_0.0
%WER 27.25 [ 20208 / 74145, 2647 ins, 5852 del, 11709 sub ] exp/chain_a/tdnn/decode_test_podcast_reseg/wer_10_0.0

View File

@@ -0,0 +1,8 @@
--use-energy=false
--sample-frequency=16000
--num-mel-bins=40
--num-ceps=40
--low-freq=40
--high-freq=-200
--allow-upsample=true
--allow-downsample=true

View File

@@ -0,0 +1,10 @@
--min-active=200
--max-active=7000
--beam=13.0
--lattice-beam=4.0
--acoustic-scale=1.0
--frame-subsampling-factor=3
--endpoint.silence-phones=1:2:3:4:5:6:7:8:9:10
--endpoint.rule2.min-trailing-silence=0.5
--endpoint.rule3.min-trailing-silence=1.0
--endpoint.rule4.min-trailing-silence=2.0

View File

@@ -0,0 +1,76 @@
9365
9366
9367
9368
9369
9370
9371
9372
9373
9374
9375
9376
9377
9378
9379
9380
9381
9382
9383
9384
9385
9386
9387
9388
9389
9390
9391
9392
9393
9394
9395
9396
9397
9398
9399
9400
9401
9402
9403
9404
9405
9406
9407
9408
9409
9410
9411
9412
9413
9414
9415
9416
9417
9418
9419
9420
9421
9422
9423
9424
9425
9426
9427
9428
9429
9430
9431
9432
9433
9434
9435
9436
9437
9438
9439
9440

View File

@@ -0,0 +1,154 @@
1 nonword
2 begin
3 end
4 internal
5 singleton
6 nonword
7 begin
8 end
9 internal
10 singleton
11 begin
12 end
13 internal
14 singleton
15 begin
16 end
17 internal
18 singleton
19 begin
20 end
21 internal
22 singleton
23 begin
24 end
25 internal
26 singleton
27 begin
28 end
29 internal
30 singleton
31 begin
32 end
33 internal
34 singleton
35 begin
36 end
37 internal
38 singleton
39 begin
40 end
41 internal
42 singleton
43 begin
44 end
45 internal
46 singleton
47 begin
48 end
49 internal
50 singleton
51 begin
52 end
53 internal
54 singleton
55 begin
56 end
57 internal
58 singleton
59 begin
60 end
61 internal
62 singleton
63 begin
64 end
65 internal
66 singleton
67 begin
68 end
69 internal
70 singleton
71 begin
72 end
73 internal
74 singleton
75 begin
76 end
77 internal
78 singleton
79 begin
80 end
81 internal
82 singleton
83 begin
84 end
85 internal
86 singleton
87 begin
88 end
89 internal
90 singleton
91 begin
92 end
93 internal
94 singleton
95 begin
96 end
97 internal
98 singleton
99 begin
100 end
101 internal
102 singleton
103 begin
104 end
105 internal
106 singleton
107 begin
108 end
109 internal
110 singleton
111 begin
112 end
113 internal
114 singleton
115 begin
116 end
117 internal
118 singleton
119 begin
120 end
121 internal
122 singleton
123 begin
124 end
125 internal
126 singleton
127 begin
128 end
129 internal
130 singleton
131 begin
132 end
133 internal
134 singleton
135 begin
136 end
137 internal
138 singleton
139 begin
140 end
141 internal
142 singleton
143 begin
144 end
145 internal
146 singleton
147 begin
148 end
149 internal
150 singleton
151 begin
152 end
153 internal
154 singleton

View File

@@ -0,0 +1,3 @@
[
1.022245e+11 -6.33291e+09 -2.480997e+09 8.290258e+09 -9.084483e+09 -8.092173e+09 -1.4735e+10 -7.041795e+09 -1.171205e+10 -2.976464e+08 -1.009425e+10 -6765179 -7.821326e+09 1.449499e+09 -6.413975e+09 -5.303802e+08 -4.998635e+09 9.521598e+07 -3.073041e+09 1.56756e+08 -1.287956e+09 1.738752e+08 -2.382392e+08 -2.716675e+07 4.404485e+08 -1.913359e+08 7.780919e+08 -4.006922e+08 7.895809e+08 -5.401082e+08 5.17605e+08 -6.227134e+08 6.58271e+08 -6.204593e+07 5.187754e+08 -4.497048e+08 4.219366e+07 -2.78742e+08 -1.797385e+07 -3.604475e+07 1.053647e+09
1.040194e+13 6.245521e+11 4.223293e+11 6.831219e+11 6.078478e+11 6.3425e+11 7.943839e+11 6.013323e+11 6.781652e+11 5.272091e+11 5.810814e+11 4.353831e+11 4.473305e+11 3.42063e+11 3.083377e+11 2.14257e+11 1.892057e+11 1.163827e+11 8.367058e+10 4.203224e+10 2.297476e+10 7.596307e+09 1.099877e+09 2.886651e+08 3.797438e+09 9.372847e+09 1.629059e+10 2.196351e+10 2.747149e+10 3.072878e+10 3.238528e+10 3.330232e+10 3.407238e+10 3.230687e+10 2.676914e+10 2.252055e+10 1.914305e+10 1.565974e+10 1.224627e+10 8.415393e+09 0 ]

View File

@@ -0,0 +1,2 @@
--left-context=3
--right-context=3

View File

@@ -0,0 +1 @@
1b7180e6-e500-4818-adc8-a41fe97a84ce

View File

@@ -46,7 +46,6 @@ import { NoteEntity } from '@joplin/lib/services/database/types';
import Logger from '@joplin/lib/Logger';
import VoiceTypingDialog from '../voiceTyping/VoiceTypingDialog';
import { voskEnabled } from '../../services/voiceTyping/vosk';
import { isSupportedLanguage } from '../../services/voiceTyping/vosk.android';
const urlUtils = require('@joplin/lib/urlUtils');
// import Vosk from 'react-native-vosk';
@@ -1001,7 +1000,7 @@ class NoteScreenComponent extends BaseScreenComponent {
});
// Voice typing is enabled only for French language and on Android for now
if (voskEnabled && shim.mobilePlatform() === 'android' && isSupportedLanguage(currentLocale())) {
if (voskEnabled && shim.mobilePlatform() === 'android' && currentLocale() === 'fr_FR') {
output.push({
title: _('Voice typing...'),
onPress: () => {
@@ -1308,7 +1307,7 @@ class NoteScreenComponent extends BaseScreenComponent {
const renderVoiceTypingDialog = () => {
if (!this.state.voiceTypingDialogShown) return null;
return <VoiceTypingDialog locale={currentLocale()} onText={this.voiceTypingDialog_onText} onDismiss={this.voiceTypingDialog_onDismiss}/>;
return <VoiceTypingDialog onText={this.voiceTypingDialog_onText} onDismiss={this.voiceTypingDialog_onDismiss}/>;
};
return (

View File

@@ -1,14 +1,12 @@
import * as React from 'react';
import { useState, useEffect, useCallback } from 'react';
import { Banner, ActivityIndicator, Modal } from 'react-native-paper';
import { _, languageName } from '@joplin/lib/locale';
import { Banner, ActivityIndicator } from 'react-native-paper';
import { _ } from '@joplin/lib/locale';
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
import { getVosk, Recorder, startRecording, Vosk } from '../../services/voiceTyping/vosk';
import { IconSource } from 'react-native-paper/lib/typescript/src/components/Icon';
import { modelIsDownloaded } from '../../services/voiceTyping/vosk.android';
interface Props {
locale: string;
onDismiss: ()=> void;
onText: (text: string)=> void;
}
@@ -18,39 +16,30 @@ enum RecorderState {
Recording = 2,
Processing = 3,
Error = 4,
Downloading = 5,
}
const useVosk = (locale: string): [Error | null, boolean, Vosk|null] => {
const useVosk = (): [Error | null, Vosk|null] => {
const [vosk, setVosk] = useState<Vosk>(null);
const [error, setError] = useState<Error>(null);
const [mustDownloadModel, setMustDownloadModel] = useState<boolean | null>(null);
useAsyncEffect(async (event: AsyncEffectEvent) => {
if (mustDownloadModel === null) return;
try {
const v = await getVosk(locale);
const v = await getVosk();
if (event.cancelled) return;
setVosk(v);
} catch (error) {
setError(error);
} finally {
setMustDownloadModel(false);
}
}, [locale, mustDownloadModel]);
}, []);
useAsyncEffect(async (_event: AsyncEffectEvent) => {
setMustDownloadModel(!(await modelIsDownloaded(locale)));
}, [locale]);
return [error, mustDownloadModel, vosk];
return [error, vosk];
};
export default (props: Props) => {
const [recorder, setRecorder] = useState<Recorder>(null);
const [recorderState, setRecorderState] = useState<RecorderState>(RecorderState.Loading);
const [voskError, mustDownloadModel, vosk] = useVosk(props.locale);
const [voskError, vosk] = useVosk();
useEffect(() => {
if (voskError) {
@@ -60,12 +49,6 @@ export default (props: Props) => {
}
}, [vosk, voskError]);
useEffect(() => {
if (mustDownloadModel) {
setRecorderState(RecorderState.Downloading);
}
}, [mustDownloadModel]);
useEffect(() => {
if (recorderState === RecorderState.Recording) {
setRecorder(startRecording(vosk, {
@@ -77,7 +60,7 @@ export default (props: Props) => {
}, [recorderState, vosk, props.onText]);
const onDismiss = useCallback(() => {
if (recorder) recorder.cleanup();
recorder.cleanup();
props.onDismiss();
}, [recorder, props.onDismiss]);
@@ -86,7 +69,6 @@ export default (props: Props) => {
[RecorderState.Loading]: () => _('Loading...'),
[RecorderState.Recording]: () => _('Please record your voice...'),
[RecorderState.Processing]: () => _('Converting speech to text...'),
[RecorderState.Downloading]: () => _('Downloading %s language files...', languageName(props.locale)),
[RecorderState.Error]: () => _('Error: %s', voskError.message),
};
@@ -98,7 +80,6 @@ export default (props: Props) => {
[RecorderState.Loading]: ({ size }: { size: number }) => <ActivityIndicator animating={true} style={{ width: size, height: size }} />,
[RecorderState.Recording]: 'microphone',
[RecorderState.Processing]: 'microphone',
[RecorderState.Downloading]: ({ size }: { size: number }) => <ActivityIndicator animating={true} style={{ width: size, height: size }} />,
[RecorderState.Error]: 'alert-circle-outline',
};
@@ -106,18 +87,16 @@ export default (props: Props) => {
};
return (
<Modal visible={true} style={{ display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
<Banner
visible={true}
icon={renderIcon()}
actions={[
{
label: _('Done'),
onPress: onDismiss,
},
]}>
{`${_('Voice typing...')}\n${renderContent()}`}
</Banner>
</Modal>
<Banner
visible={true}
icon={renderIcon()}
actions={[
{
label: _('Done'),
onPress: onDismiss,
},
]}>
{`${_('Voice typing...')}\n${renderContent()}`}
</Banner>
);
};

View File

@@ -347,7 +347,7 @@ PODS:
- React-Core
- react-native-fingerprint-scanner (6.0.0):
- React
- react-native-geolocation (3.0.6):
- react-native-geolocation (2.1.0):
- React-Core
- react-native-get-random-values (1.8.0):
- React-Core
@@ -483,15 +483,7 @@ PODS:
- React-Core
- RNVectorIcons (9.2.0):
- React-Core
- RNZipArchive (6.0.9):
- React-Core
- RNZipArchive/Core (= 6.0.9)
- SSZipArchive (~> 2.2)
- RNZipArchive/Core (6.0.9):
- React-Core
- SSZipArchive (~> 2.2)
- SocketRocket (0.6.0)
- SSZipArchive (2.4.3)
- Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
@@ -585,7 +577,6 @@ DEPENDENCIES:
- RNSecureRandom (from `../node_modules/react-native-securerandom`)
- RNShare (from `../node_modules/react-native-share`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- RNZipArchive (from `../node_modules/react-native-zip-archive`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
@@ -604,7 +595,6 @@ SPEC REPOS:
- libevent
- OpenSSL-Universal
- SocketRocket
- SSZipArchive
- YogaKit
EXTERNAL SOURCES:
@@ -734,8 +724,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-share"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
RNZipArchive:
:path: "../node_modules/react-native-zip-archive"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
@@ -779,7 +767,7 @@ SPEC CHECKSUMS:
react-native-camera: 3eae183c1d111103963f3dd913b65d01aef8110f
react-native-document-picker: 69ca2094d8780cfc1e7e613894d15290fdc54bba
react-native-fingerprint-scanner: ac6656f18c8e45a7459302b84da41a44ad96dbbe
react-native-geolocation: 0f7fe8a4c2de477e278b0365cce27d089a8c5903
react-native-geolocation: 69f4fd37650b8e7fee91816d395e62dd16f5ab8d
react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a
react-native-image-picker: ec9b713e248760bfa0f879f0715391de4651a7cb
react-native-image-resizer: d9fb629a867335bdc13230ac2a58702bb8c8828f
@@ -816,9 +804,7 @@ SPEC CHECKSUMS:
RNSecureRandom: 07efbdf2cd99efe13497433668e54acd7df49fef
RNShare: d82e10f6b7677f4b0048c23709bd04098d5aee6c
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
RNZipArchive: 68a0c6db4b1c103f846f1559622050df254a3ade
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
Yoga: e7ea9e590e27460d28911403b894722354d73479
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

View File

@@ -23,8 +23,8 @@
"@joplin/react-native-saf-x": "~2.11",
"@joplin/renderer": "~2.11",
"@react-native-community/clipboard": "1.5.1",
"@react-native-community/datetimepicker": "7.1.0",
"@react-native-community/geolocation": "3.0.6",
"@react-native-community/datetimepicker": "7.0.1",
"@react-native-community/geolocation": "2.1.0",
"@react-native-community/netinfo": "9.3.10",
"@react-native-community/push-notification-ios": "1.11.0",
"@react-native-community/slider": "4.4.2",
@@ -69,8 +69,7 @@
"react-native-version-info": "1.1.1",
"react-native-vosk": "0.1.12",
"react-native-webview": "12.4.0",
"react-native-zip-archive": "6.0.9",
"react-redux": "8.0.7",
"react-redux": "7.2.9",
"redux": "4.2.1",
"rn-fetch-blob": "0.12.0",
"stream": "0.0.2",
@@ -117,7 +116,7 @@
"metro-react-native-babel-preset": "0.73.9",
"nodemon": "2.0.22",
"ts-jest": "29.1.0",
"ts-loader": "9.4.3",
"ts-loader": "9.4.2",
"ts-node": "10.9.1",
"typescript": "5.0.2",
"uglify-js": "3.17.4",

View File

@@ -1,25 +1,17 @@
import { languageCodeOnly } from '@joplin/lib/locale';
import Logger from '@joplin/lib/Logger';
import shim from '@joplin/lib/shim';
import Vosk from 'react-native-vosk';
import { unzip } from 'react-native-zip-archive';
import RNFetchBlob from 'rn-fetch-blob';
const md5 = require('md5');
const logger = Logger.create('voiceTyping/vosk');
enum State {
Idle = 0,
Recording,
Completing,
}
interface StartOptions {
onResult: (text: string)=> void;
}
let vosk_: Record<string, Vosk> = {};
let vosk_: Vosk|null = null;
let state_: State = State.Idle;
export const voskEnabled = true;
@@ -31,113 +23,12 @@ export interface Recorder {
cleanup: ()=> void;
}
const supportedLanguages = {
'en': 'https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip',
'cn': 'https://alphacephei.com/vosk/models/vosk-model-small-cn-0.22.zip',
'ru': 'https://alphacephei.com/vosk/models/vosk-model-small-ru-0.22.zip',
'fr': 'https://alphacephei.com/vosk/models/vosk-model-small-fr-0.22.zip',
'de': 'https://alphacephei.com/vosk/models/vosk-model-small-de-0.15.zip',
'es': 'https://alphacephei.com/vosk/models/vosk-model-small-es-0.42.zip',
'pt': 'https://alphacephei.com/vosk/models/vosk-model-small-pt-0.3.zip',
'tr': 'https://alphacephei.com/vosk/models/vosk-model-small-tr-0.3.zip',
'vn': 'https://alphacephei.com/vosk/models/vosk-model-small-vn-0.4.zip',
'it': 'https://alphacephei.com/vosk/models/vosk-model-small-it-0.22.zip',
'nl': 'https://alphacephei.com/vosk/models/vosk-model-small-nl-0.22.zip',
'uk': 'https://alphacephei.com/vosk/models/vosk-model-small-uk-v3-small.zip',
'ja': 'https://alphacephei.com/vosk/models/vosk-model-small-ja-0.22.zip',
'hi': 'https://alphacephei.com/vosk/models/vosk-model-small-hi-0.22.zip',
'cs': 'https://alphacephei.com/vosk/models/vosk-model-small-cs-0.4-rhasspy.zip',
'pl': 'https://alphacephei.com/vosk/models/vosk-model-small-pl-0.22.zip',
'uz': 'https://alphacephei.com/vosk/models/vosk-model-small-uz-0.22.zip',
'ko': 'https://alphacephei.com/vosk/models/vosk-model-small-ko-0.22.zip',
};
export const isSupportedLanguage = (locale: string) => {
const l = languageCodeOnly(locale).toLowerCase();
return Object.keys(supportedLanguages).includes(l);
};
// Where all the models files for all the languages are
const getModelRootDir = () => {
return `${RNFetchBlob.fs.dirs.DocumentDir}/vosk-models`;
};
// Where we unzip a model after downloading it
const getUnzipDir = (locale: string) => {
return `${getModelRootDir()}/${locale}`;
};
// Where the model for a particular language is
const getModelDir = (locale: string) => {
return `${getUnzipDir(locale)}/model`;
};
const languageModelUrl = (locale: string) => {
const l = languageCodeOnly(locale).toLowerCase();
if (!(l in supportedLanguages)) throw new Error(`No language file for: ${locale}`);
return (supportedLanguages as any)[l];
};
export const modelIsDownloaded = async (locale: string) => {
const uuidFile = `${getModelDir(locale)}/uuid`;
return shim.fsDriver().exists(uuidFile);
};
export const getVosk = async (locale: string) => {
if (vosk_[locale]) return vosk_[locale];
const vosk = new Vosk();
const modelDir = await downloadModel(locale);
logger.info(`Loading model from ${modelDir}`);
await shim.fsDriver().readDirStats(modelDir);
const result = await vosk.loadModel(modelDir);
export const getVosk = async () => {
if (vosk_) return vosk_;
vosk_ = new Vosk();
const result = await vosk_.loadModel('model-fr-fr');
logger.info('getVosk:', result);
vosk_ = { [locale]: vosk };
return vosk;
};
const downloadModel = async (locale: string) => {
const modelUrl = languageModelUrl(locale);
const unzipDir = getUnzipDir(locale);
const zipFilePath = `${unzipDir}.zip`;
const modelDir = getModelDir(locale);
const uuidFile = `${modelDir}/uuid`;
if (await modelIsDownloaded(locale)) {
logger.info(`Model for ${locale} already exists at ${modelDir}`);
return modelDir;
}
await shim.fsDriver().remove(unzipDir);
logger.info(`Downloading model from: ${modelUrl}`);
await shim.fetchBlob(languageModelUrl(locale), {
path: zipFilePath,
});
logger.info(`Unzipping ${zipFilePath} => ${unzipDir}`);
await unzip(zipFilePath, unzipDir);
const dirs = await shim.fsDriver().readDirStats(unzipDir);
if (dirs.length !== 1) {
logger.error('Expected 1 directory but got', dirs);
throw new Error(`Expected 1 directory, but got ${dirs.length}`);
}
const fullUnzipPath = `${unzipDir}/${dirs[0].path}`;
logger.info(`Moving ${fullUnzipPath} => ${modelDir}`);
await shim.fsDriver().rename(fullUnzipPath, modelDir);
await shim.fsDriver().writeFile(uuidFile, md5(modelUrl));
await shim.fsDriver().remove(zipFilePath);
return modelDir;
return vosk_;
};
export const startRecording = (vosk: Vosk, options: StartOptions): Recorder => {
@@ -219,9 +110,8 @@ export const startRecording = (vosk: Vosk, options: StartOptions): Recorder => {
});
},
cleanup: () => {
if (state_ === State.Recording) {
if (state_ !== State.Idle) {
logger.info('Cancelling...');
state_ = State.Completing;
vosk.stopOnly();
completeRecording('', null);
}

View File

@@ -13,15 +13,7 @@ export interface Recorder {
cleanup: ()=> void;
}
export const isSupportedLanguage = (_locale: string) => {
return false;
};
export const modelIsDownloaded = async (_locale: string) => {
return false;
};
export const getVosk = async (_locale: string) => {
export const getVosk = async () => {
return {} as any;
};

View File

@@ -116,13 +116,6 @@ export default class FsDriverRN extends FsDriverBase {
return RNFS.moveFile(source, dest);
}
public async rename(source: string, dest: string) {
if (isScopedUri(source) || isScopedUri(dest)) {
await RNSAF.rename(source, dest);
}
return RNFS.moveFile(source, dest);
}
public async exists(path: string) {
if (isScopedUri(path)) {
return RNSAF.exists(path);

View File

@@ -40,7 +40,7 @@
},
"dependencies": {
"domelementtype": "2.3.0",
"domhandler": "5.0.3",
"domhandler": "3.3.0",
"domutils": "3.1.0",
"entities": "2.2.0"
},

View File

@@ -3,14 +3,13 @@ import * as callbackUrlUtils from './callbackUrlUtils';
describe('callbackUrlUtils', () => {
it('should identify valid callback urls', () => {
const url = 'joplin://x-callback-url/openFolder?a=b';
const url = 'joplin://x-callback-url/123?a=b';
expect(callbackUrlUtils.isCallbackUrl(url)).toBe(true);
});
it('should identify invalid callback urls', () => {
expect(callbackUrlUtils.isCallbackUrl('not-joplin://x-callback-url/123?a=b')).toBe(false);
expect(callbackUrlUtils.isCallbackUrl('joplin://xcallbackurl/123?a=b')).toBe(false);
expect(callbackUrlUtils.isCallbackUrl('joplin://x-callback-url/invalidCommand?a=b')).toBe(false);
});
it('should build valid note callback urls', () => {

View File

@@ -1,9 +1,7 @@
const URL = require('url-parse');
export function isCallbackUrl(s: string) {
return s.startsWith('joplin://x-callback-url/openNote?') ||
s.startsWith('joplin://x-callback-url/openFolder?') ||
s.startsWith('joplin://x-callback-url/openTag?');
return s.startsWith('joplin://x-callback-url/');
}
export function getNoteCallbackUrl(noteId: string) {

View File

@@ -508,8 +508,7 @@ function languageNameInEnglish(languageCode: string) {
return codeToLanguageE_[languageCode] ? codeToLanguageE_[languageCode] : '';
}
function languageName(canonicalName: string, defaultToEnglish: boolean = true) {
const languageCode = languageCodeOnly(canonicalName);
function languageName(languageCode: string, defaultToEnglish: boolean = true) {
if (codeToLanguage_[languageCode]) return codeToLanguage_[languageCode];
if (defaultToEnglish) return languageNameInEnglish(languageCode);
return '';
@@ -604,4 +603,4 @@ const stringByLocale = (locale: string, s: string, ...args: any[]): string => {
}
};
export { _, _n, supportedLocales, languageName, currentLocale, localesFromLanguageCode, languageCodeOnly, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };
export { _, _n, supportedLocales, currentLocale, localesFromLanguageCode, languageCodeOnly, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };

View File

@@ -30,7 +30,7 @@
"jest-environment-jsdom": "29.5.0",
"style-loader": "3.3.3",
"ts-jest": "29.1.0",
"ts-loader": "9.4.3",
"ts-loader": "9.4.2",
"typescript": "5.0.2",
"webpack": "5.74.0",
"webpack-cli": "4.10.0"
@@ -44,6 +44,6 @@
"pdfjs-dist": "2.16.105",
"react": "18.2.0",
"react-dom": "18.2.0",
"styled-components": "5.3.11"
"styled-components": "5.3.10"
}
}

View File

@@ -5,7 +5,7 @@ describe('linkReplacement', () => {
test('should handle non-resource links', () => {
const r = linkReplacement('https://example.com/test').html;
expect(r).toBe('<a data-from-md href=\'https://example.com/test\' onclick=\'postMessage(&quot;https://example.com/test&quot;, { resourceId: &quot;&quot; }); return false;\'>');
expect(r).toBe('<a data-from-md href=\'https://example.com/test\' onclick=\'postMessage("https://example.com/test", { resourceId: "" }); return false;\'>');
});
test('should handle non-resource links - simple rendering', () => {
@@ -17,7 +17,7 @@ describe('linkReplacement', () => {
// Handles a link such as:
// [Google](https://www.goo'onclick=javascript:alert(/1/);f=')
const r = linkReplacement('https://www.goo\'onclick=javascript:alert(/1/);f=\'', { linkRenderingType: 1 }).html;
expect(r).toBe('<a data-from-md href=\'https://www.goo&apos;onclick=javascript:alert(/1/);f=&apos;\' onclick=\'postMessage(&quot;https://www.goo%27onclick=javascript:alert(/1/);f=%27&quot;, { resourceId: &quot;&quot; }); return false;\'>');
expect(r).toBe('<a data-from-md href=\'https://www.goo&apos;onclick=javascript:alert(/1/);f=&apos;\' onclick=\'postMessage("https://www.goo%27onclick=javascript:alert(/1/);f=%27", { resourceId: "" }); return false;\'>');
});
test('should handle resource links - downloaded status', () => {
@@ -35,7 +35,7 @@ describe('linkReplacement', () => {
},
}).html;
expect(r).toBe(`<a data-from-md data-resource-id='${resourceId}' href='#' onclick='postMessage(&quot;joplin://${resourceId}&quot;, { resourceId: &quot;${resourceId}&quot; }); return false;'><span class="resource-icon fa-joplin"></span>`);
expect(r).toBe(`<a data-from-md data-resource-id='${resourceId}' href='#' onclick='postMessage("joplin://${resourceId}", { resourceId: "${resourceId}" }); return false;'><span class="resource-icon fa-joplin"></span>`);
});
test('should handle resource links - idle status', () => {

View File

@@ -99,7 +99,7 @@ export default function(href: string, options: Options = null): LinkReplacementR
postMessageSyntax: options.postMessageSyntax ?? 'void',
}, onClick);
} else {
js = `onclick='${htmlentities(js)}'`;
js = `onclick='${js}'`;
}
if (hrefAttr.indexOf('#') === 0 && href.indexOf('#') === 0) js = ''; // If it's an internal anchor, don't add any JS since the webview is going to handle navigating to the right place

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/server",
"version": "2.11.2",
"version": "2.11.1",
"private": true,
"scripts": {
"start-dev": "yarn run build && JOPLIN_IS_TESTING=1 nodemon --config nodemon.json --ext ts,js,mustache,css,tsx dist/app.js --env dev",
@@ -25,17 +25,17 @@
"@fortawesome/fontawesome-free": "5.15.4",
"@joplin/lib": "~2.11",
"@joplin/renderer": "~2.11",
"@koa/cors": "3.4.3",
"@koa/cors": "3.1.0",
"@types/uuid": "9.0.1",
"bcryptjs": "2.4.3",
"bulma": "0.9.4",
"bulma-prefers-dark": "0.1.0-beta.1",
"compare-versions": "3.6.0",
"dayjs": "1.11.8",
"formidable": "2.1.2",
"dayjs": "1.11.7",
"formidable": "2.1.1",
"fs-extra": "11.1.1",
"html-entities": "1.4.0",
"jquery": "3.7.0",
"jquery": "3.6.4",
"knex": "2.4.2",
"koa": "2.14.2",
"markdown-it": "13.0.1",
@@ -43,7 +43,7 @@
"nanoid": "2.1.11",
"node-cron": "3.0.2",
"node-env-file": "0.1.8",
"nodemailer": "6.9.3",
"nodemailer": "6.9.2",
"nodemon": "2.0.22",
"pg": "8.10.0",
"pretty-bytes": "5.6.0",

View File

@@ -3,7 +3,7 @@ import { DbConnection, defaultAdminEmail, defaultAdminPassword } from '../db';
import { hashPassword } from '../utils/auth';
import uuidgen from '../utils/uuidgen';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('users', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.text('email', 'mediumtext').unique().notNullable();
@@ -123,13 +123,13 @@ export const up = async (db: DbConnection) => {
updated_time: now,
created_time: now,
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('users');
await db.schema.dropTable('sessions');
await db.schema.dropTable('permissions');
await db.schema.dropTable('files');
await db.schema.dropTable('api_clients');
await db.schema.dropTable('changes');
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('notifications', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.string('owner_id', 32).notNullable();
@@ -17,8 +17,8 @@ export const up = async (db: DbConnection) => {
await db.schema.alterTable('notifications', (table: Knex.CreateTableBuilder) => {
table.unique(['owner_id', 'key']);
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('notifications');
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('shares', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.string('owner_id', 32).notNullable();
@@ -10,8 +10,8 @@ export const up = async (db: DbConnection) => {
table.bigInteger('updated_time').notNullable();
table.bigInteger('created_time').notNullable();
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('shares');
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('share_users', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.string('share_id', 32).notNullable();
@@ -38,8 +38,8 @@ export const up = async (db: DbConnection) => {
table.bigInteger('updated_time').notNullable();
table.bigInteger('created_time').notNullable();
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('share_users');
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('joplin_file_contents', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.string('owner_id', 32).notNullable();
@@ -18,8 +18,8 @@ export const up = async (db: DbConnection) => {
table.integer('content_type', 2).defaultTo(1).notNullable();
table.string('content_id', 32).defaultTo('').notNullable();
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('joplin_file_contents');
};
}

View File

@@ -1,13 +1,13 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('shares', (table: Knex.CreateTableBuilder) => {
table.string('folder_id', 32).defaultTo('').notNullable();
table.integer('is_auto', 1).defaultTo(0).notNullable();
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('joplin_items');
};
}

View File

@@ -25,7 +25,7 @@ import { DbConnection } from '../db';
// is_shared: 1
// type_: 1`;
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('items', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.text('name').notNullable();
@@ -109,8 +109,8 @@ export const up = async (db: DbConnection) => {
await db.schema.dropTable('permissions');
await db.schema.dropTable('joplin_file_contents');
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('items');
};
}

View File

@@ -1,12 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('max_item_size').defaultTo(0).notNullable();
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,12 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('can_share').defaultTo(1).notNullable();
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('email_confirmed').defaultTo(0).notNullable();
table.integer('must_set_password').defaultTo(0).notNullable();
@@ -40,8 +40,8 @@ export const up = async (db: DbConnection) => {
await db.schema.alterTable('tokens', (table: Knex.CreateTableBuilder) => {
table.index(['value', 'user_id']);
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,12 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('account_type').defaultTo(0).notNullable();
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,14 +1,14 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('can_upload').defaultTo(1).notNullable();
});
await db('users').update({ can_upload: 1 });
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('subscriptions', (table: Knex.CreateTableBuilder) => {
table.increments('id').unique().primary().notNullable();
table.string('user_id', 32).notNullable();
@@ -12,8 +12,8 @@ export const up = async (db: DbConnection) => {
table.bigInteger('updated_time').notNullable();
table.bigInteger('created_time').notNullable();
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.bigInteger('jop_updated_time').defaultTo(0).notNullable();
});
@@ -22,8 +22,8 @@ export const up = async (db: DbConnection) => {
}
});
}
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,13 +1,13 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.renameColumn('can_share', 'can_share_folder');
table.integer('can_share_note').defaultTo(1).notNullable();
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -5,14 +5,14 @@ import { DbConnection } from '../db';
// recreate it in the same migration. So this is split into two migrations.
// https://github.com/knex/knex/issues/2581
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.dropColumn('max_item_size');
table.dropColumn('can_share_folder');
table.dropColumn('can_share_note');
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('max_item_size').defaultTo(null).nullable();
table.specificType('can_share_folder', 'smallint').defaultTo(null).nullable();
@@ -12,8 +12,8 @@ export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('total_item_size').defaultTo(0).notNullable();
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,9 +1,9 @@
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db('users').update({ email: db.raw('LOWER(email)') });
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,12 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.dropColumn('total_item_size');
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,12 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.bigInteger('total_item_size').defaultTo(0).notNullable();
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.specificType('enabled', 'smallint').defaultTo(1).nullable();
});
@@ -9,8 +9,8 @@ export const up = async (db: DbConnection) => {
await db.schema.alterTable('subscriptions', (table: Knex.CreateTableBuilder) => {
table.specificType('is_deleted', 'smallint').defaultTo(0).nullable();
});
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
try {
await db.schema.alterTable('emails', (table: Knex.CreateTableBuilder) => {
table.text('key', 'mediumtext').defaultTo('').notNullable();
@@ -13,8 +13,8 @@ export const up = async (db: DbConnection) => {
// await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
// table.unique(['recipient_email', 'key']);
// });
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,7 +1,7 @@
// import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (_db: DbConnection) => {
export async function up(_db: DbConnection): Promise<any> {
// try {
// await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
// table.dropUnique(['recipient_email', 'key']);
@@ -9,8 +9,8 @@ export const up = async (_db: DbConnection) => {
// } catch (error) {
// // console.warn('Could not drop unique constraint - this is not an error.', error);
// }
};
}
export const down = async (_db: DbConnection) => {
export async function down(_db: DbConnection): Promise<any> {
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('user_flags', (table: Knex.CreateTableBuilder) => {
table.increments('id').unique().primary().notNullable();
table.string('user_id', 32).notNullable();
@@ -13,8 +13,8 @@ export const up = async (db: DbConnection) => {
await db.schema.alterTable('user_flags', (table: Knex.CreateTableBuilder) => {
table.unique(['user_id', 'type']);
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('user_flags');
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('share_users', (table: Knex.CreateTableBuilder) => {
table.text('master_key', 'mediumtext').defaultTo('').notNullable();
});
@@ -9,9 +9,9 @@ export const up = async (db: DbConnection) => {
await db.schema.alterTable('shares', (table: Knex.CreateTableBuilder) => {
table.string('master_key_id', 32).defaultTo('').notNullable();
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.alterTable('share_users', (table: Knex.CreateTableBuilder) => {
table.dropColumn('master_key');
});
@@ -19,4 +19,4 @@ export const down = async (db: DbConnection) => {
await db.schema.alterTable('shares', (table: Knex.CreateTableBuilder) => {
table.dropColumn('master_key_id');
});
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('events', (table: Knex.CreateTableBuilder) => {
table.uuid('id').unique().notNullable();
table.increments('counter').unique().primary().notNullable();
@@ -14,8 +14,8 @@ export const up = async (db: DbConnection) => {
table.index('type');
table.index('name');
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('events');
};
}

View File

@@ -2,7 +2,7 @@ import { Knex } from 'knex';
import { DbConnection } from '../db';
import { msleep } from '../utils/time';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
if (!(await db.schema.hasColumn('items', 'owner_id'))) {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.string('owner_id', 32).defaultTo('').notNullable();
@@ -58,10 +58,10 @@ export const up = async (db: DbConnection) => {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.string('owner_id', 32).notNullable().alter();
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.dropColumn('owner_id');
});
};
}

View File

@@ -1,14 +1,14 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.unique(['name', 'owner_id']);
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.dropUnique(['name', 'owner_id']);
});
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('storages', (table: Knex.CreateTableBuilder) => {
table.increments('id').unique().primary().notNullable();
table.text('connection_string').notNullable();
@@ -31,12 +31,12 @@ export const up = async (db: DbConnection) => {
await db.schema.alterTable('storages', (table: Knex.CreateTableBuilder) => {
table.unique(['connection_string']);
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('storages');
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.dropColumn('content_storage_id');
});
};
}

View File

@@ -1,14 +1,14 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.index('content_storage_id');
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.dropIndex('content_storage_id');
});
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('user_deletions', (table: Knex.CreateTableBuilder) => {
table.increments('id').unique().primary().notNullable();
table.string('user_id', 32).notNullable();
@@ -19,8 +19,8 @@ export const up = async (db: DbConnection) => {
await db.schema.alterTable('user_deletions', (table: Knex.CreateTableBuilder) => {
table.unique(['user_id']);
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('user_deletions');
};
}

View File

@@ -5,16 +5,16 @@ import { DbConnection } from '../db';
// it to an empty string by default, and update all rows that have "0" as
// recipient_id.
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('emails', (table: Knex.CreateTableBuilder) => {
table.string('recipient_id', 32).defaultTo('').notNullable().alter();
});
await db('emails').update({ recipient_id: '' }).where('recipient_id', '=', '0');
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.alterTable('emails', (table: Knex.CreateTableBuilder) => {
table.string('recipient_id', 32).defaultTo(0).notNullable().alter();
});
};
}

View File

@@ -38,17 +38,17 @@ export const disabledUserIds = async (db: DbConnection): Promise<string[]> => {
return users.map(u => u.id);
};
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.bigInteger('disabled_time').defaultTo(0).notNullable();
});
const userIds = await disabledUserIds(db);
await setUserAccountDisabledTimes(db, userIds);
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.dropColumn('disabled_time');
});
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('backup_items', (table: Knex.CreateTableBuilder) => {
table.increments('id').unique().primary().notNullable();
table.integer('type').notNullable();
@@ -10,8 +10,8 @@ export const up = async (db: DbConnection) => {
table.binary('content').notNullable();
table.bigInteger('created_time').notNullable();
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('backup_items');
};
}

View File

@@ -1,14 +1,14 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('shares', (table: Knex.CreateTableBuilder) => {
table.specificType('recursive', 'smallint').defaultTo(0).nullable();
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.alterTable('shares', (table: Knex.CreateTableBuilder) => {
table.dropColumn('recursive');
});
};
}

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export const up = async (db: DbConnection) => {
export async function up(db: DbConnection): Promise<any> {
await db.schema.createTable('task_states', (table: Knex.CreateTableBuilder) => {
table.increments('id').unique().primary().notNullable();
table.integer('task_id').unique().notNullable();
@@ -10,8 +10,8 @@ export const up = async (db: DbConnection) => {
table.bigInteger('updated_time').notNullable();
table.bigInteger('created_time').notNullable();
});
};
}
export const down = async (db: DbConnection) => {
export async function down(db: DbConnection): Promise<any> {
await db.schema.dropTable('task_states');
};
}

View File

@@ -3,7 +3,7 @@ import { ItemType, databaseSchema, Uuid, Item, ShareType, Share, ChangeType, Use
import { defaultPagination, paginateDbQuery, PaginatedResults, Pagination } from './utils/pagination';
import { isJoplinItemName, isJoplinResourceBlobPath, linkedResourceIds, serializeJoplinItem, unserializeJoplinItem } from '../utils/joplinUtils';
import { ModelType } from '@joplin/lib/BaseModel';
import { ApiError, CustomErrorCode, ErrorConflict, ErrorForbidden, ErrorPayloadTooLarge, ErrorUnprocessableEntity, ErrorCode } from '../utils/errors';
import { ApiError, ErrorCode, ErrorConflict, ErrorForbidden, ErrorPayloadTooLarge, ErrorUnprocessableEntity } from '../utils/errors';
import { Knex } from 'knex';
import { ChangePreviousItem } from './ChangeModel';
import { unique } from '../utils/array';
@@ -311,7 +311,7 @@ export default class ItemModel extends BaseModel<Item> {
try {
content = await fromDriver.read(item.id, { models: this.models() });
} catch (error) {
if (error.code === CustomErrorCode.NotFound) {
if (error.code === ErrorCode.NotFound) {
logger.info(`Could not process item, because content was deleted: ${item.id}`);
return;
}
@@ -537,10 +537,10 @@ export default class ItemModel extends BaseModel<Item> {
return this.itemToJoplinItem(raw);
}
public async saveFromRawContent(user: User, rawContentItemOrItems: SaveFromRawContentItem[] | SaveFromRawContentItem, options: ItemSaveOption = null): Promise<SaveFromRawContentResult> {
public async saveFromRawContent(user: User, rawContentItems: SaveFromRawContentItem[] | SaveFromRawContentItem, options: ItemSaveOption = null): Promise<SaveFromRawContentResult> {
options = options || {};
const rawContentItems = !Array.isArray(rawContentItemOrItems) ? [rawContentItemOrItems] : rawContentItemOrItems;
if (!Array.isArray(rawContentItems)) rawContentItems = [rawContentItems];
// In this function, first we process the input items, which may be
// serialized Joplin items or actual buffers (for resources) and convert
@@ -555,78 +555,73 @@ export default class ItemModel extends BaseModel<Item> {
joplinItem?: any;
}
interface ExistingItem {
id: Uuid;
name: string;
const existingItems = await this.loadByNames(user.id, rawContentItems.map(i => i.name));
const itemsToProcess: Record<string, ItemToProcess> = {};
for (const rawItem of rawContentItems) {
try {
const isJoplinItem = isJoplinItemName(rawItem.name);
let isNote = false;
const item: Item = {
name: rawItem.name,
};
let joplinItem: any = null;
let resourceIds: string[] = [];
if (isJoplinItem) {
joplinItem = await unserializeJoplinItem(rawItem.body.toString());
isNote = joplinItem.type_ === ModelType.Note;
resourceIds = isNote ? linkedResourceIds(joplinItem.body) : [];
item.jop_id = joplinItem.id;
item.jop_parent_id = joplinItem.parent_id || '';
item.jop_type = joplinItem.type_;
item.jop_encryption_applied = joplinItem.encryption_applied || 0;
item.jop_share_id = joplinItem.share_id || '';
item.jop_updated_time = joplinItem.updated_time;
const joplinItemToSave = { ...joplinItem };
delete joplinItemToSave.id;
delete joplinItemToSave.parent_id;
delete joplinItemToSave.share_id;
delete joplinItemToSave.type_;
delete joplinItemToSave.encryption_applied;
delete joplinItemToSave.updated_time;
item.content = Buffer.from(JSON.stringify(joplinItemToSave));
} else {
item.content = rawItem.body;
}
const existingItem = existingItems.find(i => i.name === rawItem.name);
if (existingItem) item.id = existingItem.id;
if (options.shareId) item.jop_share_id = options.shareId;
await this.models().user().checkMaxItemSizeLimit(user, rawItem.body, item, joplinItem);
itemsToProcess[rawItem.name] = {
item: item,
error: null,
resourceIds,
isNote,
joplinItem,
};
} catch (error) {
itemsToProcess[rawItem.name] = {
item: null,
error: error,
};
}
}
return this.withTransaction(async () => {
const existingItems = await this.loadByNames(user.id, rawContentItems.map(i => i.name), { fields: ['id', 'name'] }) as ExistingItem[];
const itemsToProcess: Record<string, ItemToProcess> = {};
for (const rawItem of rawContentItems) {
try {
const isJoplinItem = isJoplinItemName(rawItem.name);
let isNote = false;
const item: Item = {
name: rawItem.name,
};
let joplinItem: any = null;
let resourceIds: string[] = [];
if (isJoplinItem) {
joplinItem = await unserializeJoplinItem(rawItem.body.toString());
isNote = joplinItem.type_ === ModelType.Note;
resourceIds = isNote ? linkedResourceIds(joplinItem.body) : [];
item.jop_id = joplinItem.id;
item.jop_parent_id = joplinItem.parent_id || '';
item.jop_type = joplinItem.type_;
item.jop_encryption_applied = joplinItem.encryption_applied || 0;
item.jop_share_id = joplinItem.share_id || '';
item.jop_updated_time = joplinItem.updated_time;
const joplinItemToSave = { ...joplinItem };
delete joplinItemToSave.id;
delete joplinItemToSave.parent_id;
delete joplinItemToSave.share_id;
delete joplinItemToSave.type_;
delete joplinItemToSave.encryption_applied;
delete joplinItemToSave.updated_time;
item.content = Buffer.from(JSON.stringify(joplinItemToSave));
} else {
item.content = rawItem.body;
}
const existingItem = existingItems.find(i => i.name === rawItem.name);
if (existingItem) item.id = existingItem.id;
if (options.shareId) item.jop_share_id = options.shareId;
await this.models().user().checkMaxItemSizeLimit(user, rawItem.body, item, joplinItem);
itemsToProcess[rawItem.name] = {
item: item,
error: null,
resourceIds,
isNote,
joplinItem,
};
} catch (error) {
itemsToProcess[rawItem.name] = {
item: null,
error: error,
};
}
}
const output: SaveFromRawContentResult = {};
const output: SaveFromRawContentResult = {};
await this.withTransaction(async () => {
for (const name of Object.keys(itemsToProcess)) {
const o = itemsToProcess[name];
@@ -695,9 +690,9 @@ export default class ItemModel extends BaseModel<Item> {
};
}
}
return output;
}, 'ItemModel::saveFromRawContent');
return output;
}
protected async validate(item: Item, options: ValidateOptions = {}): Promise<Item> {
@@ -789,7 +784,7 @@ export default class ItemModel extends BaseModel<Item> {
// that shared folder. It returns null otherwise.
public async joplinItemSharedRootInfo(jopId: string): Promise<SharedRootInfo | null> {
const path = await this.joplinItemPath(jopId);
if (!path.length) throw new ApiError(`Cannot retrieve path for item: ${jopId}`, null, ErrorCode.NoPathForItem);
if (!path.length) throw new ApiError(`Cannot retrieve path for item: ${jopId}`, null, 'noPathForItem');
const rootFolderItem = path[path.length - 1];
const share = await this.models().share().itemShare(ShareType.Folder, rootFolderItem.id);
if (!share) return null;

View File

@@ -2,7 +2,7 @@ import BaseModel, { UuidType } from './BaseModel';
import { Uuid } from '../services/database/types';
import { LockType, Lock, LockClientType, defaultLockTtl, activeLock } from '@joplin/lib/services/synchronizer/LockHandler';
import { Value } from './KeyValueModel';
import { ErrorConflict, ErrorUnprocessableEntity, ErrorCode } from '../utils/errors';
import { ErrorConflict, ErrorUnprocessableEntity } from '../utils/errors';
import uuidgen from '../utils/uuidgen';
export default class LockModel extends BaseModel<Lock> {
@@ -59,7 +59,7 @@ export default class LockModel extends BaseModel<Lock> {
const exclusiveLock = activeLock(locks, new Date(), this.lockTtl, LockType.Exclusive);
if (exclusiveLock) {
throw new ErrorConflict(`Cannot acquire lock because there is already an exclusive lock for client: ${exclusiveLock.clientType} #${exclusiveLock.clientId}`, ErrorCode.HasExclusiveSyncLock);
throw new ErrorConflict(`Cannot acquire lock because there is already an exclusive lock for client: ${exclusiveLock.clientType} #${exclusiveLock.clientId}`, 'hasExclusiveLock');
}
const syncLock = activeLock(locks, new Date(), this.lockTtl, LockType.Sync, clientType, clientId);
@@ -111,7 +111,7 @@ export default class LockModel extends BaseModel<Lock> {
return JSON.stringify(locks);
} else {
throw new ErrorConflict(`Cannot acquire lock because there is already an exclusive lock for client: ${exclusiveLock.clientType} #${exclusiveLock.clientId}`, ErrorCode.HasExclusiveSyncLock);
throw new ErrorConflict(`Cannot acquire lock because there is already an exclusive lock for client: ${exclusiveLock.clientType} #${exclusiveLock.clientId}`, 'hasExclusiveLock');
}
}
@@ -121,7 +121,7 @@ export default class LockModel extends BaseModel<Lock> {
if (syncLock.clientId === clientId) {
locks = locks.filter(l => l.id !== syncLock.id);
} else {
throw new ErrorConflict(`Cannot acquire exclusive lock because there is an active sync lock for client: ${syncLock.clientType} #${syncLock.clientId}`, ErrorCode.HasSyncLock);
throw new ErrorConflict(`Cannot acquire exclusive lock because there is an active sync lock for client: ${syncLock.clientType} #${syncLock.clientId}`, 'hasSyncLock');
}
}

View File

@@ -2,7 +2,7 @@
// database (as a binary blob). For now the driver expects that the content is
// stored in the same table as the items, as it originally was.
import { CustomError, CustomErrorCode } from '../../../utils/errors';
import { CustomError, ErrorCode } from '../../../utils/errors';
import { DatabaseConfigClient, StorageDriverConfig, StorageDriverType } from '../../../utils/types';
import StorageDriverBase, { Context } from './StorageDriverBase';
@@ -41,7 +41,7 @@ export default class StorageDriverDatabase extends StorageDriverBase {
// Calling code should only call this handler if the row exists, so if
// we find it doesn't, it's an error.
if (!row) throw new CustomError(`No such row: ${itemId}`, CustomErrorCode.NotFound);
if (!row) throw new CustomError(`No such row: ${itemId}`, ErrorCode.NotFound);
return row.content;
}

View File

@@ -1,5 +1,5 @@
import { mkdirp, pathExists, readFile, remove, writeFile } from 'fs-extra';
import { CustomError, CustomErrorCode } from '../../../utils/errors';
import { CustomError, ErrorCode } from '../../../utils/errors';
import { StorageDriverConfig, StorageDriverType } from '../../../utils/types';
import StorageDriverBase from './StorageDriverBase';
@@ -36,7 +36,7 @@ export default class StorageDriverFs extends StorageDriverBase {
const result = await readFile(this.itemPath(itemId));
return result;
} catch (error) {
if (error.code === 'ENOENT') throw new CustomError(`Not found: ${itemId}`, CustomErrorCode.NotFound);
if (error.code === 'ENOENT') throw new CustomError(`Not found: ${itemId}`, ErrorCode.NotFound);
throw error;
}
}
@@ -47,7 +47,7 @@ export default class StorageDriverFs extends StorageDriverBase {
try {
await remove(this.itemPath(id));
} catch (error) {
if (error.code === 'ENOENT') throw new CustomError(`Not found: ${itemId}`, CustomErrorCode.NotFound);
if (error.code === 'ENOENT') throw new CustomError(`Not found: ${itemId}`, ErrorCode.NotFound);
throw error;
}
}

View File

@@ -1,4 +1,4 @@
import { CustomError, CustomErrorCode } from '../../../utils/errors';
import { CustomError, ErrorCode } from '../../../utils/errors';
import { StorageDriverConfig, StorageDriverType } from '../../../utils/types';
import StorageDriverBase from './StorageDriverBase';
@@ -15,7 +15,7 @@ export default class StorageDriverMemory extends StorageDriverBase {
}
public async read(itemId: string): Promise<Buffer> {
if (!(itemId in this.data_)) throw new CustomError(`No such item: ${itemId}`, CustomErrorCode.NotFound);
if (!(itemId in this.data_)) throw new CustomError(`No such item: ${itemId}`, ErrorCode.NotFound);
return this.data_[itemId];
}

View File

@@ -1,5 +1,5 @@
import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectsCommand, ObjectIdentifier, HeadObjectCommand } from '@aws-sdk/client-s3';
import { CustomError, CustomErrorCode } from '../../../utils/errors';
import { CustomError, ErrorCode } from '../../../utils/errors';
import { StorageDriverConfig, StorageDriverType } from '../../../utils/types';
import StorageDriverBase from './StorageDriverBase';
@@ -60,7 +60,7 @@ export default class StorageDriverS3 extends StorageDriverBase {
return stream2buffer(response.Body);
} catch (error) {
if (error?.$metadata?.httpStatusCode === 404) throw new CustomError(`No such item: ${itemId}`, CustomErrorCode.NotFound);
if (error?.$metadata?.httpStatusCode === 404) throw new CustomError(`No such item: ${itemId}`, ErrorCode.NotFound);
error.message = `Could not get item "${itemId}": ${error.message}`;
throw error;
}

View File

@@ -2,7 +2,7 @@
import config from '../../../config';
import { Item } from '../../../services/database/types';
import { CustomErrorCode } from '../../../utils/errors';
import { ErrorCode } from '../../../utils/errors';
import { createUserAndSession, db, makeNoteSerializedBody, models } from '../../../utils/testing/testUtils';
import { Config, StorageDriverConfig, StorageDriverMode } from '../../../utils/types';
import newModelFactory from '../../factory';
@@ -291,6 +291,6 @@ export function shouldThrowNotFoundIfNotExist(driverConfig: StorageDriverConfig)
}
expect(error).toBeTruthy();
expect(error.code).toBe(CustomErrorCode.NotFound);
expect(error.code).toBe(ErrorCode.NotFound);
});
}

View File

@@ -1,16 +1,6 @@
export enum ErrorCode {
ResyncRequired = 'resyncRequired',
NoPathForItem = 'noPathForItem',
HasExclusiveSyncLock = 'hasExclusiveLock',
HasSyncLock = 'hasSyncLock',
NoSub = 'no_sub',
NoStripeSub = 'no_stripe_sub',
InvalidOrigin = 'invalidOrigin',
}
export interface ErrorOptions {
details?: any;
code?: ErrorCode;
code?: string;
}
// For explanation of the setPrototypeOf call, see:
@@ -19,10 +9,10 @@ export class ApiError extends Error {
public static httpCode: number = 400;
public httpCode: number;
public code: ErrorCode;
public code: string;
public details: any;
public constructor(message: string, httpCode: number = null, code: ErrorCode | ErrorOptions = undefined) {
public constructor(message: string, httpCode: number = null, code: string | ErrorOptions = undefined) {
super(message);
this.httpCode = httpCode === null ? 400 : httpCode;
@@ -40,7 +30,7 @@ export class ApiError extends Error {
}
export class ErrorWithCode extends ApiError {
public constructor(message: string, code: ErrorCode) {
public constructor(message: string, code: string) {
super(message, null, code);
}
}
@@ -57,7 +47,7 @@ export class ErrorMethodNotAllowed extends ApiError {
export class ErrorNotFound extends ApiError {
public static httpCode: number = 404;
public constructor(message: string = 'Not Found', code: ErrorCode = undefined) {
public constructor(message: string = 'Not Found', code: string = undefined) {
super(message, ErrorNotFound.httpCode, code);
Object.setPrototypeOf(this, ErrorNotFound.prototype);
}
@@ -104,7 +94,7 @@ export class ErrorUnprocessableEntity extends ApiError {
export class ErrorConflict extends ApiError {
public static httpCode: number = 409;
public constructor(message: string = 'Conflict', code: ErrorCode = undefined) {
public constructor(message: string = 'Conflict', code: string = undefined) {
super(message, ErrorConflict.httpCode, code);
Object.setPrototypeOf(this, ErrorConflict.prototype);
}
@@ -114,7 +104,7 @@ export class ErrorResyncRequired extends ApiError {
public static httpCode: number = 400;
public constructor(message: string = 'Delta cursor is invalid and the complete data should be resynced') {
super(message, ErrorResyncRequired.httpCode, ErrorCode.ResyncRequired);
super(message, ErrorResyncRequired.httpCode, 'resyncRequired');
Object.setPrototypeOf(this, ErrorResyncRequired.prototype);
}
}
@@ -166,13 +156,13 @@ export function errorToPlainObject(error: any): PlainObjectError {
return output;
}
export enum CustomErrorCode {
export enum ErrorCode {
NotFound,
}
export class CustomError extends Error {
public code: CustomErrorCode;
public constructor(message: string, code: CustomErrorCode) {
public code: ErrorCode;
public constructor(message: string, code: ErrorCode) {
super(message);
this.code = code;
Object.setPrototypeOf(this, CustomError.prototype);

View File

@@ -1,6 +1,6 @@
import config, { baseUrl } from '../config';
import { Item, ItemAddressingType, User, Uuid } from '../services/database/types';
import { ErrorBadRequest, ErrorCode, ErrorForbidden, ErrorNotFound } from './errors';
import { ErrorBadRequest, ErrorForbidden, ErrorNotFound } from './errors';
import Router from './Router';
import { AppContext, HttpMethod, RouteType } from './types';
import { URL } from 'url';
@@ -206,7 +206,7 @@ export async function execRequest(routes: Routers, ctx: AppContext): Promise<Exe
if (!match) throw new ErrorNotFound();
const endPoint = match.route.findEndPoint(ctx.request.method as HttpMethod, match.subPath.schema);
if (ctx.URL && !isValidOrigin(ctx.URL.origin, baseUrl(endPoint.type), endPoint.type)) throw new ErrorNotFound(`Invalid origin: ${ctx.URL.origin}`, ErrorCode.InvalidOrigin);
if (ctx.URL && !isValidOrigin(ctx.URL.origin, baseUrl(endPoint.type), endPoint.type)) throw new ErrorNotFound(`Invalid origin: ${ctx.URL.origin}`, 'invalidOrigin');
const isPublicRoute = match.route.isPublic(match.subPath.schema);

View File

@@ -5,7 +5,7 @@ import { Subscription, Uuid } from '../services/database/types';
import { Models } from '../models/factory';
import { AccountType } from '../models/UserModel';
import { findPrice, PricePeriod } from '@joplin/lib/utils/joplinCloud';
import { ErrorWithCode, ErrorCode } from './errors';
import { ErrorWithCode } from './errors';
const stripeLib = require('stripe');
export interface SubscriptionInfo {
@@ -33,11 +33,11 @@ export function accountTypeToPriceId(accountType: AccountType): string {
export async function subscriptionInfoByUserId(models: Models, userId: Uuid): Promise<SubscriptionInfo> {
const sub = await models.subscription().byUserId(userId);
if (!sub) throw new ErrorWithCode('Could not retrieve subscription info', ErrorCode.NoSub);
if (!sub) throw new ErrorWithCode('Could not retrieve subscription info', 'no_sub');
const stripe = initStripe();
const stripeSub = await stripe.subscriptions.retrieve(sub.stripe_subscription_id);
if (!stripeSub) throw new ErrorWithCode('Could not retrieve Stripe subscription', ErrorCode.NoStripeSub);
if (!stripeSub) throw new ErrorWithCode('Could not retrieve Stripe subscription', 'no_stripe_sub');
return { sub, stripeSub };
}

View File

@@ -466,7 +466,7 @@ export async function expectThrow(asyncFn: Function, errorCode: any = undefined)
return thrownError;
}
export async function expectHttpError(asyncFn: Function, expectedHttpCode: number, expectedErrorCode: string = null): Promise<void> {
export async function expectHttpError(asyncFn: Function, expectedHttpCode: number): Promise<void> {
let thrownError = null;
try {
@@ -479,10 +479,6 @@ export async function expectHttpError(asyncFn: Function, expectedHttpCode: numbe
expect('not throw').toBe('throw');
} else {
expect(thrownError.httpCode).toBe(expectedHttpCode);
if (expectedErrorCode !== null) {
expect(thrownError.code).toBe(expectedErrorCode);
}
}
}
@@ -533,7 +529,6 @@ is_shared: 1
share_id: ${note.share_id || ''}
conflict_original_id:
master_key_id:
user_data:
type_: 1`;
}
@@ -550,7 +545,6 @@ encryption_applied: 0
parent_id: ${folder.parent_id || ''}
is_shared: 0
share_id: ${folder.share_id || ''}
user_data:
type_: 2`;
}

View File

@@ -24,7 +24,7 @@
"@joplin/renderer": "~2.11",
"@joplin/utils": "~2.11",
"compare-versions": "3.6.0",
"dayjs": "1.11.8",
"dayjs": "1.11.7",
"execa": "4.1.0",
"fs-extra": "11.1.1",
"gettext-parser": "6.0.0",

View File

@@ -101,32 +101,34 @@ async function createRelease(projectName: string, name: string, tagName: string,
console.info(`Creating release: ${suffix}`);
// if (name === '32bit') {
// await patcher.updateFileContent(`${rnDir}/android/app/build.gradle`, async (content: string) => {
// content = content.replace(/abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"/, 'abiFilters "armeabi-v7a", "x86"');
// content = content.replace(/include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"/, 'include "armeabi-v7a", "x86"');
// return content;
// });
// }
if (name === '32bit') {
await patcher.updateFileContent(`${rnDir}/android/app/build.gradle`, async (content: string) => {
content = content.replace(/abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"/, 'abiFilters "armeabi-v7a", "x86"');
content = content.replace(/include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"/, 'include "armeabi-v7a", "x86"');
return content;
});
}
// if (name !== 'vosk') {
// await patcher.updateFileContent(`${rnDir}/services/voiceTyping/vosk.android.ts`, async (_content: string) => {
// return readFile(`${rnDir}/services/voiceTyping/vosk.ios.ts`, 'utf8');
// });
if (name !== 'vosk') {
await patcher.updateFileContent(`${rnDir}/services/voiceTyping/vosk.android.ts`, async (_content: string) => {
return readFile(`${rnDir}/services/voiceTyping/vosk.ios.ts`, 'utf8');
});
// await patcher.updateFileContent(`${rnDir}/android/app/build.gradle`, async (content: string) => {
// content = content.replace(/\s+"react-native-vosk": ".*",/, '');
// return content;
// });
// }
await patcher.updateFileContent(`${rnDir}/android/app/build.gradle`, async (content: string) => {
content = content.replace(/\s+"react-native-vosk": ".*",/, '');
return content;
});
// if (name === 'vosk') {
// await patcher.updateFileContent(`${rnDir}/android/app/build.gradle`, async (content: string) => {
// content = content.replace(/(\s+)applicationId "net.cozic.joplin"/, '$1applicationId "net.cozic.joplin.mod"');
// content = content.replace(/(\s+)versionName "(\d+\.\d+\.\d+)"/, '$1versionName "$2-mod"');
// return content;
// });
// }
await patcher.removeFile(`${rnDir}/android/app/src/main/assets/model-fr-fr`);
}
if (name === 'vosk') {
await patcher.updateFileContent(`${rnDir}/android/app/build.gradle`, async (content: string) => {
content = content.replace(/(\s+)applicationId "net.cozic.joplin"/, '$1applicationId "net.cozic.joplin.mod"');
content = content.replace(/(\s+)versionName "(\d+\.\d+\.\d+)"/, '$1versionName "$2-mod"');
return content;
});
}
const apkFilename = `joplin-v${suffix}.apk`;
const apkFilePath = `${releaseDir}/${apkFilename}`;
@@ -242,7 +244,7 @@ async function main() {
const version = gradleVersionName(newContent);
const tagName = `android-v${version}`;
// const releaseNames = ['main', '32bit', 'vosk'];
const releaseNames = ['main'];
const releaseNames = ['main', 'vosk'];
const releaseFiles: Record<string, Release> = {};
const mainProjectName = 'joplin-android';
const modProjectName = 'joplin-android-mod';

View File

@@ -181,7 +181,6 @@ command | 16
| share_id | text | |
| conflict_original_id | text | |
| master_key_id | text | |
| user_data | text | |
| body_html | text | Note body, in HTML format |
| base_url | text | If `body_html` is provided and contains relative URLs, provide the `base_url` parameter too so that all the URLs can be converted to absolute ones. The base URL is basically where the HTML was fetched from, minus the query (everything after the '?'). For example if the original page was `https://stackoverflow.com/search?q=%5Bjava%5D+test`, the base URL is `https://stackoverflow.com/search`. |
| image_data_url | text | An image to attach to the note, in [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format. |
@@ -258,7 +257,6 @@ This is actually a notebook. Internally notebooks are called "folders".
| share_id | text | |
| master_key_id | text | |
| icon | text | |
| user_data | text | |
## GET /folders
@@ -308,7 +306,6 @@ Deletes the folder with ID :id
| is_shared | int | |
| share_id | text | |
| master_key_id | text | |
| user_data | text | |
## GET /resources
@@ -384,7 +381,6 @@ Deletes the resource with ID :id
| encryption_applied | int | |
| is_shared | int | |
| parent_id | text | |
| user_data | text | |
## GET /tags

View File

@@ -1,13 +1,5 @@
# Joplin Server Changelog
## [server-v2.11.2](https://github.com/laurent22/joplin/releases/tag/server-v2.11.2) - 2023-06-20T08:40:54Z
- Improved: Allow giving a different version number to forks (091eff9)
- Improved: Improve performance and reliability when adding an item (17888a2)
- Improved: Process orphaned items (84b130e)
- Improved: Updated packages @koa/cors (v4), aws, buildTools, domhandler (v5), domutils (v3.1.0), formidable (v3), highlight.js (v11.8.0), jsdom (v21.1.2), markdown-it-multimd-table (v4.2.2), nodemailer (v6.9.3), sass (v1.62.1), sharp (v0.32.1), tar (v6.1.15), yargs (v17.7.2)
- Fixed: Fixed copyright year (a7e185e)
## [server-v2.11.1](https://github.com/laurent22/joplin/releases/tag/server-v2.11.1) - 2023-05-15T17:16:13Z
- Improved: Allow setting NTP server using NTP_SERVER env variable (#8153)

View File

@@ -24,7 +24,7 @@ The overall architecture for each application is as such:
- Configuration: The application is configured using a `settings.json` file. Its schema is available online: https://joplinapp.org/schema/settings.json
<img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/architecture/Application.png" style="max-width: 100%;"/>
<img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/architecture/Application.png" style="max-width: 100%;"></div>
### Desktop application
@@ -56,7 +56,7 @@ A typical Joplin Server installation will use the following elements:
This is a typical Joplin Server installation, but many of its components can be configured - for example it is possible to use a different database engine, or to use the filesystem instead of AWS S3. Any reverse proxy would also work - using Nginx is not required.
<img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/architecture/JoplinServer.png" style="max-width: 100%;"/>
<img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/architecture/JoplinServer.png" style="max-width: 100%;"></div>
## Web Clipper

View File

@@ -1,44 +0,0 @@
# Debugging Server project with vscode
Using a debugger sometimes is much easier than trying to just print things to understand a bug,
for the server project we have a configuration that makes it easy for everyone to run in debug mode inside vscode.
In the debug screen you can select "Server" and hit the play button to start the process.
![image](https://github.com/laurent22/joplin/assets/5051088/cda6e3d6-32f3-4997-bd16-8413a6c962c5)
Before running the server there is a task called `transpile-server` that runs `yarn tsc` to make sure that the
latest changes in the `package/server` are being executed.
https://github.com/laurent22/joplin/blob/4f37e6073ad549fe72206e2a125c27174b5d96a9/joplin.code-workspace#L360-L365
In a optimal solution we would be doing this `transpile-server` task in the `tasks.json` file
(inside the `tasks.json` we can use a `type` `npm` for the task that is more ergonomic)
If there are any new environment variables that need to be included before execution, there is a place where they can be added:
https://github.com/laurent22/joplin/blob/4f37e6073ad549fe72206e2a125c27174b5d96a9/joplin.code-workspace#L387-L391
Something that we could do to improve the experience is adding a build process to other projects that are used by the server project,
but this still can be done with other terminals open.
### Modifying the configuration:
To modify the configuration, for now, it is all in the `joplin.code-workspace` file, but if needed we could also break
the configuration into two files, one for the `launch.json` and other for the `tasks.json`.
### References:
[vscode debugger](https://code.visualstudio.com/docs/editor/debugging)
[Launch option to vscode workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces#_debugging)
[vscode tasks](https://code.visualstudio.com/docs/editor/tasks)
### More images:
![image](https://github.com/laurent22/joplin/assets/5051088/1346d938-c376-4cab-82a7-98deb4283fe8)
![image](https://github.com/laurent22/joplin/assets/5051088/b3a12b9f-704c-4dc8-b2bd-14ba7a1c4759)
![image](https://github.com/laurent22/joplin/assets/5051088/c45becc4-44b7-4f95-9d49-421517e29592)

View File

@@ -55,6 +55,7 @@
"browserify",
"codemirror",
"cspell",
"electron",
"file-loader",
"gradle",
"html-webpack-plugin",
@@ -71,10 +72,6 @@
"webpack-cli",
"webpack",
// Electron and dependencies should be updated manually.
"electron",
"electron-builder",
// pdfjs-dist requires the canvas package, which cannot be built in
// Windows.
//
@@ -120,18 +117,6 @@
"node-fetch",
"execa",
"open",
"pretty-bytes",
"strip-ansi",
"formidable",
// @koa/cors has undocumented breaking changes, and the package is not
// well supported so we're stuck with latest v3 for now
"@koa/cors",
// Can't upgrade beyond 2.x because it doesn't work with Electron. If trying to
// upgrade again, check that adding a link from the CodeMirror editor works.
// See /packages/app-desktop/gui/dialogs.ts
"smalltalk",
// Don't think we can trust this lib because they don't release a
// changelog and the latest version appears to have breaking changes

219
yarn.lock
View File

@@ -2722,7 +2722,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
version: 7.22.5
resolution: "@babel/runtime@npm:7.22.5"
dependencies:
@@ -3408,16 +3408,6 @@ __metadata:
languageName: node
linkType: hard
"@electron/notarize@npm:1.2.4":
version: 1.2.4
resolution: "@electron/notarize@npm:1.2.4"
dependencies:
debug: ^4.1.1
fs-extra: ^9.0.1
checksum: 3aa19fb247f9297b96a25f1a082f552e0c78a726ddfc98de9cdd4e4b092fc36fe07d680b762dd5a2bceda97b1044d3a0e6d9eadc5022f7c329a1fcf081133c9b
languageName: node
linkType: hard
"@electron/remote@npm:2.0.9":
version: 2.0.9
resolution: "@electron/remote@npm:2.0.9"
@@ -4274,7 +4264,6 @@ __metadata:
7zip-bin-linux: ^1.0.1
7zip-bin-mac: ^1.0.1
7zip-bin-win: ^2.1.1
"@electron/notarize": 1.2.4
"@electron/remote": 2.0.9
"@fortawesome/fontawesome-free": 5.15.4
"@joeattardi/emoji-button": 4.6.4
@@ -4295,6 +4284,7 @@ __metadata:
debounce: 1.2.1
electron: 19.1.4
electron-builder: 22.11.7
electron-notarize: 1.2.2
electron-rebuild: 3.2.9
electron-window-state: 5.0.3
formatcoords: 1.1.3
@@ -4319,7 +4309,7 @@ __metadata:
react: 18.2.0
react-datetime: 3.2.0
react-dom: 18.2.0
react-redux: 8.0.7
react-redux: 8.0.5
react-select: 5.7.3
react-test-renderer: 18.2.0
react-toggle-button: 2.2.0
@@ -4329,7 +4319,7 @@ __metadata:
roboto-fontface: 0.10.0
smalltalk: 2.5.1
sqlite3: 5.1.6
styled-components: 5.3.11
styled-components: 5.3.10
styled-system: 5.1.5
taboverride: 4.0.3
tinymce: 5.10.6
@@ -4371,8 +4361,8 @@ __metadata:
"@joplin/tools": ~2.11
"@lezer/highlight": 1.1.4
"@react-native-community/clipboard": 1.5.1
"@react-native-community/datetimepicker": 7.1.0
"@react-native-community/geolocation": 3.0.6
"@react-native-community/datetimepicker": 7.0.1
"@react-native-community/geolocation": 2.1.0
"@react-native-community/netinfo": 9.3.10
"@react-native-community/push-notification-ios": 1.11.0
"@react-native-community/slider": 4.4.2
@@ -4435,8 +4425,7 @@ __metadata:
react-native-version-info: 1.1.1
react-native-vosk: 0.1.12
react-native-webview: 12.4.0
react-native-zip-archive: 6.0.9
react-redux: 8.0.7
react-redux: 7.2.9
redux: 4.2.1
rn-fetch-blob: 0.12.0
stream: 0.0.2
@@ -4444,7 +4433,7 @@ __metadata:
string-natural-compare: 3.0.1
timers: 0.1.1
ts-jest: 29.1.0
ts-loader: 9.4.3
ts-loader: 9.4.2
ts-node: 10.9.1
typescript: 5.0.2
uglify-js: 3.17.4
@@ -4463,7 +4452,7 @@ __metadata:
"@typescript-eslint/parser": 5.59.0
coveralls: 3.1.1
domelementtype: 2.3.0
domhandler: 5.0.3
domhandler: 3.3.0
domutils: 3.1.0
entities: 2.2.0
eslint: 8.39.0
@@ -4611,9 +4600,9 @@ __metadata:
react: 18.2.0
react-dom: 18.2.0
style-loader: 3.3.3
styled-components: 5.3.11
styled-components: 5.3.10
ts-jest: 29.1.0
ts-loader: 9.4.3
ts-loader: 9.4.2
typescript: 5.0.2
webpack: 5.74.0
webpack-cli: 4.10.0
@@ -4720,7 +4709,7 @@ __metadata:
"@joplin/lib": ~2.11
"@joplin/renderer": ~2.11
"@joplin/tools": ~2.11
"@koa/cors": 3.4.3
"@koa/cors": 3.1.0
"@rmp135/sql-ts": 1.16.0
"@types/formidable": 2.0.6
"@types/fs-extra": 11.0.1
@@ -4738,14 +4727,14 @@ __metadata:
bulma: 0.9.4
bulma-prefers-dark: 0.1.0-beta.1
compare-versions: 3.6.0
dayjs: 1.11.8
formidable: 2.1.2
dayjs: 1.11.7
formidable: 2.1.1
fs-extra: 11.1.1
gulp: 4.0.2
html-entities: 1.4.0
jest: 29.5.0
jest-expect-message: 1.1.3
jquery: 3.7.0
jquery: 3.6.4
jsdom: 21.1.2
knex: 2.4.2
koa: 2.14.2
@@ -4755,7 +4744,7 @@ __metadata:
node-cron: 3.0.2
node-env-file: 0.1.8
node-mocks-http: 1.12.2
nodemailer: 6.9.3
nodemailer: 6.9.2
nodemon: 2.0.22
pg: 8.10.0
pretty-bytes: 5.6.0
@@ -4789,7 +4778,7 @@ __metadata:
"@types/node-fetch": 2.6.3
"@types/yargs": 17.0.24
compare-versions: 3.6.0
dayjs: 1.11.8
dayjs: 1.11.7
execa: 4.1.0
fs-extra: 11.1.1
gettext-extractor: 3.7.2
@@ -4962,12 +4951,12 @@ __metadata:
languageName: node
linkType: hard
"@koa/cors@npm:3.4.3":
version: 3.4.3
resolution: "@koa/cors@npm:3.4.3"
"@koa/cors@npm:3.1.0":
version: 3.1.0
resolution: "@koa/cors@npm:3.1.0"
dependencies:
vary: ^1.1.2
checksum: 51a5b89b4ea19078272ea2702916c90375853b6cfb9b65bf7720653e99b36950c1fd90df66a77fb17839bb130dc7c9f83ccc0bcec40f9d09769f9ed77ced5f23
checksum: b6f18de404a967696fe19c9a8d35816e2a845be51f9aa49f1a8d31bbea095eaa20c35eceafe1b764a7e3a912052ca6eaf1965ac34b00020b1ec2823cb60a9b5d
languageName: node
linkType: hard
@@ -6636,22 +6625,22 @@ __metadata:
languageName: node
linkType: hard
"@react-native-community/datetimepicker@npm:7.1.0":
version: 7.1.0
resolution: "@react-native-community/datetimepicker@npm:7.1.0"
"@react-native-community/datetimepicker@npm:7.0.1":
version: 7.0.1
resolution: "@react-native-community/datetimepicker@npm:7.0.1"
dependencies:
invariant: ^2.2.4
checksum: c88d19213daec1dc5c6101ad8c694386d79167087e1058c44c2699585dc97c4630c18de214d0607a5e5acd84846a9bb1401fece04fa78b8a010a0290b715040d
checksum: 108724a239d086e165ab17fd39fac43a8daf91a852ea9fd40d5bf9a2dd984ad8a473079e6228bc7f9fed0d6cf5610b2f4438142765e228f25aef37f9c6c4f85f
languageName: node
linkType: hard
"@react-native-community/geolocation@npm:3.0.6":
version: 3.0.6
resolution: "@react-native-community/geolocation@npm:3.0.6"
"@react-native-community/geolocation@npm:2.1.0":
version: 2.1.0
resolution: "@react-native-community/geolocation@npm:2.1.0"
peerDependencies:
react: "*"
react-native: "*"
checksum: 813df03599c064639bd5fe90922ab80d20d6224deffc8c6bfb02b7ade3d20f80e1ef6ac19c5d4394e901cca9a9fdb36eceda334a0ea6213e47c3c066921d09a9
checksum: 43dd95a8f643af613cd4c8d0a9a557690a70d072f5985c94aa5e09e5cab41e35682a5b022080bb2f276d00029dee1341b0451fcbd8ad8b55a67bc5d98c28f8cc
languageName: node
linkType: hard
@@ -7692,6 +7681,18 @@ __metadata:
languageName: node
linkType: hard
"@types/react-redux@npm:^7.1.20":
version: 7.1.24
resolution: "@types/react-redux@npm:7.1.24"
dependencies:
"@types/hoist-non-react-statics": ^3.3.0
"@types/react": "*"
hoist-non-react-statics: ^3.3.0
redux: ^4.0.0
checksum: 6582246581331ac7fbbd44aa1f1c136c8a9c8febbcf462432ac81302263308c21e1a2e7868beb7f73bbcb52a8e67935d133cb37f5bdcb6564eaff3a811805101
languageName: node
linkType: hard
"@types/react-transition-group@npm:^4.4.0":
version: 4.4.5
resolution: "@types/react-transition-group@npm:4.4.5"
@@ -13348,10 +13349,10 @@ __metadata:
languageName: node
linkType: hard
"dayjs@npm:1.11.8":
version: 1.11.8
resolution: "dayjs@npm:1.11.8"
checksum: 4fe04b6df98ba6e5f89b49d80bba603cbf01e21a1b4a24ecb163c94c0ba5324a32ac234a139cee654f89d5277a2bcebca5347e6676c28a0a6d1a90f1d34a42b8
"dayjs@npm:1.11.7":
version: 1.11.7
resolution: "dayjs@npm:1.11.7"
checksum: 5003a7c1dd9ed51385beb658231c3548700b82d3548c0cfbe549d85f2d08e90e972510282b7506941452c58d32136d6362f009c77ca55381a09c704e9f177ebb
languageName: node
linkType: hard
@@ -14263,12 +14264,12 @@ __metadata:
languageName: node
linkType: hard
"domhandler@npm:5.0.3, domhandler@npm:^5.0.2, domhandler@npm:^5.0.3":
version: 5.0.3
resolution: "domhandler@npm:5.0.3"
"domhandler@npm:3.3.0":
version: 3.3.0
resolution: "domhandler@npm:3.3.0"
dependencies:
domelementtype: ^2.3.0
checksum: 0f58f4a6af63e6f3a4320aa446d28b5790a009018707bce2859dcb1d21144c7876482b5188395a188dfa974238c019e0a1e610d2fc269a12b2c192ea2b0b131c
domelementtype: ^2.0.1
checksum: 850e5e9fee7834ab4314811e18bc1f4294d7eafbf6a79ad03cbe50cf964108935c97257ac248944d72a9312b4a18dfa8323e857d23278964dc83b1f124467fa3
languageName: node
linkType: hard
@@ -14281,6 +14282,15 @@ __metadata:
languageName: node
linkType: hard
"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3":
version: 5.0.3
resolution: "domhandler@npm:5.0.3"
dependencies:
domelementtype: ^2.3.0
checksum: 0f58f4a6af63e6f3a4320aa446d28b5790a009018707bce2859dcb1d21144c7876482b5188395a188dfa974238c019e0a1e610d2fc269a12b2c192ea2b0b131c
languageName: node
linkType: hard
"domino@npm:^2.1.6":
version: 2.1.6
resolution: "domino@npm:2.1.6"
@@ -14502,6 +14512,16 @@ __metadata:
languageName: node
linkType: hard
"electron-notarize@npm:1.2.2":
version: 1.2.2
resolution: "electron-notarize@npm:1.2.2"
dependencies:
debug: ^4.1.1
fs-extra: ^9.0.1
checksum: 98909bc90f0840a4bd3da0edc6895d719eb0d17942812a8c8f5a130d323c7fbaafd819245cfe9a6c5b5abfa8dd3b0eb2bd8ee9c0b2dcf0c3b238b6845730c368
languageName: node
linkType: hard
"electron-publish@npm:22.11.7":
version: 22.11.7
resolution: "electron-publish@npm:22.11.7"
@@ -16549,15 +16569,15 @@ __metadata:
languageName: node
linkType: hard
"formidable@npm:2.1.2":
version: 2.1.2
resolution: "formidable@npm:2.1.2"
"formidable@npm:2.1.1":
version: 2.1.1
resolution: "formidable@npm:2.1.1"
dependencies:
dezalgo: ^1.0.4
hexoid: ^1.0.0
once: ^1.4.0
qs: ^6.11.0
checksum: 81c8e5d89f5eb873e992893468f0de22c01678ca3d315db62be0560f9de1c77d4faefc9b1f4575098eb2263b3c81ba1024833a9fc3206297ddbac88a4f69b7a8
checksum: b407bf89abf0dc40e00efe20edd80aa60ff7e7f4928b3a7048b630d3bb61012f316b69cd3350ccc9aab2d4c682516fea917bc31aef3479d4a652ed97cf8ba9aa
languageName: node
linkType: hard
@@ -20541,10 +20561,10 @@ __metadata:
languageName: node
linkType: hard
"jquery@npm:3.7.0":
version: 3.7.0
resolution: "jquery@npm:3.7.0"
checksum: 907785e133afc427650a131af5fccef66a404885037513b3d4d7d63aee6409bcc32a39836868c60e59b05aa0fb8ace8961c18b2ee3ffdf6ffdb571d6d7cd88ff
"jquery@npm:3.6.4":
version: 3.6.4
resolution: "jquery@npm:3.6.4"
checksum: 8354f7bd0a0424aa714ee1b6b1ef74b410f834eb5c8501682289b358bc151f11677f11188b544f3bb49309d6ec4d15d1a5de175661250c206b06185a252f706f
languageName: node
linkType: hard
@@ -24272,10 +24292,10 @@ __metadata:
languageName: node
linkType: hard
"nodemailer@npm:6.9.3":
version: 6.9.3
resolution: "nodemailer@npm:6.9.3"
checksum: 3bea8316652c0578515d9146d2f24660e4855807520153f061d39af76b440a4f61b4e70f10fed35f8f12f115f6aea1aeb483ea7ba0337c0e3e675f117c41c611
"nodemailer@npm:6.9.2":
version: 6.9.2
resolution: "nodemailer@npm:6.9.2"
checksum: 76a7ea0cff221914d3ebb666f26f30c9374f84dd80c1b95bb186873295ecf8dea887ed36a1e8d5d90930c36b30f85ceac062fc9efb7bc75821fe497be4103d25
languageName: node
linkType: hard
@@ -26724,7 +26744,7 @@ __metadata:
languageName: node
linkType: hard
"qs@npm:^6.10.3":
"qs@npm:^6.10.3, qs@npm:^6.11.0":
version: 6.11.0
resolution: "qs@npm:6.11.0"
dependencies:
@@ -26733,15 +26753,6 @@ __metadata:
languageName: node
linkType: hard
"qs@npm:^6.11.0":
version: 6.11.2
resolution: "qs@npm:6.11.2"
dependencies:
side-channel: ^1.0.4
checksum: e812f3c590b2262548647d62f1637b6989cc56656dc960b893fe2098d96e1bd633f36576f4cd7564dfbff9db42e17775884db96d846bebe4f37420d073ecdc0b
languageName: node
linkType: hard
"qs@npm:^6.4.0, qs@npm:^6.9.4":
version: 6.10.2
resolution: "qs@npm:6.10.2"
@@ -27020,7 +27031,7 @@ __metadata:
languageName: node
linkType: hard
"react-is@npm:^17.0.1":
"react-is@npm:^17.0.1, react-is@npm:^17.0.2":
version: 17.0.2
resolution: "react-is@npm:17.0.2"
checksum: 9d6d111d8990dc98bc5402c1266a808b0459b5d54830bbea24c12d908b536df7883f268a7868cfaedde3dd9d4e0d574db456f84d2e6df9c4526f99bb4b5344d8
@@ -27369,11 +27380,11 @@ __metadata:
"react-native-vosk@patch:react-native-vosk@npm%3A0.1.12#./.yarn/patches/react-native-vosk-npm-0.1.12-76b1caaae8.patch::locator=root%40workspace%3A.":
version: 0.1.12
resolution: "react-native-vosk@patch:react-native-vosk@npm%3A0.1.12#./.yarn/patches/react-native-vosk-npm-0.1.12-76b1caaae8.patch::version=0.1.12&hash=5deb2c&locator=root%40workspace%3A."
resolution: "react-native-vosk@patch:react-native-vosk@npm%3A0.1.12#./.yarn/patches/react-native-vosk-npm-0.1.12-76b1caaae8.patch::version=0.1.12&hash=b82215&locator=root%40workspace%3A."
peerDependencies:
react: "*"
react-native: "*"
checksum: ca31cf6345f422b778c18bc2365f5114ab2c3e20214e684bbb77220e6476cd877bb5c256d88070b2235f01714e8cd8e5bee999b854894b066d31834b9b17dca4
checksum: 9451534de711f28274e117087a2c27266ee60ffc46723136841669c02345fd5644333a204c98c18d82e2c4221bb65906ac20529fd63fdcd5439c8aad86710e3b
languageName: node
linkType: hard
@@ -27390,16 +27401,6 @@ __metadata:
languageName: node
linkType: hard
"react-native-zip-archive@npm:6.0.9":
version: 6.0.9
resolution: "react-native-zip-archive@npm:6.0.9"
peerDependencies:
react: ">=16.8.6"
react-native: ">=0.60.0"
checksum: 96c83864a596fcd8a82f55a01fa5e97822c9e80c7cf55de6b011a27cc436fbf58371207cb61c63b19dbd22b852ee62b95935358b4b9a73686695133dffdcfe27
languageName: node
linkType: hard
"react-native@npm:0.70.6":
version: 0.70.6
resolution: "react-native@npm:0.70.6"
@@ -27503,9 +27504,30 @@ __metadata:
languageName: node
linkType: hard
"react-redux@npm:8.0.7":
version: 8.0.7
resolution: "react-redux@npm:8.0.7"
"react-redux@npm:7.2.9":
version: 7.2.9
resolution: "react-redux@npm:7.2.9"
dependencies:
"@babel/runtime": ^7.15.4
"@types/react-redux": ^7.1.20
hoist-non-react-statics: ^3.3.2
loose-envify: ^1.4.0
prop-types: ^15.7.2
react-is: ^17.0.2
peerDependencies:
react: ^16.8.3 || ^17 || ^18
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
checksum: 369a2bdcf87915659af9e5c55abfd9f52a84e43e0d12dcc108ed17dbe6933558b7b7fc12caa9c10c1a10a8be7df89454b6c96989d8573fedec1a772c94a1f145
languageName: node
linkType: hard
"react-redux@npm:8.0.5":
version: 8.0.5
resolution: "react-redux@npm:8.0.5"
dependencies:
"@babel/runtime": ^7.12.1
"@types/hoist-non-react-statics": ^3.3.1
@@ -27514,16 +27536,13 @@ __metadata:
react-is: ^18.0.0
use-sync-external-store: ^1.0.0
peerDependencies:
"@reduxjs/toolkit": ^1 || ^2.0.0-beta.0
"@types/react": ^16.8 || ^17.0 || ^18.0
"@types/react-dom": ^16.8 || ^17.0 || ^18.0
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
react-native: ">=0.59"
redux: ^4 || ^5.0.0-beta.0
redux: ^4
peerDependenciesMeta:
"@reduxjs/toolkit":
optional: true
"@types/react":
optional: true
"@types/react-dom":
@@ -27534,7 +27553,7 @@ __metadata:
optional: true
redux:
optional: true
checksum: d903aa79b12154258fd76b8e61fcf56f72123f69c31033b262805646371e23822cd0cd11d24194cda1e03569a7b1bf86e935cd57a9bab4523186804ed2742fac
checksum: a108f4f7ead6ac005e656d46051474a2bbdb31ede481bbbb3d8d779c1a35e1940b8655577cc5021313411864d305f67fc719aa48d6e5ed8288cf9cbe8b7042e4
languageName: node
linkType: hard
@@ -30663,9 +30682,9 @@ __metadata:
languageName: node
linkType: hard
"styled-components@npm:5.3.11":
version: 5.3.11
resolution: "styled-components@npm:5.3.11"
"styled-components@npm:5.3.10":
version: 5.3.10
resolution: "styled-components@npm:5.3.10"
dependencies:
"@babel/helper-module-imports": ^7.0.0
"@babel/traverse": ^7.4.5
@@ -30681,7 +30700,7 @@ __metadata:
react: ">= 16.8.0"
react-dom: ">= 16.8.0"
react-is: ">= 16.8.0"
checksum: 10edd4dae3b0231ec02d86bdd09c88e894eedfa7e9d4f8e562b09fb69c67a27d586cbcf35c785002d59b3bf11e6c0940b0efce40d13ae9ed148b26b1dc8f3284
checksum: 46dfea6c435516b22aae25798cf01f9b08423cfb517cfea9db760a18c941bddf79abaad9bedb9b3cca6faad24bd7eb4ef4dd21ae84b356ae29d279b2d571f7b7
languageName: node
linkType: hard
@@ -31728,9 +31747,9 @@ __metadata:
languageName: node
linkType: hard
"ts-loader@npm:9.4.3":
version: 9.4.3
resolution: "ts-loader@npm:9.4.3"
"ts-loader@npm:9.4.2":
version: 9.4.2
resolution: "ts-loader@npm:9.4.2"
dependencies:
chalk: ^4.1.0
enhanced-resolve: ^5.0.0
@@ -31739,7 +31758,7 @@ __metadata:
peerDependencies:
typescript: "*"
webpack: ^5.0.0
checksum: 139ed53bc60717d0ca231cdffbdef7566b9feda11c72fecc697983113f1266ccca2e1cdf191f841a43afa6b87d6afe57a0caf4feecf02f30845aa7ac6f2411a4
checksum: 6f306ee4c615c2a159fb177561e3fb86ca2cbd6c641e710d408a64b4978e1ff3f2c9733df07bff27d3f82efbfa7c287523d4306049510c7485ac2669a9c37eb0
languageName: node
linkType: hard