diff --git a/.npmignore b/.npmignore index 80f70d7..9d820f3 100644 --- a/.npmignore +++ b/.npmignore @@ -6,3 +6,4 @@ /dist tsconfig.json webpack.config.js +.env diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f9f105..74a885c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## not released +## v1.1.0 (2022-07-11) + +- Improved: The default setting for `Single JEX` is now `true` (Create only one JEX backup file) to prevent the loss of internal notelinks during a restore. Joplin Discourse: [Lost all connections of my notes](https://discourse.joplinapp.org/t/lost-all-connections-of-my-notes/25464) +- Improved: Create a sub folder `JoplinBackup` in the configured `Backup path` (Only for new installations). +- Improved: Use new Joplin DirectoryPath selector for path selection. Not supported in Joplin >= v2.9.1, non-compatible Joplin versions still use a text input field. +- Add: Backup all installed Joplin plugins (Only the jpl files, no plugin settings!) + ## v1.0.5 (2021-12-05) - Fix: #28 No message was displayed that the backup is finished when starting manually diff --git a/README.md b/README.md index dc97f7c..d764923 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ Go to `Tools > Options > Backup` | Option | Description | Default | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | | `Backup path` | Where to save the backups to.
This path is exclusive for the Joplin backups, there should be no other data in it! | | -| `Single JEX` | Create only one JEX file for all notebooks | `false` | | `Keep x backups` | How many backups should be kept | `1` | | `Backups interval in hours` | Create a backup every X hours | `24` | | `Only on change` | Creates a backup at the specified backup interval only if there was a change to a `note`, `tag`, `resource` or `notebook` | `false` | @@ -44,6 +43,9 @@ Go to `Tools > Options > Backup` | `Zip compression Level` | Compression level for zip archive archive | `Copy (no compression)` | | `Temporary export path` | The data is first exported into this path before it is copied to the backup `Backup path`. | `` | | `Backup set name` | Name of the backup set if multiple backups are to be keep. [Available moment tokens](https://momentjs.com/docs/#/displaying/format/), which can be used with `{}` | `{YYYYMMDDHHmm}` | +| `Single JEX` | Create only one JEX file for all, this option is recommended to prevent the loss of internal note links or folder structure during a restore! | `true` | +| `Create Subfolder` | Create a sub folder `JoplinBackup` in the configured `Backup path`. Deactivate only if there is no other data in the `Backup path`! | `true` | +| `Backup plugins` | Backup the plugin folder from the Joplin profile with all installed plugin jpl files. | `true` | ## Keyboard Shortcuts @@ -59,6 +61,7 @@ Under `Options > Keyboard Shortcuts` you can assign a keyboard shortcut for the - The `userchrome.css` (Your Joplin customization) - The `userstyle.css` (Your Joplin customization) - The `templates` folder (Note templates) +- The `plugin` folder (All installed plugins, no plugin settings!) ## Restore @@ -78,6 +81,27 @@ The notes are imported via `File > Import > JEX - Joplin Export File`. The notes are imported additionally, no check for duplicates is performed. If the folder in which the note was located already exists in you Joplin, than the folder name is extended by one (1). +## FAQ + +### Internal Joplin links betwen notes are lost + +If several JEX files are imported and the notes have links to each other, these links will be lost. +Therefore it is recommended to create a Single JEX Backup! + +### Compine multiple JEX Files to one + +By combining the JEX files into one, the Joplin internal links will work again after the import. + +1. Open one of the JEX files in a ZIP program like 7-Zip +2. Open a second JEX and add all files to the first JEX +3. Repeat step 2 for all files +4. Import first JEX which now contains all notes + +## Open a JEX Backup file + +A Joplin JEX Backup file is a tar archive which can be opened with any zip program that supports TAR archive. +The file names in the archive correspond to the Joplin internal IDs. + ## Changelog See [CHANGELOG.md](CHANGELOG.md) diff --git a/__test__/backup.test.ts b/__test__/backup.test.ts index 0bc0940..99ef9b0 100644 --- a/__test__/backup.test.ts +++ b/__test__/backup.test.ts @@ -24,6 +24,7 @@ let spyOnLogVerbose = null; let spyOnLogInfo = null; let spyOnLogWarn = null; let spyOnLogError = null; +let spyOnShowError = null; let spyOnSaveBackupInfo = null; const spyOnsSettingsValue = jest.spyOn(joplin.settings, "value"); @@ -73,6 +74,10 @@ describe("Backup", function () { spyOnLogError = jest .spyOn(backup.log, "error") .mockImplementation(() => {}); + + spyOnShowError = jest + .spyOn(backup, "showError") + .mockImplementation(() => {}); }); afterEach(async () => { @@ -80,6 +85,7 @@ describe("Backup", function () { spyOnLogInfo.mockReset(); spyOnLogWarn.mockReset(); spyOnLogError.mockReset(); + spyOnShowError.mockReset(); spyOnsSettingsValue.mockReset(); spyOnGlobalValue.mockReset(); spyOnSaveBackupInfo.mockReset(); @@ -88,29 +94,72 @@ describe("Backup", function () { afterAll(async () => { fs.removeSync(testPath.base); }); - describe("Backup path", function () { - it(`Backup path != Profile`, async () => { - await backup.loadBackupPath(); - expect(backup.backupBasePath).toBe(testPath.backupBasePath); - expect(backup.backupBasePath).not.toBe(testPath.joplinProfile); - - /* prettier-ignore */ - when(spyOnsSettingsValue) - .calledWith("path").mockImplementation(() => Promise.resolve("")); - await backup.loadBackupPath(); - expect(backup.backupBasePath).not.toBe(testPath.joplinProfile); - expect(backup.backupBasePath).toBe(null); + it(`Path tests`, async () => { + const testCases = [ + { + backupPath: testPath.joplinProfile, + createSubfolder: false, + expectedBackupPath: null, + expectedBackupPathExist: null, + }, + { + backupPath: testPath.joplinProfile, + createSubfolder: true, + expectedBackupPath: path.join(testPath.joplinProfile, "JoplinBackup"), + expectedBackupPathExist: true, + }, + { + backupPath: testPath.backupBasePath, + createSubfolder: false, + expectedBackupPath: testPath.backupBasePath, + expectedBackupPathExist: true, + }, + { + backupPath: testPath.backupBasePath, + createSubfolder: true, + expectedBackupPath: path.join( + testPath.backupBasePath, + "JoplinBackup" + ), + expectedBackupPathExist: true, + }, + { + backupPath: path.join(testPath.backupBasePath, "NotExisting"), + createSubfolder: false, + expectedBackupPath: path.join(testPath.backupBasePath, "NotExisting"), + expectedBackupPathExist: false, + }, + { + backupPath: path.join(testPath.backupBasePath, "NotExisting"), + createSubfolder: true, + expectedBackupPath: path.join( + testPath.backupBasePath, + "NotExisting", + "JoplinBackup" + ), + expectedBackupPathExist: false, + }, + ]; - /* prettier-ignore */ - when(spyOnsSettingsValue) - .calledWith("path").mockImplementation(() => Promise.resolve(testPath.joplinProfile)); - await backup.loadBackupPath(); - expect(backup.backupBasePath).not.toBe(testPath.joplinProfile); - expect(backup.backupBasePath).toBe(null); + for (const testCase of testCases) { + when(spyOnsSettingsValue) + .calledWith("path") + .mockImplementation(() => Promise.resolve(testCase.backupPath)); + backup.createSubfolder = testCase.createSubfolder; + await backup.loadBackupPath(); + expect(backup.backupBasePath).toBe(testCase.expectedBackupPath); + expect(backup.backupBasePath).not.toBe(testPath.joplinProfile); + + if (testCase.expectedBackupPathExist !== null) { + expect(fs.existsSync(backup.backupBasePath)).toBe( + testCase.expectedBackupPathExist + ); + } - expect(backup.log.error).toHaveBeenCalledTimes(0); - expect(backup.log.warn).toHaveBeenCalledTimes(0); + expect(backup.log.error).toHaveBeenCalledTimes(0); + expect(backup.log.warn).toHaveBeenCalledTimes(0); + } }); it(`relative paths`, async () => { diff --git a/__test__/sevenZip.test.ts b/__test__/sevenZip.test.ts index 05ce33c..15da10b 100644 --- a/__test__/sevenZip.test.ts +++ b/__test__/sevenZip.test.ts @@ -32,7 +32,9 @@ describe("Test sevenZip", function () { expect(fs.existsSync(file3)).toBe(true); expect(fs.existsSync(zip)).toBe(false); - expect(await sevenZip.add(zip, testBaseDir + "\\*", "secret")).toBe(true); + expect(await sevenZip.add(zip, path.join(testBaseDir, "*"), "secret")).toBe( + true + ); expect(fs.existsSync(zip)).toBe(true); expect(await sevenZip.passwordProtected(zip)).toBe(true); diff --git a/api/Joplin.d.ts b/api/Joplin.d.ts index e30586e..62df492 100644 --- a/api/Joplin.d.ts +++ b/api/Joplin.d.ts @@ -49,6 +49,7 @@ export default class Joplin { get views(): JoplinViews; get interop(): JoplinInterop; get settings(): JoplinSettings; + get versionInfo(): any; /** * It is not possible to bundle native packages with a plugin, because they * need to work cross-platforms. Instead access to certain useful native diff --git a/api/types.ts b/api/types.ts index ed817ba..f8ac2cf 100644 --- a/api/types.ts +++ b/api/types.ts @@ -27,11 +27,12 @@ export interface Command { execute(...args: any[]): Promise; /** - * Defines whether the command should be enabled or disabled, which in turns affects - * the enabled state of any associated button or menu item. + * Defines whether the command should be enabled or disabled, which in turns + * affects the enabled state of any associated button or menu item. * - * The condition should be expressed as a "when-clause" (as in Visual Studio Code). It's a simple boolean expression that evaluates to - * `true` or `false`. It supports the following operators: + * The condition should be expressed as a "when-clause" (as in Visual Studio + * Code). It's a simple boolean expression that evaluates to `true` or + * `false`. It supports the following operators: * * Operator | Symbol | Example * -- | -- | -- @@ -40,7 +41,15 @@ export interface Command { * Or | \|\| | "noteIsTodo \|\| noteTodoCompleted" * And | && | "oneNoteSelected && !inConflictFolder" * - * Currently the supported context variables aren't documented, but you can [find the list here](https://github.com/laurent22/joplin/blob/dev/packages/lib/services/commands/stateToWhenClauseContext.ts). + * Joplin, unlike VSCode, also supports parenthesis, which allows creating + * more complex expressions such as `cond1 || (cond2 && cond3)`. Only one + * level of parenthesis is possible (nested ones aren't supported). + * + * Currently the supported context variables aren't documented, but you can + * find the list below: + * + * - [Global When Clauses](https://github.com/laurent22/joplin/blob/dev/packages/lib/services/commands/stateToWhenClauseContext.ts) + * - [Desktop app When Clauses](https://github.com/laurent22/joplin/blob/dev/packages/app-desktop/services/commands/stateToWhenClauseContext.ts) * * Note: Commands are enabled by default unless you use this property. */ @@ -193,6 +202,31 @@ export interface Disposable { // dispose():void; } +export enum ModelType { + Note = 1, + Folder = 2, + Setting = 3, + Resource = 4, + Tag = 5, + NoteTag = 6, + Search = 7, + Alarm = 8, + MasterKey = 9, + ItemChange = 10, + NoteResource = 11, + ResourceLocalState = 12, + Revision = 13, + Migration = 14, + SmartFilter = 15, + Command = 16, +} + +export interface VersionInfo { + version: string; + profileVersion: number; + syncVersion: number; +} + // ================================================================= // Menu types // ================================================================= @@ -260,6 +294,17 @@ export interface MenuItem { */ commandName?: string; + /** + * Arguments that should be passed to the command. They will be as rest + * parameters. + */ + commandArgs?: any[]; + + /** + * Set to "separator" to create a divider line + */ + type?: ('normal' | 'separator' | 'submenu' | 'checkbox' | 'radio'); + /** * Accelerator associated with the menu item */ @@ -325,12 +370,35 @@ export enum SettingItemType { Button = 6, } +export enum SettingItemSubType { + FilePathAndArgs = 'file_path_and_args', + FilePath = 'file_path', // Not supported on mobile! + DirectoryPath = 'directory_path', // Not supported on mobile! +} + +export enum AppType { + Desktop = 'desktop', + Mobile = 'mobile', + Cli = 'cli', +} + +export enum SettingStorage { + Database = 1, + File = 2, +} + // Redefine a simplified interface to mask internal details // and to remove function calls as they would have to be async. export interface SettingItem { value: any; type: SettingItemType; + /** + * Currently only used to display a file or directory selector. Always set + * `type` to `SettingItemType.String` when using this property. + */ + subType?: SettingItemSubType; + label: string; description?: string; @@ -363,7 +431,7 @@ export interface SettingItem { /** * Reserved property. Not used at the moment. */ - appTypes?: string[]; + appTypes?: AppType[]; /** * Set this to `true` to store secure data, such as passwords. Any such @@ -384,6 +452,11 @@ export interface SettingItem { minimum?: number; maximum?: number; step?: number; + + /** + * Either store the setting in the database or in settings.json. Defaults to database. + */ + storage?: SettingStorage; } export interface SettingSection { @@ -410,7 +483,7 @@ export type Path = string[]; // Content Script types // ================================================================= -export type PostMessageHandler = (id: string, message: any)=> Promise; +export type PostMessageHandler = (message: any)=> Promise; /** * When a content script is initialised, it receives a `context` object. diff --git a/package-lock.json b/package-lock.json index fb8873d..9312f2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "joplin-plugin-backup", - "version": "1.0.5", + "version": "1.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "joplin-plugin-backup", - "version": "1.0.5", + "version": "1.1.0", "license": "MIT", "dependencies": { "7zip-bin": "^5.1.1", @@ -681,9 +681,9 @@ } }, "node_modules/@jest/core/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -1260,9 +1260,9 @@ } }, "node_modules/@types/jest/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -1709,9 +1709,9 @@ } }, "node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true, "engines": { "node": ">=6" @@ -1901,12 +1901,12 @@ } }, "node_modules/axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, "dependencies": { - "follow-redirects": "^1.10.0" + "follow-redirects": "^1.14.0" } }, "node_modules/babel-jest": { @@ -2575,9 +2575,9 @@ } }, "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -3262,18 +3262,18 @@ "dev": true }, "node_modules/elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, "dependencies": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" } }, "node_modules/elliptic/node_modules/bn.js": { @@ -4035,9 +4035,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "dev": true, "funding": [ { @@ -4239,9 +4239,9 @@ } }, "node_modules/glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { "is-glob": "^4.0.1" @@ -6817,9 +6817,9 @@ } }, "node_modules/listr2/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -6984,9 +6984,9 @@ } }, "node_modules/log-update/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -7265,9 +7265,9 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "node_modules/minipass": { @@ -7390,9 +7390,9 @@ } }, "node_modules/moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", "engines": { "node": "*" } @@ -7650,15 +7650,23 @@ } }, "node_modules/node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, "engines": { "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, "node_modules/node-fetch/node_modules/tr46": { @@ -8258,9 +8266,9 @@ } }, "node_modules/pretty-format/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -9097,9 +9105,9 @@ "dev": true }, "node_modules/ssri": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", - "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", "dev": true, "dependencies": { "minipass": "^3.1.1" @@ -9231,9 +9239,9 @@ } }, "node_modules/string-length/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -9375,9 +9383,9 @@ } }, "node_modules/tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "dependencies": { "chownr": "^2.0.0", @@ -9642,9 +9650,9 @@ } }, "node_modules/terser-webpack-plugin/node_modules/ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "dev": true, "dependencies": { "figgy-pudding": "^3.5.1" @@ -9705,9 +9713,9 @@ } }, "node_modules/tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "node_modules/to-arraybuffer": { @@ -10445,7 +10453,7 @@ "node_modules/watchpack-chokidar2/node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", "dev": true, "optional": true, "dependencies": { @@ -11260,9 +11268,9 @@ } }, "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" @@ -11908,9 +11916,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "micromatch": { @@ -12404,9 +12412,9 @@ } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "diff-sequences": { @@ -12798,9 +12806,9 @@ } }, "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, "ansi-styles": { @@ -12952,12 +12960,12 @@ "dev": true }, "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, "requires": { - "follow-redirects": "^1.10.0" + "follow-redirects": "^1.14.0" } }, "babel-jest": { @@ -13497,9 +13505,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "emoji-regex": { @@ -14084,18 +14092,18 @@ "dev": true }, "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" }, "dependencies": { "bn.js": { @@ -14703,9 +14711,9 @@ } }, "follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "dev": true }, "for-in": { @@ -14847,9 +14855,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -16841,9 +16849,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "emoji-regex": { @@ -16971,9 +16979,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "emoji-regex": { @@ -17190,9 +17198,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "minipass": { @@ -17287,9 +17295,9 @@ "dev": true }, "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, "move-concurrently": { "version": "1.0.1", @@ -17497,9 +17505,9 @@ } }, "node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, "requires": { "whatwg-url": "^5.0.0" @@ -17985,9 +17993,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -18665,9 +18673,9 @@ "dev": true }, "ssri": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", - "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", "dev": true, "requires": { "minipass": "^3.1.1" @@ -18784,9 +18792,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "strip-ansi": { @@ -18894,9 +18902,9 @@ "dev": true }, "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -19101,9 +19109,9 @@ } }, "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "dev": true, "requires": { "figgy-pudding": "^3.5.1" @@ -19160,9 +19168,9 @@ } }, "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "to-arraybuffer": { @@ -19772,7 +19780,7 @@ "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", "dev": true, "optional": true, "requires": { @@ -20429,9 +20437,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "cliui": { diff --git a/package.json b/package.json index 4606873..cd3808a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "joplin-plugin-backup", - "version": "1.0.5", + "version": "1.1.0", "scripts": { "dist": "webpack --joplin-plugin-config buildMain && webpack --joplin-plugin-config buildExtraScripts && webpack --joplin-plugin-config createArchive", "prepare": "npm run dist && husky install", diff --git a/src/Backup.ts b/src/Backup.ts index bacbd60..4ab430f 100644 --- a/src/Backup.ts +++ b/src/Backup.ts @@ -20,8 +20,10 @@ class Backup { private password: string; private backupStartTime: Date; private zipArchive: string; + private backupPlugins: boolean; private compressionLevel: number; private singleJex: boolean; + private createSubfolder: boolean; private backupSetName: string; constructor() { @@ -41,15 +43,17 @@ class Backup { await this.createErrorDialog(); await this.loadSettings(); await this.startTimer(); - await this.upgradeBackupTargetVersion(); + await this.upgradeBackupPluginVersion(); await sevenZip.updateBinPath(); await sevenZip.setExecutionFlag(); this.backupStartTime = null; } - private async upgradeBackupTargetVersion() { - let version = await joplin.settings.value("backupVersion"); - const targetVersion = 1; + private async upgradeBackupPluginVersion() { + this.log.verbose("Upgrade Backup Plugin"); + let startVersion = await joplin.settings.value("backupVersion"); + let version = startVersion; + const targetVersion = 3; for ( let checkVersion = version + 1; checkVersion <= targetVersion; @@ -60,6 +64,26 @@ class Backup { if (this.backupBasePath !== "" && this.backupRetention > 1) { await this.saveOldBackupInfo(); } + } else if (checkVersion === 2) { + // When a path is set from old installation, disable the subfolder creation + if ( + this.backupBasePath && + this.backupBasePath !== "" && + startVersion === 1 + ) { + this.log.verbose("createSubfolder: false"); + this.createSubfolder = false; + await joplin.settings.setValue("createSubfolder", false); + } + } else if (checkVersion === 3) { + // Apply value from singleJex to singleJexV2, because the default value was changed and for this a new field was added + if (startVersion > 0) { + this.log.verbose("singleJexV2: set to value from singleJex"); + await joplin.settings.setValue( + "singleJexV2", + await joplin.settings.value("singleJex") + ); + } } version = checkVersion; @@ -196,6 +220,18 @@ class Backup { ); } + if (this.createSubfolder) { + this.log.verbose("append subFolder"); + this.backupBasePath = path.join(this.backupBasePath, "JoplinBackup"); + if (!fs.existsSync(this.backupBasePath)) { + try { + fs.mkdirSync(this.backupBasePath); + } catch (e) { + await this.showError("create Folder: " + e.message); + } + } + } + if (path.normalize(profileDir) === this.backupBasePath) { this.backupBasePath = null; } @@ -203,12 +239,15 @@ class Backup { public async loadSettings() { this.log.verbose("loadSettings"); + this.createSubfolder = await joplin.settings.value("createSubfolder"); await this.loadBackupPath(); this.backupRetention = await joplin.settings.value("backupRetention"); this.zipArchive = await joplin.settings.value("zipArchive"); this.compressionLevel = await joplin.settings.value("compressionLevel"); - this.singleJex = await joplin.settings.value("singleJex"); + this.singleJex = await joplin.settings.value("singleJexV2"); + + this.backupPlugins = await joplin.settings.value("backupPlugins"); this.backupSetName = await joplin.settings.value("backupSetName"); if ( @@ -309,6 +348,7 @@ class Backup { const settings = [ "path", "singleJex", + "singleJexV2", "backupRetention", "backupInterval", "onlyOnChange", @@ -320,6 +360,9 @@ class Backup { "exportPath", "backupSetName", "backupInfo", + "backupVersion", + "backupPlugins", + "createSubfolder", ]; this.log.verbose("Plugin settings:"); @@ -778,6 +821,14 @@ class Backup { path.join(activeBackupFolderProfile, "userstyle.css") ); + // Backup plugins files + if (this.backupPlugins === true) { + await this.backupFolder( + path.join(profileDir, "plugins"), + path.join(activeBackupFolderProfile, "plugins") + ); + } + // Backup Templates try { await this.backupFolder( diff --git a/src/helper.ts b/src/helper.ts index a47196c..b36289d 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -1,3 +1,5 @@ +import joplin from "api"; + export namespace helper { export async function validFileName(fileName: string) { var regChar = /[:*?"<>\/|\\]+/; // forbidden characters \ / : * ? " < > | @@ -9,4 +11,12 @@ export namespace helper { return true; } } + + export async function joplinVersionInfo(): Promise { + try { + return await joplin.versionInfo(); + } catch (error) { + return null; + } + } } diff --git a/src/manifest.json b/src/manifest.json index be16044..34ca7f4 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 1, "id": "io.github.jackgruber.backup", "app_min_version": "2.1.3", - "version": "1.0.5", + "version": "1.1.0", "name": "Simple Backup", "description": "Plugin to create manual and automatic backups.", "author": "JackGruber", diff --git a/src/settings.ts b/src/settings.ts index 141d0d8..dc92145 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,5 +1,6 @@ import joplin from "api"; -import { SettingItemType } from "api/types"; +import { SettingItemType, SettingItemSubType } from "api/types"; +import { helper } from "./helper"; export namespace Settings { export async function register() { @@ -8,22 +9,30 @@ export namespace Settings { iconName: "fas fa-archive", }); - await joplin.settings.registerSettings({ - path: { + const joplinVersion = await helper.joplinVersionInfo(); + + let pathSettings = null; + if (joplinVersion !== null) { + pathSettings = { value: "", type: SettingItemType.String, + subType: SettingItemSubType.DirectoryPath, section: "backupSection", public: true, label: "Backup path", - }, - singleJex: { - value: false, - type: SettingItemType.Bool, + }; + } else { + pathSettings = { + value: "", + type: SettingItemType.String, section: "backupSection", public: true, - label: "Single JEX", - description: "Create only one JEX file for all notebooks.", - }, + label: "Backup path", + }; + } + + await joplin.settings.registerSettings({ + path: pathSettings, backupRetention: { value: 1, minimum: 1, @@ -33,7 +42,7 @@ export namespace Settings { public: true, label: "Keep x backups", description: - "If more than one verison is configured, folders are created in the Backup Path acording to backupSetName setting.", + "If more than one version is configured, folders are created in the Backup Path acording to backupSetName setting.", }, backupInterval: { value: 24, @@ -103,6 +112,16 @@ export namespace Settings { error: "Error", }, }, + createSubfolder: { + value: true, + type: SettingItemType.Bool, + section: "backupSection", + public: true, + advanced: true, + label: "Create Subfolder", + description: + "Create a subfolder in the the configured `Backup path`. Deactivate only if there is no other data in the `Backup path`!", + }, zipArchive: { value: "no", type: SettingItemType.String, @@ -157,6 +176,34 @@ export namespace Settings { description: "Name of the backup set if multiple backups are to be keep.", }, + backupPlugins: { + value: true, + type: SettingItemType.Bool, + section: "backupSection", + public: true, + advanced: true, + label: "Backup plugins", + description: "Backup plugin jpl files", + }, + singleJexV2: { + value: true, + type: SettingItemType.Bool, + section: "backupSection", + public: true, + advanced: true, + label: "Single JEX", + description: + "Create only one JEX file for all notebooks (Recommended to prevent the loss of internal note links and folder structure).", + }, + singleJex: { + value: false, + type: SettingItemType.Bool, + section: "backupSection", + public: false, + advanced: true, + label: "Single JEX", + description: "Old setting, for compatibility and upgrade only.", + }, backupVersion: { value: 0, type: SettingItemType.Int,