Compare commits

..
Author SHA1 Message Date
palerdot c7299cf679 fix(desktop): linux folder display bug fix 2023-03-10 12:36:40 +05:30
46 changed files with 301 additions and 705 deletions

No files matched your search

+1 -6
View File
@@ -183,12 +183,7 @@ if [[ $GIT_TAG_NAME = v* ]]; then
# cd "$ROOT_DIR/packages/tools"
# node bundleDefaultPlugins.js
cd "$ROOT_DIR/packages/app-desktop"
if [ "$IS_MACOS" == "1" ]; then
PYTHON_PATH=$(which python) USE_HARD_LINKS=false yarn run dist
else
USE_HARD_LINKS=false yarn run dist
fi
USE_HARD_LINKS=false yarn run dist
elif [[ $IS_LINUX = 1 ]] && [[ $GIT_TAG_NAME = $SERVER_TAG_PREFIX-* ]]; then
echo "Step: Building Docker Image..."
cd "$ROOT_DIR"
+1 -1
View File
@@ -35,7 +35,7 @@ describe('MdToHtml', () => {
const mdFilePath = `${basePath}/${mdFilename}`;
const htmlPath = `${basePath}/${filename(mdFilePath)}.html`;
if (mdFilename !== 'sanitize_15.md') continue;
// if (mdFilename !== 'sanitize_9.md') continue;
const mdToHtmlOptions: any = {
bodyOnly: true,
@@ -1 +0,0 @@
<div class="jop-noMdConv"><svg class="jop-noMdConv"><style class="jop-noMdConv">&lt;/svg>&lt;iframe srcdoc="&lt;script>top.require('child_process').execSync('calc')&lt;/script>">&lt;/iframe>&lt;/div>
@@ -1 +0,0 @@
<div><svg><style></svg><iframe srcdoc="<script>top.require('child_process').execSync('calc')</script>"></iframe></div>
@@ -1 +0,0 @@
<a href="#" class="jop-noMdConv">XSS</a>
@@ -1 +0,0 @@
<a data-from-md="" href="javascript:top.require('child_process').execSync('open -a Calculator')">XSS</a>
@@ -1 +0,0 @@
<use href="data:image/svg+xml,&lt;svg id=&apos;x&apos; xmlns=&apos;http://www.w3.org/2000/svg&apos;&gt;&lt;image href=&apos;asdf&apos; onerror=&apos;top.require(`child_process`).execSync(`calc.exe`)&apos; /&gt;&lt;/svg&gt;#x" class="jop-noMdConv">
@@ -1 +0,0 @@
<svg><use href="data:image/svg+xml,&lt;svg id='x' xmlns='http://www.w3.org/2000/svg'&gt;&lt;image href='asdf' onerror='top.require(`child_process`).execSync(`calc.exe`)' /&gt;&lt;/svg&gt;#x" />

Before

Width:  |  Height:  |  Size: 193 B

@@ -1 +0,0 @@
<map name="test" class="jop-noMdConv"><area coords="0,0,1000,1000" href="#" class="jop-noMdConv"/></map><img usemap="#test" src="https://github.com/Ry0taK.png" class="jop-noMdConv"/>
@@ -1 +0,0 @@
<map name="test"><area coords="0,0,1000,1000" href="javascript:top.require(`child_process`).execSync(`calc.exe`)"></map><img usemap="#test" src="https://github.com/Ry0taK.png">
+1 -13
View File
@@ -80,7 +80,6 @@ const appDefaultState = createAppDefaultState(
class Application extends BaseApplication {
private checkAllPluginStartedIID_: any = null;
private initPluginServiceDone_: boolean = false;
public constructor() {
super();
@@ -259,9 +258,6 @@ class Application extends BaseApplication {
}
private async initPluginService() {
if (this.initPluginServiceDone_) return;
this.initPluginServiceDone_ = true;
const service = PluginService.instance();
const pluginRunner = new PluginRunner();
@@ -557,15 +553,7 @@ class Application extends BaseApplication {
bridge().addEventListener('nativeThemeUpdated', this.bridge_nativeThemeUpdated);
// We need to delay plugin initialisation until the main screen is
// ready. Otherwise plugins might try to modify the application layout,
// which will cause an error related to an empty layout item. This in
// turns will cause the screen to go white on startup.
//
// https://discourse.joplinapp.org/t/upgrade-produces-blank-window/31138/8
eventManager.on('mainScreenReady', () => {
void this.initPluginService();
});
await this.initPluginService();
this.setupContextMenu();
@@ -46,7 +46,6 @@ import PromptDialog from '../PromptDialog';
import NotePropertiesDialog from '../NotePropertiesDialog';
const PluginManager = require('@joplin/lib/services/PluginManager');
const ipcRenderer = require('electron').ipcRenderer;
import eventManager from '@joplin/lib/eventManager';
interface LayerModalState {
visible: boolean;
@@ -392,7 +391,6 @@ class MainScreenComponent extends React.Component<Props, State> {
componentDidMount() {
window.addEventListener('keydown', this.layoutModeListenerKeyDown);
eventManager.emit('mainScreenReady');
}
componentWillUnmount() {
+3 -5
View File
@@ -21,7 +21,7 @@ import checkForUpdates from '../checkForUpdates';
const { connect } = require('react-redux');
import { reg } from '@joplin/lib/registry';
import { ProfileConfig } from '@joplin/lib/services/profileConfig/types';
import PluginService, { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
import PluginService from '@joplin/lib/services/plugins/PluginService';
const packageInfo = require('../packageInfo.js');
const { clipboard } = require('electron');
const Menu = bridge().Menu;
@@ -128,7 +128,6 @@ interface Props {
customCss: string;
locale: string;
profileConfig: ProfileConfig;
pluginSettings: PluginSettings;
}
const commandNames: string[] = menuCommandNames();
@@ -488,7 +487,8 @@ function useMenu(props: Props) {
}
function _showAbout() {
const v = versionInfo(packageInfo, PluginService.instance().enabledPlugins(props.pluginSettings));
const v = versionInfo(packageInfo, PluginService.instance().plugins);
const copyToClipboard = bridge().showMessageBox(v.message, {
icon: `${bridge().electronApp().buildDir()}/icons/128x128.png`,
@@ -931,7 +931,6 @@ function useMenu(props: Props) {
props['spellChecker.languages'],
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
props['spellChecker.enabled'],
props.pluginSettings,
props.customCss,
props.locale,
props.profileConfig,
@@ -987,7 +986,6 @@ const mapStateToProps = (state: AppState) => {
['folders.sortOrder.field']: state.settings['folders.sortOrder.field'],
['notes.sortOrder.reverse']: state.settings['notes.sortOrder.reverse'],
['folders.sortOrder.reverse']: state.settings['folders.sortOrder.reverse'],
pluginSettings: state.settings['plugins.states'],
showNoteCounts: state.settings.showNoteCounts,
uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,
showCompletedTodos: state.settings.showCompletedTodos,
@@ -77,6 +77,16 @@ function stripMarkup(markupLanguage: number, markup: string, options: any = null
return markupToHtml_.stripMarkup(markupLanguage, markup, options);
}
function createSyntheticClipboardEventWithoutHTML(): ClipboardEvent {
const clipboardData = new DataTransfer();
for (const format of clipboard.availableFormats()) {
if (format !== 'text/html') {
clipboardData.setData(format, clipboard.read(format));
}
}
return new ClipboardEvent('paste', { clipboardData });
}
interface TinyMceCommand {
name: string;
value?: any;
@@ -1066,24 +1076,24 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}
}
async function onKeyDown(event: any) {
function onKeyDown(event: any) {
// It seems "paste as text" is handled automatically on Windows and Linux,
// so we need to run the below code only on macOS. If we were to run this
// on Windows/Linux, we would have this double-paste issue:
// https://github.com/laurent22/joplin/issues/4243
// While "paste as text" functionality is handled by Windows and Linux, if we
// want to allow the user to customize the shortcut we need to prevent when it
// has the default value so it doesn't paste the content twice
// (one by the system and the other by our code)
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.code === 'KeyV') {
event.preventDefault();
pasteAsPlainText(null);
// Handle "paste as text". Note that when pressing CtrlOrCmd+Shift+V it's going
// to trigger the "keydown" event but not the "paste" event, so it's ok to process
// it here and we don't need to do anything special in onPaste
if (!shim.isWindows() && !shim.isLinux()) {
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.code === 'KeyV') {
pasteAsPlainText();
}
}
}
function onPasteAsText() {
pasteAsPlainText(null);
async function onPasteAsText() {
await onPaste(createSyntheticClipboardEventWithoutHTML());
}
editor.on(TinyMceEditorEvents.KeyUp, onKeyUp);
@@ -631,7 +631,7 @@ const mapStateToProps = (state: AppState) => {
], whenClauseContext)[0],
contentMaxWidth: state.settings['style.editor.contentMaxWidth'],
isSafeMode: state.settings.isSafeMode,
useCustomPdfViewer: false, // state.settings.useCustomPdfViewer,
useCustomPdfViewer: state.settings.useCustomPdfViewer,
};
};
@@ -294,7 +294,7 @@ const NoteListComponent = (props: Props) => {
useEffect(() => {
if (previousSelectedNoteIds !== props.selectedNoteIds && props.selectedNoteIds.length === 1) {
const id = props.selectedNoteIds[0];
const doRefocus = props.notes.length < previousNotes.length && !props.focusedField;
const doRefocus = props.notes.length < previousNotes.length;
for (let i = 0; i < props.notes.length; i++) {
if (props.notes[i].id === id) {
@@ -311,7 +311,8 @@ const NoteListComponent = (props: Props) => {
if (previousVisible !== props.visible) {
updateSizeState();
}
}, [previousSelectedNoteIds, previousNotes, previousVisible, props.selectedNoteIds, props.notes, props.focusedField, props.visible]);
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [previousSelectedNoteIds, previousNotes, previousVisible, props.selectedNoteIds, props.notes]);
const scrollNoteIndex_ = (keyCode: any, ctrlKey: any, metaKey: any, noteIndex: any) => {
@@ -558,7 +559,6 @@ const mapStateToProps = (state: AppState) => {
highlightedWords: state.highlightedWords,
plugins: state.pluginService.plugins,
customCss: state.customCss,
focusedField: state.focusedField,
};
};
@@ -23,5 +23,4 @@ export interface Props {
highlightedWords: string[];
provisionalNoteIds: string[];
visible: boolean;
focusedField: string;
}
@@ -1,6 +1,6 @@
import { AppState } from '../../app.reducer';
import * as React from 'react';
import { useEffect, useRef, useMemo, useState } from 'react';
import { useEffect, useRef } from 'react';
import SearchBar from '../SearchBar/SearchBar';
import Button, { ButtonLevel, ButtonSize, buttonSizePx } from '../Button/Button';
import CommandService from '@joplin/lib/services/CommandService';
@@ -11,13 +11,6 @@ import { _ } from '@joplin/lib/locale';
const { connect } = require('react-redux');
const styled = require('styled-components').default;
enum BaseBreakpoint {
Sm = 75,
Md = 80,
Lg = 120,
Xl = 474,
}
interface Props {
showNewNoteButtons: boolean;
sortOrderButtonsVisible: boolean;
@@ -25,15 +18,6 @@ interface Props {
sortOrderReverse: boolean;
notesParentType: string;
height: number;
width: number;
onContentHeightChange: (sameRow: boolean)=> void;
}
interface Breakpoints {
Sm: number;
Md: number;
Lg: number;
Xl: number;
}
const StyledRoot = styled.div`
@@ -50,9 +34,7 @@ const StyledButton = styled(Button)`
width: auto;
height: 26px;
min-height: 26px;
min-width: 37px;
max-width: none;
white-space: nowrap;
flex: 1 0 auto;
.fa, .fas {
font-size: 11px;
@@ -72,13 +54,7 @@ const StyledPairButtonR = styled(Button)`
width: auto;
`;
const TopRow = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
`;
const BottomRow = styled.div`
const RowContainer = styled.div`
display: flex;
flex-direction: row;
flex: 1 1 auto;
@@ -92,102 +68,7 @@ const SortOrderButtonsContainer = styled.div`
`;
function NoteListControls(props: Props) {
const [dynamicBreakpoints, setDynamicBreakpoints] = useState<Breakpoints>({ Sm: BaseBreakpoint.Sm, Md: BaseBreakpoint.Md, Lg: BaseBreakpoint.Lg, Xl: BaseBreakpoint.Xl });
const searchBarRef = useRef(null);
const newNoteRef = useRef(null);
const newTodoRef = useRef(null);
const noteControlsRef = useRef(null);
const searchAndSortRef = useRef(null);
const getTextWidth = (text: string): number => {
const canvas = document.createElement('canvas');
if (!canvas) throw new Error('Failed to create canvas element');
const ctx = canvas.getContext('2d');
if (!ctx) throw new Error('Failed to get context');
const fontWeight = getComputedStyle(newNoteRef.current).getPropertyValue('font-weight');
const fontSize = getComputedStyle(newNoteRef.current).getPropertyValue('font-size');
const fontFamily = getComputedStyle(newNoteRef.current).getPropertyValue('font-family');
ctx.font = `${fontWeight} ${fontSize} ${fontFamily}`;
return ctx.measureText(text).width;
};
// Initialize language-specific breakpoints
useEffect(() => {
// Use the longest string to calculate the amount of extra width needed
const smAdditional = getTextWidth(_('note')) > getTextWidth(_('to-do')) ? getTextWidth(_('note')) : getTextWidth(_('to-do'));
const mdAdditional = getTextWidth(_('New note')) > getTextWidth(_('New to-do')) ? getTextWidth(_('New note')) : getTextWidth(_('New to-do'));
const Sm = BaseBreakpoint.Sm + smAdditional * 2;
const Md = BaseBreakpoint.Md + mdAdditional * 2;
const Lg = BaseBreakpoint.Lg + Md;
const Xl = BaseBreakpoint.Xl;
setDynamicBreakpoints({ Sm, Md, Lg, Xl });
}, []);
const breakpoint = useMemo(() => {
// Find largest breakpoint that width is less than
const index = Object.values(dynamicBreakpoints).findIndex(x => props.width < x);
return index === -1 ? dynamicBreakpoints.Xl : Object.values(dynamicBreakpoints)[index];
}, [props.width, dynamicBreakpoints]);
const noteButtonText = useMemo(() => {
if (breakpoint === dynamicBreakpoints.Sm) {
return '';
} else if (breakpoint === dynamicBreakpoints.Md) {
return _('note');
} else {
return _('New note');
}
}, [breakpoint, dynamicBreakpoints]);
const todoButtonText = useMemo(() => {
if (breakpoint === dynamicBreakpoints.Sm) {
return '';
} else if (breakpoint === dynamicBreakpoints.Md) {
return _('to-do');
} else {
return _('New to-do');
}
}, [breakpoint, dynamicBreakpoints]);
const noteIcon = useMemo(() => {
if (breakpoint === dynamicBreakpoints.Sm) {
return 'icon-note';
} else {
return 'fas fa-plus';
}
}, [breakpoint, dynamicBreakpoints]);
const todoIcon = useMemo(() => {
if (breakpoint === dynamicBreakpoints.Sm) {
return 'far fa-check-square';
} else {
return 'fas fa-plus';
}
}, [breakpoint, dynamicBreakpoints]);
const showTooltip = useMemo(() => {
if (breakpoint === dynamicBreakpoints.Sm) {
return true;
} else {
return false;
}
}, [breakpoint, dynamicBreakpoints.Sm]);
useEffect(() => {
if (breakpoint === dynamicBreakpoints.Xl) {
noteControlsRef.current.style.flexDirection = 'row';
searchAndSortRef.current.style.flex = '2 1 auto';
props.onContentHeightChange(true);
} else {
noteControlsRef.current.style.flexDirection = 'column';
props.onContentHeightChange(false);
}
}, [breakpoint, dynamicBreakpoints, props.onContentHeightChange]);
useEffect(() => {
CommandService.instance().registerRuntime('focusSearch', focusSearchRuntime(searchBarRef));
@@ -246,63 +127,64 @@ function NoteListControls(props: Props) {
if (!props.showNewNoteButtons) return null;
return (
<TopRow className="new-note-todo-buttons">
<StyledButton ref={newNoteRef}
<RowContainer>
<StyledButton
className="new-note-button"
tooltip={ showTooltip ? CommandService.instance().label('newNote') : '' }
iconName={noteIcon}
title={_('%s', noteButtonText)}
tooltip={CommandService.instance().label('newNote')}
iconName="fas fa-plus"
title={_('%s', 'New note')}
level={ButtonLevel.Primary}
size={ButtonSize.Small}
onClick={onNewNoteButtonClick}
/>
<StyledButton ref={newTodoRef}
<StyledButton
className="new-todo-button"
tooltip={ showTooltip ? CommandService.instance().label('newTodo') : '' }
iconName={todoIcon}
title={_('%s', todoButtonText)}
tooltip={CommandService.instance().label('newTodo')}
iconName="fas fa-plus"
title={_('%s', 'New to-do')}
level={ButtonLevel.Secondary}
size={ButtonSize.Small}
onClick={onNewTodoButtonClick}
/>
</TopRow>
</RowContainer>
);
}
return (
<StyledRoot ref={noteControlsRef}>
<StyledRoot>
{renderNewNoteButtons()}
<BottomRow ref={searchAndSortRef} className="search-and-sort">
<RowContainer>
<SearchBar inputRef={searchBarRef}/>
{showsSortOrderButtons() &&
<SortOrderButtonsContainer>
<StyledPairButtonL
className="sort-order-field-button"
tooltip={sortOrderFieldTooltip()}
iconName={sortOrderFieldIcon()}
level={ButtonLevel.Secondary}
size={ButtonSize.Small}
onClick={onSortOrderFieldButtonClick}
/>
<StyledPairButtonR
className="sort-order-reverse-button"
tooltip={CommandService.instance().label('toggleNotesSortOrderReverse')}
iconName={sortOrderReverseIcon()}
level={ButtonLevel.Secondary}
size={ButtonSize.Small}
onClick={onSortOrderReverseButtonClick}
/>
</SortOrderButtonsContainer>
}
</BottomRow>
<SortOrderButtonsContainer>
{showsSortOrderButtons() &&
<StyledPairButtonL
className="sort-order-field-button"
tooltip={sortOrderFieldTooltip()}
iconName={sortOrderFieldIcon()}
level={ButtonLevel.Secondary}
size={ButtonSize.Small}
onClick={onSortOrderFieldButtonClick}
/>
}
{showsSortOrderButtons() &&
<StyledPairButtonR
className="sort-order-reverse-button"
tooltip={CommandService.instance().label('toggleNotesSortOrderReverse')}
iconName={sortOrderReverseIcon()}
level={ButtonLevel.Secondary}
size={ButtonSize.Small}
onClick={onSortOrderReverseButtonClick}
/>
}
</SortOrderButtonsContainer>
</RowContainer>
</StyledRoot>
);
}
const mapStateToProps = (state: AppState) => {
return {
// TODO: showNewNoteButtons and the logic associated is not needed anymore.
showNewNoteButtons: true,
showNewNoteButtons: state.focusedField !== 'globalSearch',
sortOrderButtonsVisible: state.settings['notes.sortOrder.buttonsVisible'],
sortOrderField: state.settings['notes.sortOrder.field'],
sortOrderReverse: state.settings['notes.sortOrder.reverse'],
@@ -1,6 +1,6 @@
import { themeStyle } from '@joplin/lib/theme';
import * as React from 'react';
import { useMemo, useState } from 'react';
import { useMemo } from 'react';
import NoteList from '../NoteList/NoteList';
import NoteListControls from '../NoteListControls/NoteListControls';
import { Size } from '../ResizableLayout/utils/types';
@@ -22,15 +22,7 @@ const StyledRoot = styled.div`
export default function NoteListWrapper(props: Props) {
const theme = themeStyle(props.themeId);
const [controlHeight, setControlHeight] = useState(theme.topRowHeight);
const onContentHeightChange = (sameRow: boolean) => {
if (sameRow) {
setControlHeight(theme.topRowHeight);
} else {
setControlHeight(theme.topRowHeight * 2);
}
};
const controlHeight = theme.topRowHeight;
const noteListSize = useMemo(() => {
return {
@@ -41,7 +33,7 @@ export default function NoteListWrapper(props: Props) {
return (
<StyledRoot>
<NoteListControls height={controlHeight} width={noteListSize.width} onContentHeightChange={onContentHeightChange}/>
<NoteListControls height={controlHeight}/>
<NoteList resizableLayoutEventEmitter={props.resizableLayoutEventEmitter} size={noteListSize} visible={props.visible}/>
</StyledRoot>
);
+9 -20
View File
@@ -26,15 +26,11 @@ export default class PromptDialog extends React.Component<Props, any> {
private focusInput_: boolean;
private styles_: any;
private styleKey_: string;
private menuIsOpened_: boolean = false;
constructor(props: Props) {
super(props);
this.answerInput_ = React.createRef();
this.select_menuOpen = this.select_menuOpen.bind(this);
this.select_menuClose = this.select_menuClose.bind(this);
}
UNSAFE_componentWillMount() {
@@ -43,7 +39,6 @@ export default class PromptDialog extends React.Component<Props, any> {
answer: this.props.defaultValue ? this.props.defaultValue : '',
});
this.focusInput_ = true;
this.menuIsOpened_ = false;
}
UNSAFE_componentWillReceiveProps(newProps: Props) {
@@ -57,15 +52,7 @@ export default class PromptDialog extends React.Component<Props, any> {
}
}
private select_menuOpen() {
this.menuIsOpened_ = true;
}
private select_menuClose() {
this.menuIsOpened_ = false;
}
public componentDidUpdate() {
componentDidUpdate() {
if (this.focusInput_ && this.answerInput_.current) this.answerInput_.current.focus();
this.focusInput_ = false;
}
@@ -237,14 +224,16 @@ export default class PromptDialog extends React.Component<Props, any> {
const onKeyDown = (event: any) => {
if (event.key === 'Enter') {
// If the dropdown is open, we don't close the dialog - instead
// the currently item will be selcted. If it is closed however
// we confirm the dialog.
if ((this.props.inputType === 'tags' || this.props.inputType === 'dropdown') && this.menuIsOpened_) {
if (this.props.inputType === 'tags' || this.props.inputType === 'dropdown') {
// Do nothing
} else {
onClose(true);
}
// } else if (this.answerInput_.current && !this.answerInput_.current.state.menuIsOpen) {
// // The menu will be open if the user is selecting a new item
// onClose(true);
// }
} else if (event.key === 'Escape') {
onClose(false);
}
@@ -257,9 +246,9 @@ export default class PromptDialog extends React.Component<Props, any> {
if (this.props.inputType === 'datetime') {
inputComp = <Datetime className="datetime-picker" value={this.state.answer} inputProps={{ style: styles.input }} dateFormat={time.dateFormat()} timeFormat={time.timeFormat()} onChange={(momentObject: any) => onDateTimeChange(momentObject)} />;
} else if (this.props.inputType === 'tags') {
inputComp = <CreatableSelect className="tag-selector" onMenuOpen={this.select_menuOpen} onMenuClose={this.select_menuClose} styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} value={this.state.answer} placeholder="" components={makeAnimated()} isMulti={true} isClearable={false} backspaceRemovesValue={true} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={(event: any) => onKeyDown(event)} />;
inputComp = <CreatableSelect className="tag-selector" styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} value={this.state.answer} placeholder="" components={makeAnimated()} isMulti={true} isClearable={false} backspaceRemovesValue={true} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={(event: any) => onKeyDown(event)} />;
} else if (this.props.inputType === 'dropdown') {
inputComp = <Select className="item-selector" onMenuOpen={this.select_menuOpen} onMenuClose={this.select_menuClose} styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} components={makeAnimated()} value={this.props.answer} defaultValue={this.props.defaultValue} isClearable={false} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={(event: any) => onKeyDown(event)} />;
inputComp = <Select className="item-selector" styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} components={makeAnimated()} value={this.props.answer} defaultValue={this.props.defaultValue} isClearable={false} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={(event: any) => onKeyDown(event)} />;
} else {
inputComp = <input style={styles.input} ref={this.answerInput_} value={this.state.answer} type="text" onChange={event => onChange(event)} onKeyDown={event => onKeyDown(event)} />;
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "2.10.20",
"version": "2.10.8",
"description": "Joplin for Desktop",
"main": "main.js",
"private": true,
@@ -27,7 +27,6 @@
},
"build": {
"appId": "net.cozic.joplin-desktop",
"compression": "normal",
"productName": "Joplin",
"npmRebuild": false,
"afterSign": "./tools/notarizeMacApp.js",
@@ -116,7 +115,7 @@
"@types/react-redux": "7.1.25",
"@types/styled-components": "5.1.26",
"electron": "19.1.4",
"electron-builder": "22.11.7",
"electron-builder": "23.6.0",
"electron-notarize": "1.2.2",
"electron-rebuild": "3.2.9",
"glob": "8.1.0",
@@ -138,6 +137,7 @@
"@fortawesome/fontawesome-free": "5.15.4",
"@joeattardi/emoji-button": "4.6.4",
"@joplin/lib": "~2.10",
"@joplin/pdf-viewer": "~2.10",
"@joplin/renderer": "~2.10",
"async-mutex": "0.4.0",
"codemirror": "5.65.9",
@@ -72,10 +72,10 @@ async function main() {
src: langSourceDir,
dest: `${buildLibDir}/tinymce/langs`,
},
// {
// src: resolve(__dirname, '../../pdf-viewer/dist'),
// dest: `${buildLibDir}/@joplin/pdf-viewer`,
// },
{
src: resolve(__dirname, '../../pdf-viewer/dist'),
dest: `${buildLibDir}/@joplin/pdf-viewer`,
},
];
const files = [
@@ -93,10 +93,10 @@ async function main() {
src: resolve(__dirname, '../../lib/services/plugins/sandboxProxy.js'),
dest: `${buildLibDir}/@joplin/lib/services/plugins/sandboxProxy.js`,
},
// {
// src: resolve(__dirname, '../../pdf-viewer/index.html'),
// dest: `${buildLibDir}/@joplin/pdf-viewer/index.html`,
// },
{
src: resolve(__dirname, '../../pdf-viewer/index.html'),
dest: `${buildLibDir}/@joplin/pdf-viewer/index.html`,
},
];
// First we delete all the destination directories, then we copy the files.
+2 -2
View File
@@ -150,8 +150,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097685
versionName "2.10.9"
versionCode 2097684
versionName "2.10.8"
// ndk {
// abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
// }
@@ -13,11 +13,7 @@ interface Props {
}
export default (props: Props) => {
// The initial prompt is there so that the user can choose to opt-in to
// biometrics auth the first time the app is launched. However since it
// doesn't work properly, we disable it. We only want the user to enable the
// feature after they've read the description in the config screen.
const [initialPromptDone, setInitialPromptDone] = useState(true); // useState(Setting.value('security.biometricsInitialPromptDone'));
const [initialPromptDone, setInitialPromptDone] = useState(Setting.value('security.biometricsInitialPromptDone'));
const [display, setDisplay] = useState(!!props.sensorInfo.supportedSensors && (props.sensorInfo.enabled || !initialPromptDone));
const [tryBiometricsCheck, setTryBiometricsCheck] = useState(initialPromptDone);
@@ -8,18 +8,6 @@ export interface SensorInfo {
}
export default async (): Promise<SensorInfo> => {
// Early exit if the feature is disabled, so that we don't make any
// FingerprintScanner scanner calls, since it seems they can fail and freeze
// the app.
if (!Setting.value('security.biometricsEnabled')) {
return {
enabled: false,
sensorsHaveChanged: false,
supportedSensors: '',
};
}
let hasChanged = false;
let supportedSensors = '';
+2 -2
View File
@@ -432,7 +432,7 @@ PODS:
- React
- RNSecureRandom (1.0.1):
- React
- RNShare (8.2.0):
- RNShare (8.1.0):
- React-Core
- RNVectorIcons (9.2.0):
- React-Core
@@ -751,7 +751,7 @@ SPEC CHECKSUMS:
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
RNSecureRandom: 07efbdf2cd99efe13497433668e54acd7df49fef
RNShare: b089c33619bbfb0a32bc4069c858b9274e694187
RNShare: 48b3113cd089a2be8ff0515c3ae7a46a4db8a76b
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
Yoga: 99caf8d5ab45e9d637ee6e0174ec16fbbb01bcfc
+1 -1
View File
@@ -501,7 +501,7 @@ async function initialize(dispatch: Function) {
if (Setting.value('env') === 'prod') {
await db.open({ name: getDatabaseName(currentProfile, isSubProfile) });
} else {
await db.open({ name: getDatabaseName(currentProfile, isSubProfile, '-1') });
await db.open({ name: getDatabaseName(currentProfile, isSubProfile) });
// await db.clearForTesting();
}
@@ -24,10 +24,9 @@ export const getResourceDir = (profile: Profile, isSubProfile: boolean) => {
return `${getProfilesRootDir()}/resources-${profile.id}`;
};
// The suffix is for debugging only
export const getDatabaseName = (profile: Profile, isSubProfile: boolean, suffix: string = '') => {
if (!isSubProfile) return `joplin${suffix}.sqlite`;
return `joplin-${profile.id}${suffix}.sqlite`;
export const getDatabaseName = (profile: Profile, isSubProfile: boolean) => {
if (!isSubProfile) return 'joplin.sqlite';
return `joplin-${profile.id}.sqlite`;
};
export const loadProfileConfig = async () => {
+5 -6
View File
@@ -45,13 +45,12 @@ export default class JoplinServerApi {
private options_: Options;
private session_: Session;
private debugRequests_: boolean = false;
private debugRequestsShowPasswords_: boolean = false;
public constructor(options: Options) {
this.options_ = options;
if (options.env !== Env.Dev) {
this.debugRequestsShowPasswords_ = false;
if (options.env === Env.Dev) {
// this.debugRequests_ = true;
}
}
@@ -98,15 +97,15 @@ export default class JoplinServerApi {
try {
const output = JSON.parse(o);
if (!output) return o;
if (output.password && !this.debugRequestsShowPasswords_) output.password = '******';
if (output.password) output.password = '******';
return JSON.stringify(output);
} catch (error) {
return o;
}
} else {
const output = { ...o };
if (output.password && !this.debugRequestsShowPasswords_) output.password = '******';
if (output['X-API-AUTH'] && !this.debugRequestsShowPasswords_) output['X-API-AUTH'] = '******';
if (output.password) output.password = '******';
if (output['X-API-AUTH']) output['X-API-AUTH'] = '******';
return output;
}
}
+3 -9
View File
@@ -96,20 +96,14 @@ export default class SyncTargetOneDrive extends BaseSyncTarget {
let context = Setting.value(`sync.${this.syncTargetId()}.context`);
context = context === '' ? null : JSON.parse(context);
let accountProperties = context ? context.accountProperties : null;
const api = this.api();
if (!accountProperties) {
accountProperties = await api.execAccountPropertiesRequest();
accountProperties = await this.api_.execAccountPropertiesRequest();
context ? context.accountProperties = accountProperties : context = { accountProperties: accountProperties };
Setting.setValue(`sync.${this.syncTargetId()}.context`, JSON.stringify(context));
}
api.setAccountProperties(accountProperties);
this.api_.setAccountProperties(accountProperties);
const appDir = await this.api().appDirectory();
// the appDir might contain non-ASCII characters
// /[^\u0021-\u00ff]/ is used in Node.js to detect the unescaped characters.
// See https://github.com/nodejs/node/blob/bbbf97b6dae63697371082475dc8651a6a220336/lib/_http_client.js#L176
const baseDir = RegExp(/[^\u0021-\u00ff]/).exec(appDir) !== null ? encodeURI(appDir) : appDir;
const fileApi = new FileApi(baseDir, new FileApiDriverOneDrive(this.api()));
const fileApi = new FileApi(appDir, new FileApiDriverOneDrive(this.api()));
fileApi.setSyncTargetId(this.syncTargetId());
fileApi.setLogger(this.logger());
return fileApi;
+2 -14
View File
@@ -175,10 +175,6 @@ export default class FileApiDriverJoplinServer {
// they can have names such as ".resources/xxxxxxxxxx'
}
private isRejectedBySyncTargetError(error: any) {
return error.code === 413 || error.code === 409 || error.httpCode === 413 || error.httpCode === 409;
}
public async put(path: string, content: any, options: any = null) {
try {
const output = await this.api().exec('PUT', `${this.apiFilePath_(path)}/content`, options && options.shareId ? { share_id: options.shareId } : null, content, {
@@ -186,7 +182,7 @@ export default class FileApiDriverJoplinServer {
}, options);
return output;
} catch (error) {
if (this.isRejectedBySyncTargetError(error)) {
if (error.code === 413) {
throw new JoplinError(error.message, 'rejectedByTarget');
}
throw error;
@@ -194,15 +190,7 @@ export default class FileApiDriverJoplinServer {
}
public async multiPut(items: MultiPutItem[], options: any = null) {
const output = await this.api().exec('PUT', 'api/batch_items', null, { items: items }, null, options);
for (const [, response] of Object.entries<any>(output.items)) {
if (response.error && this.isRejectedBySyncTargetError(response.error)) {
response.error.code = 'rejectedByTarget';
}
}
return output;
return this.api().exec('PUT', 'api/batch_items', null, { items: items }, null, options);
}
public async delete(path: string) {
+3 -15
View File
@@ -1,4 +1,4 @@
import Setting, { SettingItemType, SettingSectionSource, SettingStorage } from '../models/Setting';
import Setting, { SettingSectionSource, SettingStorage } from '../models/Setting';
import { setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow, msleep } from '../testing/test-utils';
import { readFile, stat, mkdirp, writeFile, pathExists, readdir } from 'fs-extra';
import Logger from '../Logger';
@@ -297,21 +297,12 @@ describe('models/Setting', () => {
expect(Setting.isSet('spellChecker.languages')).toBe(false);
}));
it('should load sub-profile settings', async () => {
it('should load sub-profile settings - 1', async () => {
await Setting.reset();
await Setting.registerSetting('non_builtin', {
public: true,
storage: SettingStorage.File,
isGlobal: true,
type: SettingItemType.Bool,
value: false,
});
Setting.setValue('locale', 'fr_FR'); // Global setting
Setting.setValue('theme', Setting.THEME_DARK); // Global setting
Setting.setValue('sync.target', 9); // Local setting
Setting.setValue('non_builtin', true); // Local setting
await Setting.saveAll();
await switchToSubProfileSettings();
@@ -320,9 +311,6 @@ describe('models/Setting', () => {
expect(Setting.value('theme')).toBe(Setting.THEME_DARK); // Should come from the root profile
expect(Setting.value('sync.target')).toBe(0); // Should come from the local profile
// Non-built-in variables are not copied
expect(() => Setting.value('non_builtin')).toThrow();
// Also check that the special loadOne() function works as expected
expect((await Setting.loadOne('locale')).value).toBe('fr_FR');
@@ -330,7 +318,7 @@ describe('models/Setting', () => {
expect((await Setting.loadOne('sync.target')).value).toBe(undefined);
});
it('should save sub-profile settings', async () => {
it('should save sub-profile settings - 2', async () => {
await Setting.reset();
Setting.setValue('locale', 'fr_FR'); // Global setting
Setting.setValue('theme', Setting.THEME_DARK); // Global setting
+5 -20
View File
@@ -9,7 +9,6 @@ import FileHandler, { SettingValues } from './settings/FileHandler';
import Logger from '../Logger';
import mergeGlobalAndLocalSettings from '../services/profileConfig/mergeGlobalAndLocalSettings';
import splitGlobalAndLocalSettings from '../services/profileConfig/splitGlobalAndLocalSettings';
import JoplinError from '../JoplinError';
const { sprintf } = require('sprintf-js');
const ObjectUtils = require('../ObjectUtils');
const { toTitleCase } = require('../string-utils.js');
@@ -313,7 +312,6 @@ class Setting extends BaseModel {
private static fileHandler_: FileHandler = null;
private static rootFileHandler_: FileHandler = null;
private static settingFilename_: string = 'settings.json';
private static buildInMetadata_: SettingItems = null;
static tableName() {
return 'settings';
@@ -408,7 +406,7 @@ class Setting extends BaseModel {
return output;
};
this.buildInMetadata_ = {
this.metadata_ = {
'clientId': {
value: '',
type: SettingItemType.String,
@@ -1386,7 +1384,7 @@ class Setting extends BaseModel {
useCustomPdfViewer: {
value: false,
type: SettingItemType.Bool,
public: false,
public: true,
advanced: true,
appTypes: [AppType.Desktop],
label: () => 'Use custom PDF viewer (Beta)',
@@ -1636,17 +1634,10 @@ class Setting extends BaseModel {
storage: SettingStorage.Database,
},
// The biometrics feature is disabled by default and marked as beta
// because it seems to cause a freeze or slow down startup on
// certain devices. May be the reason for:
//
// - https://discourse.joplinapp.org/t/on-android-when-joplin-gets-started-offline/29951/1
// - https://github.com/laurent22/joplin/issues/7956
'security.biometricsEnabled': {
value: false,
type: SettingItemType.Bool,
label: () => `${_('Use biometrics to secure access to the app')} (Beta)`,
description: () => 'Important: This is a beta feature and it is not compatible with certain devices. If the app no longer starts after enabling this or is very slow to start, please uninstall and reinstall the app.',
label: () => _('Use biometrics to secure access to the app'),
public: true,
appTypes: [AppType.Mobile],
},
@@ -1687,8 +1678,6 @@ class Setting extends BaseModel {
};
this.metadata_ = { ...this.buildInMetadata_ };
this.metadata_ = Object.assign(this.metadata_, this.customMetadata_);
if (this.constants_.env === Env.Dev) this.validateMetadata(this.metadata_);
@@ -1702,10 +1691,6 @@ class Setting extends BaseModel {
}
}
public static isBuiltinKey(key: string): boolean {
return key in this.buildInMetadata_;
}
public static customCssFilePath(filename: string): string {
return `${this.value('rootProfileDir')}/${filename}`;
}
@@ -1807,7 +1792,7 @@ class Setting extends BaseModel {
static settingMetadata(key: string): SettingItem {
const metadata = this.metadata();
if (!(key in metadata)) throw new JoplinError(`Unknown key: ${key}`, 'unknown_key');
if (!(key in metadata)) throw new Error(`Unknown key: ${key}`);
const output = Object.assign({}, metadata[key]);
output.key = key;
return output;
@@ -2252,7 +2237,7 @@ class Setting extends BaseModel {
static enumOptions(key: string) {
const metadata = this.metadata();
if (!metadata[key]) throw new JoplinError(`Unknown key: ${key}`, 'unknown_key');
if (!metadata[key]) throw new Error(`Unknown key: ${key}`);
if (!metadata[key].options) throw new Error(`No options for: ${key}`);
return metadata[key].options();
}
@@ -100,11 +100,6 @@ export default class PluginService extends BaseService {
return this.plugins_;
}
public enabledPlugins(pluginSettings: PluginSettings): Plugins {
const enabledPlugins = Object.fromEntries(Object.entries(this.plugins_).filter((p) => this.pluginEnabled(pluginSettings, p[0])));
return enabledPlugins;
}
public get pluginIds(): string[] {
return Object.keys(this.plugins_);
}
@@ -1,52 +1,20 @@
import Logger from '../../Logger';
import Setting from '../../models/Setting';
const logger = Logger.create('mergeGlobalAndLocalSettings');
export default (rootSettings: Record<string, any>, subProfileSettings: Record<string, any>) => {
const output: Record<string, any> = { ...subProfileSettings };
for (const k of Object.keys(output)) {
try {
const md = Setting.settingMetadata(k);
if (md.isGlobal) {
delete output[k];
if (k in rootSettings) output[k] = rootSettings[k];
}
} catch (error) {
if (error.code === 'unknown_key') {
// The root settings may contain plugin parameters, but the
// sub-profile won't necessarily have these plugins. In that
// case, the app will throw an error, but we can ignore it since
// we don't need this particular setting.
// https://github.com/laurent22/joplin/issues/8143
logger.info(`Ignoring unknown key in root settings: ${k}`);
}
const md = Setting.settingMetadata(k);
if (md.isGlobal) {
delete output[k];
if (k in rootSettings) output[k] = rootSettings[k];
}
}
for (const k of Object.keys(rootSettings)) {
// We only copy built-in key and not, for example, plugin keys, because
// those are plugin-specific
if (!Setting.isBuiltinKey(k)) {
logger.info(`Skipping non-built-in key: ${k}`);
continue;
}
try {
const md = Setting.settingMetadata(k);
if (md.isGlobal) {
output[k] = rootSettings[k];
}
} catch (error) {
if (error.code === 'unknown_key') {
// The root settings may contain plugin parameters, but the
// sub-profile won't necessarily have these plugins. In that
// case, the app will throw an error, but we can ignore it since
// we don't need this particular setting.
// https://github.com/laurent22/joplin/issues/8143
logger.info(`Ignoring unknown key in root settings: ${k}`);
}
const md = Setting.settingMetadata(k);
if (md.isGlobal) {
output[k] = rootSettings[k];
}
}
@@ -1,6 +1,5 @@
import { ModelType } from '../../BaseModel';
import { FileApi, MultiPutItem } from '../../file-api';
import JoplinError from '../../JoplinError';
import Logger from '../../Logger';
import BaseItem from '../../models/BaseItem';
import { BaseItemEntity } from '../database/types';
@@ -46,8 +45,7 @@ export default class ItemUploader {
// the regular upload.
logger.warn(`Pre-uploaded item updated_time has changed. It is going to be re-uploaded again: ${path} (From ${this.preUploadedItemUpdatedTimes_[path]} to ${local.updated_time})`);
} else {
const error = preUploadItem.error;
if (error) throw new JoplinError(error.message ? error.message : 'Unknown pre-upload error', error.code);
if (preUploadItem.error) throw new Error(preUploadItem.error.message ? preUploadItem.error.message : 'Unknown pre-upload error');
return;
}
}
+1 -1
View File
@@ -60,7 +60,7 @@ const globalStyle: any = {
toolbarPadding: 6,
appearance: 'light',
mainPadding: 12,
topRowHeight: 50,
topRowHeight: 81,
editorPaddingLeft: 8,
};
-2
View File
@@ -86,7 +86,6 @@ describe('getPluginLists', () => {
);
plugins[plugin.manifest.id] = plugin;
}
const v = versionInfo(packageInfo, plugins);
expect(v.body).toMatch(/\n\nPlugin1: 1\nPlugin2: 1\nPlugin3: 1/);
@@ -111,7 +110,6 @@ describe('getPluginLists', () => {
plugins[plugin.manifest.id] = plugin;
}
const v = versionInfo(packageInfo, plugins);
const body = '\n';
-2
View File
@@ -16,8 +16,6 @@ function getPluginLists(plugins: Plugins): PluginList {
}
}
pluginList.sort(Intl.Collator().compare);
let completeList = '';
let summary = '';
if (pluginList.length > 0) {
+6 -6
View File
@@ -9,12 +9,12 @@
"access": "restricted"
},
"scripts": {
"tsc_DISABLED": "tsc --project tsconfig.json",
"watch_DISABLED": "webpack --watch --config webpack.config.js --mode=development",
"build_DISABLED": "webpack --config webpack.config.js --mode=production",
"test_DISABLED": "jest",
"test-ci_DISABLED": "yarn test",
"postinstall_DISABLED": "yarn build"
"tsc": "tsc --project tsconfig.json",
"watch": "webpack --watch --config webpack.config.js --mode=development",
"build": "webpack --config webpack.config.js --mode=production",
"test": "jest",
"test-ci": "yarn test",
"postinstall": "yarn build"
},
"author": "Joplin",
"license": "AGPL-3.0-or-later",
+18 -52
View File
@@ -144,11 +144,6 @@ class HtmlUtils {
.replace(/</g, '&lt;');
}
private isAcceptedUrl(url: string): boolean {
url = url.toLowerCase();
return url.startsWith('https://') || url.startsWith('http://') || url.startsWith('mailto://');
}
public sanitizeHtml(html: string, options: any = null) {
options = Object.assign({}, {
// If true, adds a "jop-noMdConv" class to all the tags.
@@ -172,31 +167,23 @@ class HtmlUtils {
// The BASE tag allows changing the base URL from which files are
// loaded, and that can break several plugins, such as Katex (which
// needs to load CSS files using a relative URL). For that reason it is
// disabled. More info: https://github.com/laurent22/joplin/issues/3021
// needs to load CSS files using a relative URL). For that reason
// it is disabled. More info:
// https://github.com/laurent22/joplin/issues/3021
//
// "link" can be used to escape the parser and inject JavaScript. Adding
// "meta" too for the same reason as it shouldn't be used in notes
// anyway.
//
// There are too many issues with SVG tags and to handle them properly
// we should parse them separately. Currently we are not so it is better
// to disable them. SVG graphics are still supported via the IMG tag.
// "link" can be used to escape the parser and inject JavaScript.
// Adding "meta" too for the same reason as it shouldn't be used in
// notes anyway.
const disallowedTags = [
'script', 'iframe', 'frameset', 'frame', 'object', 'base',
'embed', 'link', 'meta', 'noscript', 'button', 'form',
'input', 'select', 'textarea', 'option', 'optgroup',
'svg',
];
const parser = new htmlparser2.Parser({
onopentag: (name: string, attrs: Record<string, string>) => {
// Note: "name" and attribute names are always lowercase even
// when the input is not. So there is no need to call
// "toLowerCase" on them.
tagStack.push(name);
onopentag: (name: string, attrs: any) => {
tagStack.push(name.toLowerCase());
if (disallowedTags.includes(currentTag())) {
disallowedTagDepth++;
@@ -215,25 +202,11 @@ class HtmlUtils {
// regular harmless attribute that starts with "on" will also
// be removed.
// 0: https://developer.mozilla.org/en-US/docs/Web/Events
for (const attrName in attrs) {
if (!attrs.hasOwnProperty(attrName)) continue;
if (attrName.length <= 2) continue;
if (attrName.substr(0, 2) !== 'on') continue;
delete attrs[attrName];
}
// Make sure that only non-acceptable URLs are filtered out. In
// particular we want to exclude `javascript:` URLs. This
// applies to A tags, and also AREA ones but to be safe we don't
// filter on the tag name and process all HREF attributes.
if ('href' in attrs && !this.isAcceptedUrl(attrs['href'])) {
attrs['href'] = '#';
}
// We need to clear any such attribute, otherwise it will
// make any arbitrary link open within the application.
if ('data-from-md' in attrs) {
delete attrs['data-from-md'];
for (const name in attrs) {
if (!attrs.hasOwnProperty(name)) continue;
if (name.length <= 2) continue;
if (name.toLowerCase().substr(0, 2) !== 'on') continue;
delete attrs[name];
}
if (options.addNoMdConvClass) {
@@ -249,7 +222,7 @@ class HtmlUtils {
// attribute. It doesn't always happen and it seems to depend on
// what else is in the note but in any case adding the "href"
// fixes it. https://github.com/laurent22/joplin/issues/5687
if (name === 'a' && !attrs['href']) {
if (name.toLowerCase() === 'a' && !attrs['href']) {
attrs['href'] = '#';
}
@@ -262,18 +235,11 @@ class HtmlUtils {
ontext: (decodedText: string) => {
if (disallowedTagDepth) return;
if (currentTag() === 'style') {
// For CSS, we have to put the style as-is inside the tag
// because if we html-entities encode it, it's not going to
// work. But it's ok because JavaScript won't run within the
// style tag. Ideally CSS should be loaded from an external
// file.
// We however have to encode at least the `<` characters to
// prevent certain XSS injections that would rely on the
// content not being encoded (see sanitize_13.md)
output.push(decodedText.replace(/</g, '&lt;'));
// For CSS, we have to put the style as-is inside the tag because if we html-entities encode
// it, it's not going to work. But it's ok because JavaScript won't run within the style tag.
// Ideally CSS should be loaded from an external file.
output.push(decodedText);
} else {
output.push(htmlentities(decodedText));
}
+3 -14
View File
@@ -3,12 +3,12 @@ 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, ErrorCode, ErrorConflict, ErrorForbidden, ErrorPayloadTooLarge, ErrorUnprocessableEntity } from '../utils/errors';
import { ApiError, ErrorCode, ErrorForbidden, ErrorPayloadTooLarge, ErrorUnprocessableEntity } from '../utils/errors';
import { Knex } from 'knex';
import { ChangePreviousItem } from './ChangeModel';
import { unique } from '../utils/array';
import StorageDriverBase, { Context } from './items/storage/StorageDriverBase';
import { DbConnection, isUniqueConstraintError, returningSupported } from '../db';
import { DbConnection, returningSupported } from '../db';
import { Config, StorageDriverConfig, StorageDriverMode } from '../utils/types';
import { NewModelFactoryHandler } from './factory';
import loadStorageDriver from './items/storage/loadStorageDriver';
@@ -21,8 +21,6 @@ const mimeUtils = require('@joplin/lib/mime-utils.js').mime;
// Converts "root:/myfile.txt:" to "myfile.txt"
const extractNameRegex = /^root:\/(.*):$/;
const modelLogger = Logger.create('ItemModel');
export interface DeleteOptions extends BaseDeleteOptions {
deleteChanges?: boolean;
}
@@ -930,16 +928,7 @@ export default class ItemModel extends BaseModel<Item> {
}
return this.withTransaction(async () => {
try {
item = await super.save(item, options);
} catch (error) {
if (isUniqueConstraintError(error)) {
modelLogger.error(`Unique constraint error on item: ${JSON.stringify({ id: item.id, name: item.name, jop_id: item.jop_id, owner_id: item.owner_id })}`, error);
throw new ErrorConflict(`This item is already present and cannot be added again: ${item.jop_id}`);
} else {
throw error;
}
}
item = await super.save(item, options);
if (isNew) await this.models().userItem().add(userId, item.id);
-9
View File
@@ -3,7 +3,6 @@
// (Desktop|Mobile|Android|iOS[CLI): (New|Improved|Fixed): Some message..... (#ISSUE)
import { execCommand, githubUsername } from './tool-utils';
import * as compareVersions from 'compare-versions';
interface LogEntry {
message: string;
@@ -92,11 +91,6 @@ function platformFromTag(tagName: string): Platform {
throw new Error(`Could not determine platform from tag: "${tagName}"`);
}
const versionFromTag = (tag: string) => {
const s = tag.split('-');
return s[s.length - 1];
};
function filterLogs(logs: LogEntry[], platform: Platform) {
const output: LogEntry[] = [];
const revertedLogs = [];
@@ -317,13 +311,10 @@ function capitalizeFirstLetter(string: string) {
async function findFirstRelevantTag(baseTag: string, platform: Platform, allTags: string[]) {
let baseTagIndex = allTags.indexOf(baseTag);
if (baseTagIndex < 0) baseTagIndex = allTags.length;
const baseVersion = versionFromTag(baseTag);
for (let i = baseTagIndex - 1; i >= 0; i--) {
const tag = allTags[i];
if (platformFromTag(tag) !== platform) continue;
const currentVersion = versionFromTag(tag);
if (compareVersions(baseVersion, currentVersion) <= 0) continue;
try {
const logs = await gitLog(tag);
-1
View File
@@ -24,7 +24,6 @@
"@joplin/renderer": "^2.10.2",
"@types/node-fetch": "2.6.2",
"@types/yargs": "17.0.20",
"compare-versions": "3.6.0",
"dayjs": "1.11.7",
"execa": "4.1.0",
"fs-extra": "11.1.0",
-4
View File
@@ -1,9 +1,5 @@
# Joplin Android app changelog
## [android-v2.10.9](https://github.com/laurent22/joplin/releases/tag/android-v2.10.9) (Pre-release) - 2023-03-22T18:40:57Z
- Improved: Mark biometrics feature as beta and ensure no call is made if it is not enabled (e44a934)
## [android-v2.10.8](https://github.com/laurent22/joplin/releases/tag/android-v2.10.8) (Pre-release) - 2023-02-28T18:09:21Z
- Improved: Stop synchronization with unsupported WebDAV providers (#7819) (#7661 by [@julien](https://github.com/julien))
+146 -228
View File
@@ -3825,16 +3825,18 @@ __metadata:
languageName: node
linkType: hard
"@electron/universal@npm:1.0.5":
version: 1.0.5
resolution: "@electron/universal@npm:1.0.5"
"@electron/universal@npm:1.2.1":
version: 1.2.1
resolution: "@electron/universal@npm:1.2.1"
dependencies:
"@malept/cross-spawn-promise": ^1.1.0
asar: ^3.0.3
asar: ^3.1.0
debug: ^4.3.1
dir-compare: ^2.4.0
fs-extra: ^9.0.1
checksum: 64eae3bbbfa422f28dbc1e92d12d954059cec7dac9ecc3ecad2c7895bb6cd10d30e8b3848092bfba8815bc71b60393a42f792751e50b9b5f643d6f1d03826b86
minimatch: ^3.0.4
plist: ^3.0.4
checksum: 9a7d98cf2b8414ff0274384fef1b72b5a545a0feb7ce03163d2e2ee1b13e4f7064dfe7147cdd652708a1314d1b5e68acdd907847a1747866ec8d2d3e757ec1f7
languageName: node
linkType: hard
@@ -4642,6 +4644,7 @@ __metadata:
"@fortawesome/fontawesome-free": 5.15.4
"@joeattardi/emoji-button": 4.6.4
"@joplin/lib": ~2.10
"@joplin/pdf-viewer": ~2.10
"@joplin/renderer": ~2.10
"@joplin/tools": ~2.10
"@testing-library/react-hooks": 8.0.1
@@ -4657,7 +4660,7 @@ __metadata:
countable: 3.0.1
debounce: 1.2.1
electron: 19.1.4
electron-builder: 22.11.7
electron-builder: 23.6.0
electron-notarize: 1.2.2
electron-rebuild: 3.2.9
electron-window-state: 5.0.3
@@ -4945,7 +4948,7 @@ __metadata:
languageName: unknown
linkType: soft
"@joplin/pdf-viewer@workspace:packages/pdf-viewer":
"@joplin/pdf-viewer@workspace:packages/pdf-viewer, @joplin/pdf-viewer@~2.10":
version: 0.0.0-use.local
resolution: "@joplin/pdf-viewer@workspace:packages/pdf-viewer"
dependencies:
@@ -5142,7 +5145,6 @@ __metadata:
"@types/node": 18.11.18
"@types/node-fetch": 2.6.2
"@types/yargs": 17.0.20
compare-versions: 3.6.0
dayjs: 1.11.7
execa: 4.1.0
fs-extra: 11.1.0
@@ -7242,7 +7244,7 @@ __metadata:
languageName: node
linkType: hard
"@types/debug@npm:^4.1.5":
"@types/debug@npm:^4.1.6":
version: 4.1.7
resolution: "@types/debug@npm:4.1.7"
dependencies:
@@ -7983,12 +7985,12 @@ __metadata:
languageName: node
linkType: hard
"@types/yargs@npm:^16.0.2":
version: 16.0.5
resolution: "@types/yargs@npm:16.0.5"
"@types/yargs@npm:^17.0.1":
version: 17.0.7
resolution: "@types/yargs@npm:17.0.7"
dependencies:
"@types/yargs-parser": "*"
checksum: 22697f7cc8aa32dcc10981a87f035e183303a58351c537c81fb450270d5c494b1d918186210e445b0eb2e4a8b34a8bda2a595f346bdb1c9ed2b63d193cb00430
checksum: 520e803720b2a626338c0e2b61825dd1b956910fec4d855018c17e62cf9fb41eb67bcf122ba5c6fb569b1bfac73520d5d73e8be502b87f9a551619df441b1875
languageName: node
linkType: hard
@@ -8707,7 +8709,7 @@ __metadata:
languageName: node
linkType: hard
"ansi-align@npm:^3.0.0, ansi-align@npm:^3.0.1":
"ansi-align@npm:^3.0.1":
version: 3.0.1
resolution: "ansi-align@npm:3.0.1"
dependencies:
@@ -8908,41 +8910,44 @@ __metadata:
languageName: node
linkType: hard
"app-builder-bin@npm:3.5.13":
version: 3.5.13
resolution: "app-builder-bin@npm:3.5.13"
checksum: 2947c693642c877e518ced1b13d975e23184dbd6ea8e258d9406034de0416f820b65bc465c47d9367063382e154a298ee614b19189eac46e27bbfe8c3064e9f4
"app-builder-bin@npm:4.0.0":
version: 4.0.0
resolution: "app-builder-bin@npm:4.0.0"
checksum: c3c8fd85c371b7a396c1bb1160ab2e3231ba4309abea5b36a5b366e42511e347c65a33ff50d56f4960b337833d539c263137b0ba131e2fa268c32edeb6c9f683
languageName: node
linkType: hard
"app-builder-lib@npm:22.11.7":
version: 22.11.7
resolution: "app-builder-lib@npm:22.11.7"
"app-builder-lib@npm:23.6.0":
version: 23.6.0
resolution: "app-builder-lib@npm:23.6.0"
dependencies:
7zip-bin: ~5.1.1
"@develar/schema-utils": ~2.6.5
"@electron/universal": 1.0.5
"@electron/universal": 1.2.1
"@malept/flatpak-bundler": ^0.4.0
async-exit-hook: ^2.0.1
bluebird-lst: ^1.0.9
builder-util: 22.11.7
builder-util-runtime: 8.7.7
builder-util: 23.6.0
builder-util-runtime: 9.1.1
chromium-pickle-js: ^0.2.0
debug: ^4.3.2
ejs: ^3.1.6
electron-publish: 22.11.7
fs-extra: ^10.0.0
hosted-git-info: ^4.0.2
debug: ^4.3.4
ejs: ^3.1.7
electron-osx-sign: ^0.6.0
electron-publish: 23.6.0
form-data: ^4.0.0
fs-extra: ^10.1.0
hosted-git-info: ^4.1.0
is-ci: ^3.0.0
isbinaryfile: ^4.0.8
isbinaryfile: ^4.0.10
js-yaml: ^4.1.0
lazy-val: ^1.0.5
minimatch: ^3.0.4
minimatch: ^3.1.2
read-config-file: 6.2.0
sanitize-filename: ^1.6.3
semver: ^7.3.5
semver: ^7.3.7
tar: ^6.1.11
temp-file: ^3.4.0
checksum: e7691fd09d0663693fa650b53c0b92c8027202d07da85b65dcb24a9cbe85fbeb6c8f6b5a4b93282638675a64e1a650bec0962ef945561788937ccad8723b1607
checksum: da3cc9f24e127add651197076c5fa2f68bc7979bcd6a441df7f69629e96bf3aca3118d61c63a85d382a824748f8056a7639464f07b1ded09db53ff1c4b3101be
languageName: node
linkType: hard
@@ -9312,9 +9317,9 @@ __metadata:
languageName: node
linkType: hard
"asar@npm:^3.0.3":
version: 3.2.0
resolution: "asar@npm:3.2.0"
"asar@npm:^3.1.0":
version: 3.1.0
resolution: "asar@npm:3.1.0"
dependencies:
"@types/glob": ^7.1.1
chromium-pickle-js: ^0.2.0
@@ -9326,7 +9331,7 @@ __metadata:
optional: true
bin:
asar: bin/asar.js
checksum: f7d30b45970b053252ac124230bf319459d0728d7f6dedbe2f765cd2a83792d5a716d2c3f2861ceda69372b401f335e1f46460335169eadd0e91a0904a4f5a15
checksum: facc80845639fa4f9e1d1aa40b96adbd1e8b6fee0725d287e8c8e30a69b235cd5b7131b7b09ff700da06c919dd0595b373e372c55722808f983fdb71ef0d5399
languageName: node
linkType: hard
@@ -9975,7 +9980,7 @@ __metadata:
languageName: node
linkType: hard
"bluebird@npm:^3.5.1, bluebird@npm:^3.5.3, bluebird@npm:^3.5.5":
"bluebird@npm:^3.5.0, bluebird@npm:^3.5.1, bluebird@npm:^3.5.3, bluebird@npm:^3.5.5":
version: 3.7.2
resolution: "bluebird@npm:3.7.2"
checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef
@@ -10010,22 +10015,6 @@ __metadata:
languageName: node
linkType: hard
"boxen@npm:^5.0.0":
version: 5.1.2
resolution: "boxen@npm:5.1.2"
dependencies:
ansi-align: ^3.0.0
camelcase: ^6.2.0
chalk: ^4.1.0
cli-boxes: ^2.2.1
string-width: ^4.2.2
type-fest: ^0.20.2
widest-line: ^3.1.0
wrap-ansi: ^7.0.0
checksum: 82d03e42a72576ff235123f17b7c505372fe05c83f75f61e7d4fa4bcb393897ec95ce766fecb8f26b915f0f7a7227d66e5ec7cef43f5b2bd9d3aeed47ec55877
languageName: node
linkType: hard
"boxen@npm:^7.0.0":
version: 7.0.0
resolution: "boxen@npm:7.0.0"
@@ -10340,6 +10329,23 @@ __metadata:
languageName: node
linkType: hard
"buffer-alloc-unsafe@npm:^1.1.0":
version: 1.1.0
resolution: "buffer-alloc-unsafe@npm:1.1.0"
checksum: c5e18bf51f67754ec843c9af3d4c005051aac5008a3992938dda1344e5cfec77c4b02b4ca303644d1e9a6e281765155ce6356d85c6f5ccc5cd21afc868def396
languageName: node
linkType: hard
"buffer-alloc@npm:^1.2.0":
version: 1.2.0
resolution: "buffer-alloc@npm:1.2.0"
dependencies:
buffer-alloc-unsafe: ^1.1.0
buffer-fill: ^1.0.0
checksum: 560cd27f3cbe73c614867da373407d4506309c62fe18de45a1ce191f3785ec6ca2488d802ff82065798542422980ca25f903db078c57822218182c37c3576df5
languageName: node
linkType: hard
"buffer-crc32@npm:~0.2.3":
version: 0.2.13
resolution: "buffer-crc32@npm:0.2.13"
@@ -10354,6 +10360,13 @@ __metadata:
languageName: node
linkType: hard
"buffer-fill@npm:^1.0.0":
version: 1.0.0
resolution: "buffer-fill@npm:1.0.0"
checksum: c29b4723ddeab01e74b5d3b982a0c6828f2ded49cef049ddca3dac661c874ecdbcecb5dd8380cf0f4adbeb8cff90a7de724126750a1f1e5ebd4eb6c59a1315b1
languageName: node
linkType: hard
"buffer-from@npm:^1.0.0":
version: 1.1.2
resolution: "buffer-from@npm:1.1.2"
@@ -10406,45 +10419,38 @@ __metadata:
languageName: node
linkType: hard
"builder-util-runtime@npm:8.7.6":
version: 8.7.6
resolution: "builder-util-runtime@npm:8.7.6"
"builder-util-runtime@npm:9.1.1":
version: 9.1.1
resolution: "builder-util-runtime@npm:9.1.1"
dependencies:
debug: ^4.3.2
debug: ^4.3.4
sax: ^1.2.4
checksum: 0d02e6a5069f3d1b105d34bab6ca4aaafd70fbc182986ccf6803afa1705b6139aa99666c08087def21a722b209a81fda9d7ce19671ba041a0953bfb56fe7f4a9
checksum: 3458f9c8accad6e934c841cffa93f5d4b342c22b10b9c1a2eb3fd44ca96ea2c662b1048f9a075da9b8a4fada17206887b7e92ebdca331b1071520916e013e245
languageName: node
linkType: hard
"builder-util-runtime@npm:8.7.7":
version: 8.7.7
resolution: "builder-util-runtime@npm:8.7.7"
dependencies:
debug: ^4.3.2
sax: ^1.2.4
checksum: cf09c5538d4758a5408a5989ca4c0441fea6416b98f9047508dbe2e8fdfe0fa4b11c7a9952c5bacda0f6711573b05c12ee29bfe93fc456cc3e41b7b2996b54aa
languageName: node
linkType: hard
"builder-util@npm:22.11.7":
version: 22.11.7
resolution: "builder-util@npm:22.11.7"
"builder-util@npm:23.6.0":
version: 23.6.0
resolution: "builder-util@npm:23.6.0"
dependencies:
7zip-bin: ~5.1.1
"@types/debug": ^4.1.5
"@types/debug": ^4.1.6
"@types/fs-extra": ^9.0.11
app-builder-bin: 3.5.13
app-builder-bin: 4.0.0
bluebird-lst: ^1.0.9
builder-util-runtime: 8.7.7
builder-util-runtime: 9.1.1
chalk: ^4.1.1
debug: ^4.3.2
cross-spawn: ^7.0.3
debug: ^4.3.4
fs-extra: ^10.0.0
http-proxy-agent: ^5.0.0
https-proxy-agent: ^5.0.0
is-ci: ^3.0.0
js-yaml: ^4.1.0
source-map-support: ^0.5.19
stat-mode: ^1.0.0
temp-file: ^3.4.0
checksum: 53791d1feefc1c839ddb711ef84f74c3efdb37b0fc523c9c3ea7261bd755a4126c2f15d826ca8486aed9a3a0a5a942208b86c4ec15be94ee7552e1459ec9e588
checksum: 138fb9abed01ea2e5ac895e6a6ed75310ca6c89e0050483c81801b052f61b42ae5a042f457088b6e205ec8b4403b1ff3a325955f110255afb4da2310e3cf14ad
languageName: node
linkType: hard
@@ -11196,7 +11202,7 @@ __metadata:
languageName: node
linkType: hard
"cli-boxes@npm:^2.2.0, cli-boxes@npm:^2.2.1":
"cli-boxes@npm:^2.2.0":
version: 2.2.1
resolution: "cli-boxes@npm:2.2.1"
checksum: be79f8ec23a558b49e01311b39a1ea01243ecee30539c880cf14bf518a12e223ef40c57ead0cb44f509bffdffc5c129c746cd50d863ab879385370112af4f585
@@ -11727,7 +11733,7 @@ __metadata:
languageName: node
linkType: hard
"compare-version@npm:0.1.2":
"compare-version@npm:0.1.2, compare-version@npm:^0.1.2":
version: 0.1.2
resolution: "compare-version@npm:0.1.2"
checksum: 0ceaf50b5f912c8eb8eeca19375e617209d200abebd771e9306510166462e6f91ad764f33f210a3058ee27c83f2f001a7a4ca32f509da2d207d0143a3438a020
@@ -13297,7 +13303,7 @@ __metadata:
languageName: node
linkType: hard
"debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.9":
"debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.8, debug@npm:^2.6.9":
version: 2.6.9
resolution: "debug@npm:2.6.9"
dependencies:
@@ -14049,25 +14055,25 @@ __metadata:
languageName: node
linkType: hard
"dmg-builder@npm:22.11.7":
version: 22.11.7
resolution: "dmg-builder@npm:22.11.7"
"dmg-builder@npm:23.6.0":
version: 23.6.0
resolution: "dmg-builder@npm:23.6.0"
dependencies:
app-builder-lib: 22.11.7
builder-util: 22.11.7
builder-util-runtime: 8.7.6
dmg-license: ^1.0.9
app-builder-lib: 23.6.0
builder-util: 23.6.0
builder-util-runtime: 9.1.1
dmg-license: ^1.0.11
fs-extra: ^10.0.0
iconv-lite: ^0.6.2
js-yaml: ^4.1.0
dependenciesMeta:
dmg-license:
optional: true
checksum: a46b7a0eaf304eef8ed1ebe859743d8415a1206c29ffadbf2781e67c849bfbec22c641985f08eb421ccbe47bc629ea954f4818ff22f55c6023542d50639f5852
checksum: 3e37a4b191cf40c9c7b97d07408c2bf58e7632d78de0dc49a142fb7c68670fd2a7123f31ee8803b3cd100f38feea7b785c28698dfaace508254659d81ecc0b80
languageName: node
linkType: hard
"dmg-license@npm:^1.0.9":
"dmg-license@npm:^1.0.11":
version: 1.0.11
resolution: "dmg-license@npm:1.0.11"
dependencies:
@@ -14383,37 +14389,37 @@ __metadata:
languageName: node
linkType: hard
"ejs@npm:^3.1.6":
version: 3.1.9
resolution: "ejs@npm:3.1.9"
"ejs@npm:^3.1.7":
version: 3.1.7
resolution: "ejs@npm:3.1.7"
dependencies:
jake: ^10.8.5
bin:
ejs: bin/cli.js
checksum: af6f10eb815885ff8a8cfacc42c6b6cf87daf97a4884f87a30e0c3271fedd85d76a3a297d9c33a70e735b97ee632887f85e32854b9cdd3a2d97edf931519a35f
checksum: fe40764af39955ce8f8b116716fc8b911959946698edb49ecab85df597746c07aa65d5b74ead28a1e2ffa75b0f92d9bedd752f1c29437da6137b3518271e988c
languageName: node
linkType: hard
"electron-builder@npm:22.11.7":
version: 22.11.7
resolution: "electron-builder@npm:22.11.7"
"electron-builder@npm:23.6.0":
version: 23.6.0
resolution: "electron-builder@npm:23.6.0"
dependencies:
"@types/yargs": ^16.0.2
app-builder-lib: 22.11.7
builder-util: 22.11.7
builder-util-runtime: 8.7.7
"@types/yargs": ^17.0.1
app-builder-lib: 23.6.0
builder-util: 23.6.0
builder-util-runtime: 9.1.1
chalk: ^4.1.1
dmg-builder: 22.11.7
dmg-builder: 23.6.0
fs-extra: ^10.0.0
is-ci: ^3.0.0
lazy-val: ^1.0.5
read-config-file: 6.2.0
update-notifier: ^5.1.0
yargs: ^17.0.1
simple-update-notifier: ^1.0.7
yargs: ^17.5.1
bin:
electron-builder: cli.js
install-app-deps: install-app-deps.js
checksum: 8f21d2d1d4acb88c51b077d8d7da851d6ac4d32577296c586640618c9569be33826ce55e255d5a849060748fae1c218f38baef4792da247163962065e543a103
checksum: 227f8fb9c9bb11a11d999f2ade6a5cd1afb720d6ff5053c88b4be62d1265b6268c8f6b4b3b8ad6d0a7261d57ea5acd6619ef301b843865f260b616c474cf8cbd
languageName: node
linkType: hard
@@ -14427,18 +14433,35 @@ __metadata:
languageName: node
linkType: hard
"electron-publish@npm:22.11.7":
version: 22.11.7
resolution: "electron-publish@npm:22.11.7"
"electron-osx-sign@npm:^0.6.0":
version: 0.6.0
resolution: "electron-osx-sign@npm:0.6.0"
dependencies:
bluebird: ^3.5.0
compare-version: ^0.1.2
debug: ^2.6.8
isbinaryfile: ^3.0.2
minimist: ^1.2.0
plist: ^3.0.1
bin:
electron-osx-flat: bin/electron-osx-flat.js
electron-osx-sign: bin/electron-osx-sign.js
checksum: b688f9efb013670b4226cff7c38101e7b1384ea44e1ab203259995f1eefc019c63aa18e936217a76d33b5a5a452b987ab3d86a56a961294582ce42acbb950de6
languageName: node
linkType: hard
"electron-publish@npm:23.6.0":
version: 23.6.0
resolution: "electron-publish@npm:23.6.0"
dependencies:
"@types/fs-extra": ^9.0.11
builder-util: 22.11.7
builder-util-runtime: 8.7.7
builder-util: 23.6.0
builder-util-runtime: 9.1.1
chalk: ^4.1.1
fs-extra: ^10.0.0
lazy-val: ^1.0.5
mime: ^2.5.2
checksum: 99eb8f50d92210904212125e59936e45bae1cd3f2b363f728846d6ff23fa1358395945c19f582b977dcc8e3f45c1bbcfb8100e0cf5718429be2c135a8359e6e4
checksum: 70473d800f0607b5ffc32473e87004079fe3e5f133242bb498dcff0be89bfaa4ce967860809e12b97ce216b1e907649a8a916b7483daf7a00ea28db3d665878e
languageName: node
linkType: hard
@@ -14931,13 +14954,6 @@ __metadata:
languageName: node
linkType: hard
"escape-goat@npm:^2.0.0":
version: 2.1.1
resolution: "escape-goat@npm:2.1.1"
checksum: ce05c70c20dd7007b60d2d644b625da5412325fdb57acf671ba06cb2ab3cd6789e2087026921a05b665b0a03fadee2955e7fc0b9a67da15a6551a980b260eba7
languageName: node
linkType: hard
"escape-html@npm:^1.0.3, escape-html@npm:~1.0.3":
version: 1.0.3
resolution: "escape-html@npm:1.0.3"
@@ -17685,13 +17701,6 @@ __metadata:
languageName: node
linkType: hard
"has-yarn@npm:^2.1.0":
version: 2.1.0
resolution: "has-yarn@npm:2.1.0"
checksum: 5eb1d0bb8518103d7da24532bdbc7124ffc6d367b5d3c10840b508116f2f1bcbcf10fd3ba843ff6e2e991bdf9969fd862d42b2ed58aade88343326c950b7e7f7
languageName: node
linkType: hard
"has@npm:^1.0.0, has@npm:^1.0.3, has@npm:~1.0.3":
version: 1.0.3
resolution: "has@npm:1.0.3"
@@ -17849,7 +17858,7 @@ __metadata:
languageName: node
linkType: hard
"hosted-git-info@npm:^4.0.2":
"hosted-git-info@npm:^4.1.0":
version: 4.1.0
resolution: "hosted-git-info@npm:4.1.0"
dependencies:
@@ -18346,13 +18355,6 @@ __metadata:
languageName: node
linkType: hard
"import-lazy@npm:^2.1.0":
version: 2.1.0
resolution: "import-lazy@npm:2.1.0"
checksum: 05294f3b9dd4971d3a996f0d2f176410fb6745d491d6e73376429189f5c1c3d290548116b2960a7cf3e89c20cdf11431739d1d2d8c54b84061980795010e803a
languageName: node
linkType: hard
"import-local@npm:^2.0.0":
version: 2.0.0
resolution: "import-local@npm:2.0.0"
@@ -19124,13 +19126,6 @@ __metadata:
languageName: node
linkType: hard
"is-npm@npm:^5.0.0":
version: 5.0.0
resolution: "is-npm@npm:5.0.0"
checksum: 9baff02b0c69a3d3c79b162cb2f9e67fb40ef6d172c16601b2e2471c21e9a4fa1fc9885a308d7bc6f3a3cd2a324c27fa0bf284c133c3349bb22571ab70d041cc
languageName: node
linkType: hard
"is-number-object@npm:^1.0.4":
version: 1.0.6
resolution: "is-number-object@npm:1.0.6"
@@ -19446,13 +19441,6 @@ __metadata:
languageName: node
linkType: hard
"is-yarn-global@npm:^0.3.0":
version: 0.3.0
resolution: "is-yarn-global@npm:0.3.0"
checksum: bca013d65fee2862024c9fbb3ba13720ffca2fe750095174c1c80922fdda16402b5c233f5ac9e265bc12ecb5446e7b7f519a32d9541788f01d4d44e24d2bf481
languageName: node
linkType: hard
"is2@npm:^2.0.6":
version: 2.0.7
resolution: "is2@npm:2.0.7"
@@ -19471,7 +19459,16 @@ __metadata:
languageName: node
linkType: hard
"isbinaryfile@npm:^4.0.8":
"isbinaryfile@npm:^3.0.2":
version: 3.0.3
resolution: "isbinaryfile@npm:3.0.3"
dependencies:
buffer-alloc: ^1.2.0
checksum: 9a555786857c66fe36024d15a54e0ca371c02275622b007356d6afca2b3bca179cb0bd97e1adf5d3922b3325c0fe22813645c7f7eafb4c4bdab1da9d635133c2
languageName: node
linkType: hard
"isbinaryfile@npm:^4.0.10":
version: 4.0.10
resolution: "isbinaryfile@npm:4.0.10"
checksum: a6b28db7e23ac7a77d3707567cac81356ea18bd602a4f21f424f862a31d0e7ab4f250759c98a559ece35ffe4d99f0d339f1ab884ffa9795172f632ab8f88e686
@@ -21046,15 +21043,6 @@ __metadata:
languageName: node
linkType: hard
"latest-version@npm:^5.1.0":
version: 5.1.0
resolution: "latest-version@npm:5.1.0"
dependencies:
package-json: ^6.3.0
checksum: fbc72b071eb66c40f652441fd783a9cca62f08bf42433651937f078cd9ef94bf728ec7743992777826e4e89305aef24f234b515e6030503a2cbee7fc9bdc2c0f
languageName: node
linkType: hard
"lazy-val@npm:^1.0.4, lazy-val@npm:^1.0.5":
version: 1.0.5
resolution: "lazy-val@npm:1.0.5"
@@ -24749,18 +24737,6 @@ __metadata:
languageName: node
linkType: hard
"package-json@npm:^6.3.0":
version: 6.5.0
resolution: "package-json@npm:6.5.0"
dependencies:
got: ^9.6.0
registry-auth-token: ^4.0.0
registry-url: ^5.0.0
semver: ^6.2.0
checksum: cc9f890d3667d7610e6184decf543278b87f657d1ace0deb4a9c9155feca738ef88f660c82200763d3348010f4e42e9c7adc91e96ab0f86a770955995b5351e2
languageName: node
linkType: hard
"packet-reader@npm:1.0.0":
version: 1.0.0
resolution: "packet-reader@npm:1.0.0"
@@ -25404,7 +25380,7 @@ __metadata:
languageName: node
linkType: hard
"plist@npm:^3.0.4":
"plist@npm:^3.0.1, plist@npm:^3.0.4":
version: 3.0.4
resolution: "plist@npm:3.0.4"
dependencies:
@@ -26068,15 +26044,6 @@ __metadata:
languageName: node
linkType: hard
"pupa@npm:^2.1.1":
version: 2.1.1
resolution: "pupa@npm:2.1.1"
dependencies:
escape-goat: ^2.0.0
checksum: 49529e50372ffdb0cccf0efa0f3b3cb0a2c77805d0d9cc2725bd2a0f6bb414631e61c93a38561b26be1259550b7bb6c2cb92315aa09c8bf93f3bdcb49f2b2fb7
languageName: node
linkType: hard
"q@npm:^1.5.1":
version: 1.5.1
resolution: "q@npm:1.5.1"
@@ -26259,7 +26226,7 @@ __metadata:
languageName: node
linkType: hard
"rc@npm:1.2.8, rc@npm:^1.2.7, rc@npm:^1.2.8":
"rc@npm:^1.2.7":
version: 1.2.8
resolution: "rc@npm:1.2.8"
dependencies:
@@ -27334,24 +27301,6 @@ __metadata:
languageName: node
linkType: hard
"registry-auth-token@npm:^4.0.0":
version: 4.2.2
resolution: "registry-auth-token@npm:4.2.2"
dependencies:
rc: 1.2.8
checksum: c5030198546ecfdcbcb0722cbc3e260c4f5f174d8d07bdfedd4620e79bfdf17a2db735aa230d600bd388fce6edd26c0a9ed2eb7e9b4641ec15213a28a806688b
languageName: node
linkType: hard
"registry-url@npm:^5.0.0":
version: 5.1.0
resolution: "registry-url@npm:5.1.0"
dependencies:
rc: ^1.2.8
checksum: bcea86c84a0dbb66467b53187fadebfea79017cddfb4a45cf27530d7275e49082fe9f44301976eb0164c438e395684bcf3dae4819b36ff9d1640d8cc60c73df9
languageName: node
linkType: hard
"regjsgen@npm:^0.7.1":
version: 0.7.1
resolution: "regjsgen@npm:0.7.1"
@@ -28254,15 +28203,6 @@ __metadata:
languageName: node
linkType: hard
"semver-diff@npm:^3.1.1":
version: 3.1.1
resolution: "semver-diff@npm:3.1.1"
dependencies:
semver: ^6.3.0
checksum: 8bbe5a5d7add2d5e51b72314a9215cd294d71f41cdc2bf6bd59ee76411f3610b576172896f1d191d0d7294cb9f2f847438d2ee158adacc0c224dca79052812fe
languageName: node
linkType: hard
"semver-greatest-satisfied-range@npm:^1.1.0":
version: 1.1.0
resolution: "semver-greatest-satisfied-range@npm:1.1.0"
@@ -31803,28 +31743,6 @@ __metadata:
languageName: node
linkType: hard
"update-notifier@npm:^5.1.0":
version: 5.1.0
resolution: "update-notifier@npm:5.1.0"
dependencies:
boxen: ^5.0.0
chalk: ^4.1.0
configstore: ^5.0.1
has-yarn: ^2.1.0
import-lazy: ^2.1.0
is-ci: ^2.0.0
is-installed-globally: ^0.4.0
is-npm: ^5.0.0
is-yarn-global: ^0.3.0
latest-version: ^5.1.0
pupa: ^2.1.1
semver: ^7.3.4
semver-diff: ^3.1.1
xdg-basedir: ^4.0.0
checksum: 461e5e5b002419296d3868ee2abe0f9ab3e1846d9db642936d0c46f838872ec56069eddfe662c45ce1af0a8d6d5026353728de2e0a95ab2e3546a22ea077caf1
languageName: node
linkType: hard
"upper-case-first@npm:^2.0.2":
version: 2.0.2
resolution: "upper-case-first@npm:2.0.2"