diff --git a/.gitignore b/.gitignore index 798bf5409..ad60fd8c1 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ packages/encryption/dist packages/files/dist packages/models/dist packages/services/dist +packages/utils/dist **/.pnp.* **/.yarn/* diff --git a/.yarn/cache/@standardnotes-utils-npm-1.6.12-8fa8d7d09b-e177b1fa51.zip b/.yarn/cache/@standardnotes-utils-npm-1.6.12-8fa8d7d09b-e177b1fa51.zip deleted file mode 100644 index 66f715046..000000000 Binary files a/.yarn/cache/@standardnotes-utils-npm-1.6.12-8fa8d7d09b-e177b1fa51.zip and /dev/null differ diff --git a/.yarn/cache/@standardnotes-utils-npm-1.6.2-2a6213ff14-e7d9087992.zip b/.yarn/cache/@standardnotes-utils-npm-1.6.2-2a6213ff14-e7d9087992.zip deleted file mode 100644 index cba407410..000000000 Binary files a/.yarn/cache/@standardnotes-utils-npm-1.6.2-2a6213ff14-e7d9087992.zip and /dev/null differ diff --git a/.yarn/cache/@types-dompurify-npm-2.3.3-502805169c-427e2dc60d.zip b/.yarn/cache/@types-dompurify-npm-2.3.3-502805169c-427e2dc60d.zip new file mode 100644 index 000000000..713a976f1 Binary files /dev/null and b/.yarn/cache/@types-dompurify-npm-2.3.3-502805169c-427e2dc60d.zip differ diff --git a/.yarn/cache/@types-jsdom-npm-16.2.14-bfbb37071c-12bb926fa7.zip b/.yarn/cache/@types-jsdom-npm-16.2.14-bfbb37071c-12bb926fa7.zip new file mode 100644 index 000000000..62ae3528b Binary files /dev/null and b/.yarn/cache/@types-jsdom-npm-16.2.14-bfbb37071c-12bb926fa7.zip differ diff --git a/.yarn/cache/@types-parse5-npm-6.0.3-a0bee0f9b4-ddb59ee414.zip b/.yarn/cache/@types-parse5-npm-6.0.3-a0bee0f9b4-ddb59ee414.zip new file mode 100644 index 000000000..57baab673 Binary files /dev/null and b/.yarn/cache/@types-parse5-npm-6.0.3-a0bee0f9b4-ddb59ee414.zip differ diff --git a/.yarn/cache/@types-tough-cookie-npm-4.0.2-9e61f877e6-e055556ffd.zip b/.yarn/cache/@types-tough-cookie-npm-4.0.2-9e61f877e6-e055556ffd.zip new file mode 100644 index 000000000..10d67a133 Binary files /dev/null and b/.yarn/cache/@types-tough-cookie-npm-4.0.2-9e61f877e6-e055556ffd.zip differ diff --git a/.yarn/cache/cssom-npm-0.5.0-44ab2704f2-823471aa30.zip b/.yarn/cache/cssom-npm-0.5.0-44ab2704f2-823471aa30.zip new file mode 100644 index 000000000..dc47b2e75 Binary files /dev/null and b/.yarn/cache/cssom-npm-0.5.0-44ab2704f2-823471aa30.zip differ diff --git a/.yarn/cache/data-urls-npm-3.0.2-c8b2050319-033fc3dd0f.zip b/.yarn/cache/data-urls-npm-3.0.2-c8b2050319-033fc3dd0f.zip new file mode 100644 index 000000000..015df7be6 Binary files /dev/null and b/.yarn/cache/data-urls-npm-3.0.2-c8b2050319-033fc3dd0f.zip differ diff --git a/.yarn/cache/domexception-npm-4.0.0-5093673f9b-ddbc1268ed.zip b/.yarn/cache/domexception-npm-4.0.0-5093673f9b-ddbc1268ed.zip new file mode 100644 index 000000000..4253de44a Binary files /dev/null and b/.yarn/cache/domexception-npm-4.0.0-5093673f9b-ddbc1268ed.zip differ diff --git a/.yarn/cache/entities-npm-4.3.1-3a76d89ee3-e8f6d2bac2.zip b/.yarn/cache/entities-npm-4.3.1-3a76d89ee3-e8f6d2bac2.zip new file mode 100644 index 000000000..e916792a5 Binary files /dev/null and b/.yarn/cache/entities-npm-4.3.1-3a76d89ee3-e8f6d2bac2.zip differ diff --git a/.yarn/cache/html-encoding-sniffer-npm-3.0.0-daac3dfe41-8d806aa004.zip b/.yarn/cache/html-encoding-sniffer-npm-3.0.0-daac3dfe41-8d806aa004.zip new file mode 100644 index 000000000..1fbdcc51d Binary files /dev/null and b/.yarn/cache/html-encoding-sniffer-npm-3.0.0-daac3dfe41-8d806aa004.zip differ diff --git a/.yarn/cache/jsdom-npm-20.0.0-9c1ad43ab8-f69b40679d.zip b/.yarn/cache/jsdom-npm-20.0.0-9c1ad43ab8-f69b40679d.zip new file mode 100644 index 000000000..f209ba812 Binary files /dev/null and b/.yarn/cache/jsdom-npm-20.0.0-9c1ad43ab8-f69b40679d.zip differ diff --git a/.yarn/cache/parse5-npm-7.0.0-3158a72394-7da5d61cc1.zip b/.yarn/cache/parse5-npm-7.0.0-3158a72394-7da5d61cc1.zip new file mode 100644 index 000000000..3159f21f0 Binary files /dev/null and b/.yarn/cache/parse5-npm-7.0.0-3158a72394-7da5d61cc1.zip differ diff --git a/.yarn/cache/saxes-npm-6.0.0-31558949f5-d3fa3e2aaf.zip b/.yarn/cache/saxes-npm-6.0.0-31558949f5-d3fa3e2aaf.zip new file mode 100644 index 000000000..487af08ad Binary files /dev/null and b/.yarn/cache/saxes-npm-6.0.0-31558949f5-d3fa3e2aaf.zip differ diff --git a/.yarn/cache/w3c-xmlserializer-npm-3.0.0-3419fc8f05-0af8589942.zip b/.yarn/cache/w3c-xmlserializer-npm-3.0.0-3419fc8f05-0af8589942.zip new file mode 100644 index 000000000..b26b4590c Binary files /dev/null and b/.yarn/cache/w3c-xmlserializer-npm-3.0.0-3419fc8f05-0af8589942.zip differ diff --git a/.yarn/cache/whatwg-encoding-npm-2.0.0-d7451f51b4-7087810c41.zip b/.yarn/cache/whatwg-encoding-npm-2.0.0-d7451f51b4-7087810c41.zip new file mode 100644 index 000000000..182445510 Binary files /dev/null and b/.yarn/cache/whatwg-encoding-npm-2.0.0-d7451f51b4-7087810c41.zip differ diff --git a/.yarn/cache/whatwg-mimetype-npm-3.0.0-5b617710c1-ce08bbb36b.zip b/.yarn/cache/whatwg-mimetype-npm-3.0.0-5b617710c1-ce08bbb36b.zip new file mode 100644 index 000000000..aa91c250d Binary files /dev/null and b/.yarn/cache/whatwg-mimetype-npm-3.0.0-5b617710c1-ce08bbb36b.zip differ diff --git a/.yarn/cache/xml-name-validator-npm-4.0.0-0857c21729-af100b79c2.zip b/.yarn/cache/xml-name-validator-npm-4.0.0-0857c21729-af100b79c2.zip new file mode 100644 index 000000000..abb3efef2 Binary files /dev/null and b/.yarn/cache/xml-name-validator-npm-4.0.0-0857c21729-af100b79c2.zip differ diff --git a/packages/components/src/Packages/Editors/org.standardnotes.advanced-checklist/package.json b/packages/components/src/Packages/Editors/org.standardnotes.advanced-checklist/package.json index 76ba47e71..86c182bdf 100644 --- a/packages/components/src/Packages/Editors/org.standardnotes.advanced-checklist/package.json +++ b/packages/components/src/Packages/Editors/org.standardnotes.advanced-checklist/package.json @@ -87,7 +87,7 @@ "@reduxjs/toolkit": "1.8.0", "@standardnotes/editor-kit": "2.2.5", "@standardnotes/stylekit": "5.23.0", - "@standardnotes/utils": "1.6.2", + "@standardnotes/utils": "workspace:*", "@testing-library/dom": "8.11.3", "@testing-library/jest-dom": "5.16.2", "@testing-library/react": "12.1.4", diff --git a/packages/encryption/package.json b/packages/encryption/package.json index ad5721ecb..8cb8fcaa4 100644 --- a/packages/encryption/package.json +++ b/packages/encryption/package.json @@ -43,7 +43,7 @@ "@standardnotes/responses": "^1.6.39", "@standardnotes/services": "workspace:*", "@standardnotes/sncrypto-common": "^1.9.0", - "@standardnotes/utils": "^1.6.12", + "@standardnotes/utils": "workspace:*", "reflect-metadata": "^0.1.13" } } diff --git a/packages/filepicker/package.json b/packages/filepicker/package.json index 5b102a318..5d0c0469a 100644 --- a/packages/filepicker/package.json +++ b/packages/filepicker/package.json @@ -35,7 +35,7 @@ "dependencies": { "@standardnotes/common": "^1.23.1", "@standardnotes/services": "workspace:*", - "@standardnotes/utils": "^1.6.12", + "@standardnotes/utils": "workspace:*", "@types/wicg-file-system-access": "^2020.9.5", "reflect-metadata": "^0.1.13" } diff --git a/packages/files/package.json b/packages/files/package.json index e9b2c0e2f..b84b94eb5 100644 --- a/packages/files/package.json +++ b/packages/files/package.json @@ -39,7 +39,7 @@ "@standardnotes/responses": "^1.6.39", "@standardnotes/services": "workspace:*", "@standardnotes/sncrypto-common": "^1.9.0", - "@standardnotes/utils": "^1.6.12", + "@standardnotes/utils": "workspace:*", "reflect-metadata": "^0.1.13" } } diff --git a/packages/models/package.json b/packages/models/package.json index 28c696298..2b75290a2 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -36,7 +36,7 @@ "@standardnotes/common": "^1.23.1", "@standardnotes/features": "workspace:*", "@standardnotes/responses": "^1.6.39", - "@standardnotes/utils": "^1.6.12", + "@standardnotes/utils": "workspace:*", "lodash": "^4.17.21", "reflect-metadata": "^0.1.13" } diff --git a/packages/services/package.json b/packages/services/package.json index 97cddb2f3..8c6378a60 100644 --- a/packages/services/package.json +++ b/packages/services/package.json @@ -29,7 +29,7 @@ "@standardnotes/common": "^1.23.1", "@standardnotes/models": "workspace:*", "@standardnotes/responses": "^1.6.39", - "@standardnotes/utils": "^1.6.12", + "@standardnotes/utils": "workspace:*", "reflect-metadata": "^0.1.13" }, "devDependencies": { diff --git a/packages/utils/.eslintignore b/packages/utils/.eslintignore new file mode 100644 index 000000000..f06235c46 --- /dev/null +++ b/packages/utils/.eslintignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/packages/utils/.eslintrc b/packages/utils/.eslintrc new file mode 100644 index 000000000..cb7136174 --- /dev/null +++ b/packages/utils/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": "../../.eslintrc", + "parserOptions": { + "project": "./linter.tsconfig.json" + } +} diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md new file mode 100644 index 000000000..af2598b94 --- /dev/null +++ b/packages/utils/CHANGELOG.md @@ -0,0 +1,156 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.6.13](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.12...@standardnotes/utils@1.6.13) (2022-07-04) + +### Bug Fixes + +* add jsdom types to utils package ([72b257c](https://github.com/standardnotes/snjs/commit/72b257cf878306905781e61ef4b32d6d96ab6885)) +* add missing jsdom to utils package ([7ccce33](https://github.com/standardnotes/snjs/commit/7ccce3329d1f03bcdfad88f6b908bf7e407631a0)) +* add missing reflect-metadata package to all packages ([ce3a5bb](https://github.com/standardnotes/snjs/commit/ce3a5bbf3f1d2276ac4abc3eec3c6a44c8c3ba9b)) + +## [1.6.12](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.11...@standardnotes/utils@1.6.12) (2022-06-27) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.11](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.10...@standardnotes/utils@1.6.11) (2022-06-15) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.10](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.9...@standardnotes/utils@1.6.10) (2022-05-22) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.9](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.8...@standardnotes/utils@1.6.9) (2022-05-17) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.8](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.7...@standardnotes/utils@1.6.8) (2022-05-16) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.7](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.6...@standardnotes/utils@1.6.7) (2022-05-16) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.6](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.5...@standardnotes/utils@1.6.6) (2022-05-06) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.5](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.3...@standardnotes/utils@1.6.5) (2022-05-04) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.4](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.3...@standardnotes/utils@1.6.4) (2022-05-04) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.3](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.2...@standardnotes/utils@1.6.3) (2022-04-28) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.2](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.1...@standardnotes/utils@1.6.2) (2022-04-22) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.6.1](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.6.0...@standardnotes/utils@1.6.1) (2022-04-21) + +**Note:** Version bump only for package @standardnotes/utils + +# [1.6.0](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.5.0...@standardnotes/utils@1.6.0) (2022-04-15) + +### Features + +* introduce sync resolved payloads to ensure deltas always return up to date dirty state ([#694](https://github.com/standardnotes/snjs/issues/694)) ([e5278ba](https://github.com/standardnotes/snjs/commit/e5278ba0b2afa987c37f009a2101fb91949d44c6)) + +# [1.5.0](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.4.8...@standardnotes/utils@1.5.0) (2022-04-15) + +### Features + +* no merge payloads in payload manager ([#693](https://github.com/standardnotes/snjs/issues/693)) ([68a577c](https://github.com/standardnotes/snjs/commit/68a577cb887fd2d5556dc9ddec461f6ae665fcb6)) + +## [1.4.8](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.4.7...@standardnotes/utils@1.4.8) (2022-04-11) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.4.7](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.4.6...@standardnotes/utils@1.4.7) (2022-04-01) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.4.6](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.4.5...@standardnotes/utils@1.4.6) (2022-03-31) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.4.5](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.4.4...@standardnotes/utils@1.4.5) (2022-03-31) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.4.4](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.4.3...@standardnotes/utils@1.4.4) (2022-03-30) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.4.3](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.4.2...@standardnotes/utils@1.4.3) (2022-03-22) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.4.2](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.4.1...@standardnotes/utils@1.4.2) (2022-03-21) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.4.1](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.4.0...@standardnotes/utils@1.4.1) (2022-03-18) + +**Note:** Version bump only for package @standardnotes/utils + +# [1.4.0](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.3.0...@standardnotes/utils@1.4.0) (2022-03-16) + +### Features + +* delete file functionality ([#657](https://github.com/standardnotes/snjs/issues/657)) ([edec4f7](https://github.com/standardnotes/snjs/commit/edec4f7a65ef557ed5f47be4dddcf2b659ee28b4)) + +## [1.3.1](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.3.0...@standardnotes/utils@1.3.1) (2022-03-16) + +**Note:** Version bump only for package @standardnotes/utils + +# [1.3.0](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.2.3...@standardnotes/utils@1.3.0) (2022-03-14) + +### Features + +* move vault into applications package ([#653](https://github.com/standardnotes/snjs/issues/653)) ([3d320eb](https://github.com/standardnotes/snjs/commit/3d320eb51ac74729ab8864f1c4c4f24d8fb794d5)) + +## [1.2.3](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.2.1...@standardnotes/utils@1.2.3) (2022-02-28) + +### Bug Fixes + +* add pseudo change to get lerna to trigger ([41e6817](https://github.com/standardnotes/snjs/commit/41e6817bbf726b0932cdf16f58622328b9e42803)) + +## [1.2.2](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.2.1...@standardnotes/utils@1.2.2) (2022-02-28) + +### Bug Fixes + +* add pseudo change to get lerna to trigger ([41e6817](https://github.com/standardnotes/snjs/commit/41e6817bbf726b0932cdf16f58622328b9e42803)) + +## [1.2.1](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.2.0...@standardnotes/utils@1.2.1) (2022-02-27) + +**Note:** Version bump only for package @standardnotes/utils + +# [1.2.0](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.1.2...@standardnotes/utils@1.2.0) (2022-02-25) + +### Features + +* extract core functionalities to separate packages ([#610](https://github.com/standardnotes/snjs/issues/610)) ([801547a](https://github.com/standardnotes/snjs/commit/801547a71614ad51a92fb249eaa184ed46a44aac)) + +## [1.1.2](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.1.1...@standardnotes/utils@1.1.2) (2022-02-24) + +**Note:** Version bump only for package @standardnotes/utils + +## [1.1.1](https://github.com/standardnotes/snjs/compare/@standardnotes/utils@1.1.0...@standardnotes/utils@1.1.1) (2022-02-22) + +**Note:** Version bump only for package @standardnotes/utils + +# 1.1.0 (2022-02-22) + +### Features + +* extract SNJS utils as a separate package ([#604](https://github.com/standardnotes/snjs/issues/604)) ([b28195c](https://github.com/standardnotes/snjs/commit/b28195c20be788eec8dabc44c5aff518f074cdd9)) diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js new file mode 100644 index 000000000..bef9ea6b9 --- /dev/null +++ b/packages/utils/jest.config.js @@ -0,0 +1,19 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const base = require('../../node_modules/@standardnotes/config/src/jest.json'); + +module.exports = { + ...base, + globals: { + 'ts-jest': { + tsconfig: 'tsconfig.json', + }, + }, + coverageThreshold: { + global: { + branches: 4, + functions: 4, + lines: 24, + statements: 25 + } + } +}; diff --git a/packages/utils/linter.tsconfig.json b/packages/utils/linter.tsconfig.json new file mode 100644 index 000000000..c1a7d22c5 --- /dev/null +++ b/packages/utils/linter.tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["dist"] +} diff --git a/packages/utils/package.json b/packages/utils/package.json new file mode 100644 index 000000000..3e249db3d --- /dev/null +++ b/packages/utils/package.json @@ -0,0 +1,45 @@ +{ + "name": "@standardnotes/utils", + "version": "1.7.0", + "engines": { + "node": ">=16.0.0 <17.0.0" + }, + "description": "Common utilities for Standard Notes projects", + "main": "dist/index.js", + "author": "Standard Notes", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "license": "AGPL-3.0-or-later", + "scripts": { + "clean": "rm -fr dist", + "prestart": "yarn clean", + "start": "tsc -p tsconfig.json --watch", + "prebuild": "yarn clean", + "build": "tsc -p tsconfig.json", + "lint": "eslint . --ext .ts", + "test:unit": "jest spec" + }, + "dependencies": { + "@standardnotes/common": "^1.23.1", + "dompurify": "^2.3.6", + "lodash": "^4.17.21", + "reflect-metadata": "^0.1.13" + }, + "devDependencies": { + "@types/dompurify": "^2.3.3", + "@types/jest": "^27.4.1", + "@types/jsdom": "^16.2.14", + "@types/lodash": "^4.14.179", + "@types/node": "^18.0.0", + "@typescript-eslint/eslint-plugin": "^5.30.0", + "eslint-plugin-prettier": "^4.2.1", + "jest": "^27.5.1", + "jsdom": "^20.0.0", + "ts-jest": "^27.1.3" + } +} diff --git a/packages/utils/src/Domain/Deferred/Deferred.ts b/packages/utils/src/Domain/Deferred/Deferred.ts new file mode 100644 index 000000000..4010029b3 --- /dev/null +++ b/packages/utils/src/Domain/Deferred/Deferred.ts @@ -0,0 +1,15 @@ +export const Deferred = () => { + let resolve!: (value: T | PromiseLike) => void + let reject!: () => void + + const promise = new Promise((res, rej) => { + resolve = res + reject = rej + }) + + return { + resolve, + reject, + promise, + } +} diff --git a/packages/utils/src/Domain/Utils/Utils.spec.ts b/packages/utils/src/Domain/Utils/Utils.spec.ts new file mode 100644 index 000000000..306ac3476 --- /dev/null +++ b/packages/utils/src/Domain/Utils/Utils.spec.ts @@ -0,0 +1,61 @@ +import * as DOMPurifyLib from 'dompurify' +import { JSDOM } from 'jsdom' +import { sortByKey, withoutLastElement } from './Utils' + +const window = new JSDOM('').window +const DOMPurify = DOMPurifyLib(window as never) + +describe('Utils', () => { + it('sanitizeHtmlString', () => { + const dirty = '' + const cleaned = DOMPurify.sanitize(dirty) + expect(cleaned).toEqual('') + }) + + it('without last works', () => { + expect(withoutLastElement([])).toEqual([]) + expect(withoutLastElement(['a'])).toEqual([]) + expect(withoutLastElement(['a', 'b'])).toEqual(['a']) + expect(withoutLastElement(['a', 'b', 'c'])).toEqual(['a', 'b']) + }) + + const sortByKey_INPUT = [ + { id: 'aza', age: 0, missing: 7 }, + { id: 'aaa', age: 12, missing: 8 }, + { id: 'ace', age: 12, missing: 0 }, + { id: 'zzz', age: -9 }, + ] + + it('sortByKey sort by key', () => { + const input = sortByKey_INPUT + + expect(sortByKey(input, 'id')).toEqual([ + { id: 'aaa', age: 12, missing: 8 }, + { id: 'ace', age: 12, missing: 0 }, + { id: 'aza', age: 0, missing: 7 }, + { id: 'zzz', age: -9 }, + ]) + + expect(sortByKey(input, 'age')).toEqual([ + { id: 'zzz', age: -9 }, + { id: 'aza', age: 0, missing: 7 }, + { id: 'aaa', age: 12, missing: 8 }, + { id: 'ace', age: 12, missing: 0 }, + ]) + + expect(sortByKey(input, 'missing')).toEqual([ + { id: 'ace', age: 12, missing: 0 }, + { id: 'aza', age: 0, missing: 7 }, + { id: 'aaa', age: 12, missing: 8 }, + { id: 'zzz', age: -9 }, + ]) + }) + + it('sortByKey does not mutate the input & creates a new array', () => { + const input = sortByKey_INPUT + const initial = [...input] + + expect(sortByKey(input, 'id')).not.toBe(input) + expect(initial).toEqual(input) + }) +}) diff --git a/packages/utils/src/Domain/Utils/Utils.ts b/packages/utils/src/Domain/Utils/Utils.ts new file mode 100644 index 000000000..3c3cb7fe5 --- /dev/null +++ b/packages/utils/src/Domain/Utils/Utils.ts @@ -0,0 +1,681 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import * as DOMPurify from 'dompurify' +import { find, isArray, mergeWith, remove, uniq, uniqWith } from 'lodash' +import { AnyRecord } from '@standardnotes/common' + +const collator = typeof Intl !== 'undefined' ? new Intl.Collator('en', { numeric: true }) : undefined + +export function getGlobalScope(): Window | unknown | null { + return typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : null +} + +export function dictToArray(dict: Record): T[] { + return Object.values(dict) +} + +/** + * Whether we are in a web browser + */ +export function isWebEnvironment(): boolean { + return getGlobalScope() !== null +} + +interface IEDocument { + documentMode?: number +} + +/** + * @returns true if WebCrypto is available + */ +export function isWebCryptoAvailable(): boolean { + return ( + (isWebEnvironment() && !isReactNativeEnvironment() && !(document && (document as IEDocument).documentMode)) || + (/Edge/.test(navigator.userAgent) && window.crypto && !!window.crypto.subtle) + ) +} + +/** + * Whether we are in React Native app + */ +export function isReactNativeEnvironment(): boolean { + return typeof navigator !== 'undefined' && navigator.product === 'ReactNative' +} + +/** + * Searches array of objects for first object where object[key] === value + * @returns Matching object or null if not found + */ +export function findInArray(array: T[], key: K, value: T[K]): T | undefined { + return array.find((item: T) => item[key] === value) +} + +/** + * Searches array of objects for first object where object[key] === value + * @returns Matching object or null if not found + */ +export function searchArray(array: T[], predicate: Partial): T | undefined { + return find(array, predicate) as T +} + +export function sureSearchArray(array: T[], predicate: Partial): T { + return searchArray(array, predicate) as T +} + +/** + * @returns Whether the value is a function or object + */ +export function isObject(value: unknown): value is object { + if (value === null) { + return false + } + return typeof value === 'function' || typeof value === 'object' +} + +/** + * @returns Whether the value is a function + */ +export function isFunction(value: unknown): boolean { + if (value === null) { + return false + } + return typeof value === 'function' +} + +/** + * @returns True if the object is null or undefined, otherwise false + */ +export function isNullOrUndefined(value: unknown): value is null | undefined { + return value === null || value === undefined +} + +export function isNotUndefined(val: T | undefined | null): val is T { + return val != undefined +} + +/** + * @returns True if the string is empty or undefined + */ +export function isEmpty(string: string): boolean { + return !string || string.length === 0 +} + +/** + * @returns Whether the value is a string + */ +export function isString(value: unknown): value is string { + return typeof value === 'string' || value instanceof String +} + +/** + * @returns The greater of the two dates + */ +export function greaterOfTwoDates(dateA: Date, dateB: Date): Date { + if (dateA > dateB) { + return dateA + } else { + return dateB + } +} + +/** + * Returns a new array containing only unique values by combining the two input arrays. + * Elements are unique based on the values of `equalityKeys`. + * @param equalityKeys - Keys to determine element equality + * @returns Array containing unique values + */ +export function uniqCombineObjArrays(arrayA: T[], arrayB: T[], equalityKeys: (keyof T)[]): T[] { + return uniqWith(arrayA.concat(arrayB), (a: T, b: T) => { + for (const key of equalityKeys) { + if (a[key] !== b[key]) { + return false + } + } + return true + }) +} + +/** + * Returns a new array containing only unique values + * @returns Array containing unique values + */ +export function uniqueArray(array: T[]): T[] { + return uniq(array) +} + +/** + * Returns a new array containing only unique values + * @returns Array containing unique values + */ +export function uniqueArrayByKey(array: T[], key: keyof T): T[] { + return uniqWith(array, (a: T, b: T) => { + return a[key] === b[key] + }) +} + +/** + * Returns the last element in the array. + * @returns The last element in the array + */ +export function lastElement(array: T[]): T | undefined { + return array[array.length - 1] +} + +/** + * Adds all items from otherArray into inArray, in-place. + * Does not return a value. + */ +export function extendArray(inArray: T[], otherArray: K[]): void { + for (const value of otherArray) { + inArray.push(value) + } +} + +/** + * Removes all items appearing in toSubtract from inArray, in-place + * @param toSubtract - The list of items to remove from inArray + */ +export function subtractFromArray(inArray: T[], toSubtract: T[]): void { + for (const value of toSubtract) { + removeFromArray(inArray, value) + } +} + +/** + * Removes the first matching element of an array by strict equality. + * If no matchin element is found, the array is left unchanged. + */ +export function removeFromArray(array: T[], value: T): void { + const valueIndex = array.indexOf(value) + if (valueIndex === -1) { + return + } + array.splice(valueIndex, 1) +} + +/** + * Adds the element to the array if the array does not already include the value. + * The array is searched via array.indexOf + * @returns true if value was added + */ +export function addIfUnique(array: T[], value: T): boolean { + if (!array.includes(value)) { + array.push(value) + return true + } + return false +} + +/** + * Removes an object from the array in-place by searching for an object where all the + * key/values in predicate match with the candidate element. + */ +export function filterFromArray( + array: T[], + predicate: Partial> | ((object: T) => boolean), +): void { + remove(array, predicate) +} + +/** + * Returns a new array by removing all elements in subtract from array + */ +export function arrayByDifference(array: T[], subtract: T[]): T[] { + return array.filter((x) => !subtract.includes(x)).concat(subtract.filter((x) => !array.includes(x))) +} + +export function compareValues(left: T, right: T) { + if ((left && !right) || (!left && right)) { + return false + } + if (left instanceof Date && right instanceof Date) { + return left.getTime() === right.getTime() + } else if (left instanceof String && right instanceof String) { + return left === right + } else { + return topLevelCompare(left, right) + } +} + +/** + * Removes the value from the array at the given index, in-place. + */ +export function removeFromIndex(array: any[], index: number) { + array.splice(index, 1) +} + +/** + * Adds the value from the array at the given index, in-place. + */ +export function addAtIndex(array: T[], element: T, index: number) { + array.splice(index, 0, element) +} + +/** + * Returns a new array by removeing the value from the array at the given index + */ +export function arrayByRemovingFromIndex(array: T[], index: number) { + const copy = array.slice() + removeFromIndex(copy, index) + return copy +} + +/** + * Returns an array where each element is the value of a top-level + * object key. + * Example: objectToValueArray({a: 1, b: 2}) returns [1, 2] + */ +export function objectToValueArray(object: AnyRecord) { + const values = [] + for (const key of Object.keys(object)) { + values.push(object[key]) + } + return values +} + +/** + * Returns a key-sorted copy of the object. + * For example, sortedCopy({b: '1', a: '2'}) returns {a: '2', b: '1'} + */ +export function sortedCopy(object: any) { + const keys = Object.keys(object).sort() + const result: any = {} + for (const key of keys) { + result[key] = object[key] + } + return Copy(result) +} + +export const sortByKey = (input: T[], key: keyof T): T[] => { + const compare = (a: T, b: T): number => { + const valueA = a[key] + const valueB = b[key] + + if (valueA < valueB) { + return -1 + } + if (valueA > valueB) { + return 1 + } + return 0 + } + + const newArray = [...input] + newArray.sort(compare) + + return newArray +} + +/** Returns a new object by omitting any keys which have an undefined or null value */ +export function omitUndefinedCopy(object: any) { + const result: any = {} + for (const key of Object.keys(object)) { + if (!isNullOrUndefined(object[key])) { + result[key] = object[key] + } + } + return result +} + +/** + * Returns a new array by sorting an array of elements based on a date property, + * as indicated by the input key value. + */ +export function dateSorted(elements: T[], key: keyof T, ascending = true) { + return elements.sort((a, b) => { + const aTimestamp = (a[key] as unknown as Date).getTime() + const bTimestamp = (b[key] as unknown as Date).getTime() + const vector = ascending ? 1 : -1 + if (aTimestamp < bTimestamp) { + return -1 * vector + } else if (aTimestamp > bTimestamp) { + return 1 * vector + } else { + return 0 + } + }) +} + +/** Compares for equality by comparing top-level keys value equality (===) */ +export function topLevelCompare(left: T, right: T) { + if (!left && !right) { + return true + } + if (!left || !right) { + return false + } + const leftKeys = Object.keys(left) + const rightKeys = Object.keys(right) + if (leftKeys.length !== rightKeys.length) { + return false + } + for (const key of leftKeys) { + if ((left as any)[key] !== (right as any)[key]) { + return false + } + } + return true +} + +/** + * Returns a new object by attempting to JSON.parse any top-level object keys. + */ +export function jsonParseEmbeddedKeys(object: AnyRecord) { + const result: AnyRecord = {} + for (const key of Object.keys(object)) { + let value + try { + value = JSON.parse(object[key] as string) + } catch (error) { + value = object[key] + } + result[key] = value + } + return result +} + +export const withoutLastElement = (array: T[]): T[] => { + return array.slice(0, -1) +} + +/** + * Deletes keys of the input object. + */ +export function omitInPlace(object: T, keys: Array) { + if (!object) { + return + } + for (const key of keys) { + delete object[key] + } +} + +/** + * Creates a new object by omitting `keys` from `object` + */ +export function omitByCopy(object: T, keys: Array) { + if (isNullOrUndefined(object)) { + return undefined + } + const newObject = Object.assign({}, object) + /** + * Lodash's omit, which was previously used, seems to cause unexpected behavior + * when payload is an ES6 item class. So we instead manually omit each key. + */ + for (const key of keys) { + delete newObject[key] + } + return newObject +} + +/** + * Similiar to Node's path.join, this function combines an array of paths into + * one resolved path. + */ +export function joinPaths(...args: string[]) { + return args + .map((part, i) => { + if (i === 0) { + return part.trim().replace(/[/]*$/g, '') + } else { + return part.trim().replace(/(^[/]*|[/]*$)/g, '') + } + }) + .filter((x) => x.length) + .join('/') +} + +/** + * Creates a copy of the input object by JSON stringifying the object then JSON parsing + * the string (if the input is an object). If input is date, a Date copy will be created, + * and if input is a primitive value, it will be returned as-is. + */ +export function Copy(object: any) { + if (object instanceof Date) { + return new Date(object) + } else if (isObject(object)) { + return JSON.parse(JSON.stringify(object)) + } else { + return object + } +} + +/** + * Merges the second object parameter into the first object, in-place. + * @returns The now modified first object parameter passed into the function. + */ +export function deepMerge(a: AnyRecord, b: AnyRecord) { + /** + * lodash.merge will not merge a full array with an empty one. + * deepMerge will replace arrays wholesale + */ + if (!a || !b) { + throw 'Attempting to deepMerge with null values' + } + const customizer = (aValue: any, bValue: any) => { + if (isArray(aValue)) { + return bValue + } + } + mergeWith(a, b, customizer) + return a +} + +/** + * Returns a new object by selecting certain keys from input object. + */ +export function pickByCopy(object: T, keys: Array) { + const result = {} as T + for (const key of keys) { + result[key] = object[key] + } + return Copy(result) +} + +/** + * Recursively makes an object immutable via Object.freeze + */ +export function deepFreeze(object: any) { + const propNames = Object.getOwnPropertyNames(object) + for (const name of propNames) { + const value = object[name] + if (value && typeof value === 'object' && !Object.isFrozen(value)) { + object[name] = deepFreeze(value) + } else { + object[name] = value + } + } + + return Object.freeze(object) +} + +export function isValidUrl(url: string): boolean { + try { + new URL(url) + return true + } catch (error) { + return false + } +} + +/** + * Determines if an object has a getter defined for a given property + */ +export function hasGetter(object: any, property: string) { + const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(object), property) + return descriptor && !isNullOrUndefined(descriptor.get) +} + +/** + * Truncates a hex string into a desired number of bits + * @returns A hexadecimal string truncated to the number of desired bits + */ +export function truncateHexString(string: string, desiredBits: number) { + const BITS_PER_HEX_CHAR = 4 + const desiredCharLength = desiredBits / BITS_PER_HEX_CHAR + return string.substring(0, desiredCharLength) +} + +/** + * When awaited, this function allows code execution to pause for a set time. + * Should be used primarily for testing. + */ +export async function sleep(milliseconds: number, warn = true): Promise { + if (warn) { + console.warn(`Sleeping for ${milliseconds}ms`) + } + return new Promise((resolve) => { + setTimeout(function () { + resolve() + }, milliseconds) + }) +} + +export function assertUnreachable(uncheckedCase: never): never { + throw Error('Unchecked case ' + uncheckedCase) +} + +/** + * Returns a boolean representing whether two dates are on the same day + */ +export function isSameDay(dateA: Date, dateB: Date) { + return ( + dateA.getFullYear() === dateB.getFullYear() && + dateA.getMonth() === dateB.getMonth() && + dateA.getDate() === dateB.getDate() + ) +} + +/** + * Sorts an array of objects in natural order + * @param items - The array of objects to sort + * @param property - The objects' property to sort by + * @param direction - The sorting direction, either ascending (default) or descending + * @returns Array of objects sorted in natural order + */ +export function naturalSort( + items: T[], + property: keyof T, + direction: 'asc' | 'desc' = 'asc', +): T[] { + switch (direction) { + case 'asc': + return [...items].sort( + collator + ? (a, b) => collator.compare(a[property] as string, b[property] as string) + : (a, b) => (a[property] as string).localeCompare(b[property] as string, 'en', { numeric: true }), + ) + case 'desc': + return [...items].sort( + collator + ? (a, b) => collator.compare(b[property] as string, a[property] as string) + : (a, b) => (b[property] as string).localeCompare(a[property] as string, 'en', { numeric: true }), + ) + } +} + +export function arraysEqual(left: T[], right: T[]): boolean { + if (left.length !== right.length) { + return false + } + return left.every((item) => right.includes(item)) && right.every((item) => left.includes(item)) +} + +const MicrosecondsInAMillisecond = 1_000 +const MillisecondsInASecond = 1_000 + +enum TimestampDigits { + Seconds = 10, + Milliseconds = 13, + Microseconds = 16, +} + +export function convertTimestampToMilliseconds(timestamp: number): number { + const digits = String(timestamp).length + switch (digits) { + case TimestampDigits.Seconds: + return timestamp * MillisecondsInASecond + case TimestampDigits.Milliseconds: + return timestamp + case TimestampDigits.Microseconds: + return Math.floor(timestamp / MicrosecondsInAMillisecond) + + default: + throw `Unhandled timestamp precision: ${timestamp}` + } +} + +export function sanitizeHtmlString(html: string): string { + return DOMPurify.sanitize(html) +} + +let sharedDateFormatter: unknown +export function dateToLocalizedString(date: Date): string { + if (typeof Intl !== 'undefined' && Intl.DateTimeFormat && typeof navigator !== 'undefined') { + if (!sharedDateFormatter) { + const locale = navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language + sharedDateFormatter = new Intl.DateTimeFormat(locale, { + year: 'numeric', + month: 'short', + day: '2-digit', + weekday: 'long', + hour: '2-digit', + minute: '2-digit', + }) + } + return (sharedDateFormatter as Intl.DateTimeFormat).format(date) + } else { + // IE < 11, Safari <= 9.0. + // In English, this generates the string most similar to + // the toLocaleDateString() result above. + return date.toDateString() + ' ' + date.toLocaleTimeString() + } +} + +export function nonSecureRandomIdentifier(): string { + return `${Math.random() * 100}`.replace('.', '') +} + +export function splitString(string: string, parts: number): string[] { + const outputLength = string.length + const partLength = outputLength / parts + const partitions = [] + for (let i = 0; i < parts; i++) { + const partition = string.slice(partLength * i, partLength * (i + 1)) + partitions.push(partition) + } + return partitions +} + +export function firstHalfOfString(string: string): string { + return string.substring(0, string.length / 2) +} + +export function secondHalfOfString(string: string): string { + return string.substring(string.length / 2, string.length) +} + +export function log(namespace: string, ...args: any[]): void { + const date = new Date() + const timeString = `${date.toLocaleTimeString().replace(' PM', '').replace(' AM', '')}.${date.getMilliseconds()}` + customLog( + `%c${namespace}%c${timeString}`, + 'color: black; font-weight: bold; margin-right: 4px', + 'color: gray', + ...args, + ) +} + +function customLog(..._args: any[]) { + // eslint-disable-next-line no-console, prefer-rest-params + Function.prototype.apply.call(console.log, console, arguments) +} + +export function assert(value: unknown): asserts value { + if (value === undefined) { + throw new Error('Assertion failed; value must be defined') + } +} + +export function useBoolean(value: boolean | undefined, defaultValue: boolean): boolean { + return value != undefined ? value : defaultValue +} diff --git a/packages/utils/src/Domain/Uuid/Utils.ts b/packages/utils/src/Domain/Uuid/Utils.ts new file mode 100644 index 000000000..e5321673e --- /dev/null +++ b/packages/utils/src/Domain/Uuid/Utils.ts @@ -0,0 +1,5 @@ +export function Uuids(items: { uuid: string }[]): string[] { + return items.map((item) => { + return item.uuid + }) +} diff --git a/packages/utils/src/Domain/Uuid/UuidGenerator.ts b/packages/utils/src/Domain/Uuid/UuidGenerator.ts new file mode 100644 index 000000000..265380a61 --- /dev/null +++ b/packages/utils/src/Domain/Uuid/UuidGenerator.ts @@ -0,0 +1,21 @@ +/** + * An abstract class with no instance methods. Used globally to generate uuids by any + * consumer. Application must call SetGenerator before use. + */ +export class UuidGenerator { + private static syncUuidFunc: () => string + + /** + * @param {function} syncImpl - A syncronous function that returns a UUID. + */ + static SetGenerator(syncImpl: () => string): void { + this.syncUuidFunc = syncImpl + } + + /** + * Generates a UUID string asyncronously. + */ + public static GenerateUuid(): string { + return this.syncUuidFunc() + } +} diff --git a/packages/utils/src/Domain/Uuid/UuidMap.ts b/packages/utils/src/Domain/Uuid/UuidMap.ts new file mode 100644 index 000000000..bcd58586e --- /dev/null +++ b/packages/utils/src/Domain/Uuid/UuidMap.ts @@ -0,0 +1,95 @@ +import { Uuid } from '@standardnotes/common' +import { addIfUnique, removeFromArray } from '../Utils/Utils' + +/** + * Maps a UUID to an array of UUIDS to establish either direct or inverse + * relationships between UUID strings (represantative of items or payloads). + */ +export class UuidMap { + /** uuid to uuids that we have a relationship with */ + private directMap: Partial> = {} + /** uuid to uuids that have a relationship with us */ + private inverseMap: Partial> = {} + + public makeCopy(): UuidMap { + const copy = new UuidMap() + copy.directMap = Object.assign({}, this.directMap) + copy.inverseMap = Object.assign({}, this.inverseMap) + return copy + } + + public getDirectRelationships(uuid: Uuid): Uuid[] { + return this.directMap[uuid] || [] + } + + public getInverseRelationships(uuid: Uuid): Uuid[] { + return this.inverseMap[uuid] || [] + } + + public establishRelationship(uuidA: Uuid, uuidB: Uuid): void { + this.establishDirectRelationship(uuidA, uuidB) + this.establishInverseRelationship(uuidA, uuidB) + } + + public deestablishRelationship(uuidA: Uuid, uuidB: Uuid): void { + this.deestablishDirectRelationship(uuidA, uuidB) + this.deestablishInverseRelationship(uuidA, uuidB) + } + + public setAllRelationships(uuid: Uuid, relationships: Uuid[]): void { + const previousDirect = this.directMap[uuid] || [] + this.directMap[uuid] = relationships + + /** Remove all previous values in case relationships have changed + * The updated references will be added afterwards. + */ + for (const previousRelationship of previousDirect) { + this.deestablishInverseRelationship(uuid, previousRelationship) + } + + /** Now map current relationships */ + for (const newRelationship of relationships) { + this.establishInverseRelationship(uuid, newRelationship) + } + } + + public removeFromMap(uuid: Uuid): void { + /** Items that we reference */ + const directReferences = this.directMap[uuid] || [] + for (const directReference of directReferences) { + removeFromArray(this.inverseMap[directReference] || [], uuid) + } + delete this.directMap[uuid] + + /** Items that are referencing us */ + const inverseReferences = this.inverseMap[uuid] || [] + for (const inverseReference of inverseReferences) { + removeFromArray(this.directMap[inverseReference] || [], uuid) + } + delete this.inverseMap[uuid] + } + + private establishDirectRelationship(uuidA: Uuid, uuidB: Uuid): void { + const index = this.directMap[uuidA] || [] + addIfUnique(index, uuidB) + this.directMap[uuidA] = index + } + + private establishInverseRelationship(uuidA: Uuid, uuidB: Uuid): void { + const inverseIndex = this.inverseMap[uuidB] || [] + addIfUnique(inverseIndex, uuidA) + this.inverseMap[uuidB] = inverseIndex + } + + private deestablishDirectRelationship(uuidA: Uuid, uuidB: Uuid): void { + const index = this.directMap[uuidA] || [] + removeFromArray(index, uuidB) + this.directMap[uuidA] = index + } + + private deestablishInverseRelationship(uuidA: Uuid, uuidB: Uuid): void { + const inverseIndex = this.inverseMap[uuidB] || [] + removeFromArray(inverseIndex, uuidA) + this.inverseMap[uuidB] = inverseIndex + } +} diff --git a/packages/utils/src/Domain/index.ts b/packages/utils/src/Domain/index.ts new file mode 100644 index 000000000..7db6f5e1e --- /dev/null +++ b/packages/utils/src/Domain/index.ts @@ -0,0 +1,5 @@ +export * from './Utils/Utils' +export * from './Uuid/UuidGenerator' +export * from './Uuid/UuidMap' +export * from './Uuid/Utils' +export * from './Deferred/Deferred' diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts new file mode 100644 index 000000000..920deacdb --- /dev/null +++ b/packages/utils/src/index.ts @@ -0,0 +1 @@ +export * from './Domain' diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json new file mode 100644 index 000000000..f3dac14ef --- /dev/null +++ b/packages/utils/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../node_modules/@standardnotes/config/src/tsconfig.json", + "compilerOptions": { + "skipLibCheck": true, + "rootDir": "./src", + "outDir": "./dist", + }, + "include": [ + "src/**/*" + ], + "references": [], + "exclude": ["**/*.spec.ts", "dist", "node_modules"] +} diff --git a/yarn.lock b/yarn.lock index ec3387913..8c1b58ad5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6130,7 +6130,7 @@ __metadata: "@reduxjs/toolkit": 1.8.0 "@standardnotes/editor-kit": 2.2.5 "@standardnotes/stylekit": 5.23.0 - "@standardnotes/utils": 1.6.2 + "@standardnotes/utils": "workspace:*" "@testing-library/dom": 8.11.3 "@testing-library/jest-dom": 5.16.2 "@testing-library/react": 12.1.4 @@ -6326,7 +6326,7 @@ __metadata: languageName: unknown linkType: soft -"@standardnotes/common@npm:^1.19.6, @standardnotes/common@npm:^1.23.1": +"@standardnotes/common@npm:^1.23.1": version: 1.23.1 resolution: "@standardnotes/common@npm:1.23.1" checksum: f498f4c469f7b9cdfc08b7648077832a2a214d844f012d02d73c2c418fab221af3302570cc6b1957b5ed481b55237f95bc24fc5c231162bfcdf8730333d38405 @@ -6519,7 +6519,7 @@ __metadata: "@standardnotes/responses": ^1.6.39 "@standardnotes/services": "workspace:*" "@standardnotes/sncrypto-common": ^1.9.0 - "@standardnotes/utils": ^1.6.12 + "@standardnotes/utils": "workspace:*" "@types/jest": ^27.4.1 "@types/node": ^18.0.0 "@typescript-eslint/eslint-plugin": "*" @@ -6573,7 +6573,7 @@ __metadata: dependencies: "@standardnotes/common": ^1.23.1 "@standardnotes/services": "workspace:*" - "@standardnotes/utils": ^1.6.12 + "@standardnotes/utils": "workspace:*" "@types/jest": ^27.4.1 "@types/wicg-file-system-access": ^2020.9.5 "@typescript-eslint/eslint-plugin": ^5.30.0 @@ -6596,7 +6596,7 @@ __metadata: "@standardnotes/responses": ^1.6.39 "@standardnotes/services": "workspace:*" "@standardnotes/sncrypto-common": ^1.9.0 - "@standardnotes/utils": ^1.6.12 + "@standardnotes/utils": "workspace:*" "@types/jest": ^27.4.1 "@typescript-eslint/eslint-plugin": ^5.30.0 eslint-plugin-prettier: ^4.2.1 @@ -6982,7 +6982,7 @@ __metadata: "@standardnotes/common": ^1.23.1 "@standardnotes/features": "workspace:*" "@standardnotes/responses": ^1.6.39 - "@standardnotes/utils": ^1.6.12 + "@standardnotes/utils": "workspace:*" "@types/jest": ^27.4.1 "@types/lodash": ^4.14.182 "@typescript-eslint/eslint-plugin": ^5.30.0 @@ -7088,7 +7088,7 @@ __metadata: "@standardnotes/common": ^1.23.1 "@standardnotes/models": "workspace:*" "@standardnotes/responses": ^1.6.39 - "@standardnotes/utils": ^1.6.12 + "@standardnotes/utils": "workspace:*" "@types/jest": ^27.4.1 "@typescript-eslint/eslint-plugin": ^5.30.0 "@typescript-eslint/parser": ^5.12.1 @@ -7298,27 +7298,26 @@ __metadata: languageName: unknown linkType: soft -"@standardnotes/utils@npm:1.6.2": - version: 1.6.2 - resolution: "@standardnotes/utils@npm:1.6.2" - dependencies: - "@standardnotes/common": ^1.19.6 - dompurify: ^2.3.6 - lodash: ^4.17.21 - checksum: e7d90879921d3991527dc1fd2f87cf16706008d06d20b690e41a7a3ee1ab37289fa4ab354a58b3d2ae58609bb8b56f1afe90544b7911be5b04ca4117aa4b982d - languageName: node - linkType: hard - -"@standardnotes/utils@npm:^1.6.12": - version: 1.6.12 - resolution: "@standardnotes/utils@npm:1.6.12" +"@standardnotes/utils@^1.6.12, @standardnotes/utils@workspace:*, @standardnotes/utils@workspace:packages/utils": + version: 0.0.0-use.local + resolution: "@standardnotes/utils@workspace:packages/utils" dependencies: "@standardnotes/common": ^1.23.1 + "@types/dompurify": ^2.3.3 + "@types/jest": ^27.4.1 + "@types/jsdom": ^16.2.14 + "@types/lodash": ^4.14.179 + "@types/node": ^18.0.0 + "@typescript-eslint/eslint-plugin": ^5.30.0 dompurify: ^2.3.6 + eslint-plugin-prettier: ^4.2.1 + jest: ^27.5.1 + jsdom: ^20.0.0 lodash: ^4.17.21 - checksum: e177b1fa518ca2c0bcc5602d2a83d37946461bc4c3d8daa329d7290d61dec9bcfd5b9c1593582672598b0696d994f7ce42b9ca16f82a485fd45cb221411bd4e8 - languageName: node - linkType: hard + reflect-metadata: ^0.1.13 + ts-jest: ^27.1.3 + languageName: unknown + linkType: soft "@standardnotes/web-server@workspace:packages/web-server": version: 0.0.0-use.local @@ -7994,6 +7993,15 @@ __metadata: languageName: node linkType: hard +"@types/dompurify@npm:^2.3.3": + version: 2.3.3 + resolution: "@types/dompurify@npm:2.3.3" + dependencies: + "@types/trusted-types": "*" + checksum: 427e2dc60d94d13d7860a293b926b376727cb2f545a3334a3f2e7de695a2bb23058dd15108e49e0651378229b443ee8ae0028034b6f2df9a9008c04fb7ad6f8f + languageName: node + linkType: hard + "@types/eslint-scope@npm:^3.7.3": version: 3.7.4 resolution: "@types/eslint-scope@npm:3.7.4" @@ -8239,6 +8247,17 @@ __metadata: languageName: node linkType: hard +"@types/jsdom@npm:^16.2.14": + version: 16.2.14 + resolution: "@types/jsdom@npm:16.2.14" + dependencies: + "@types/node": "*" + "@types/parse5": "*" + "@types/tough-cookie": "*" + checksum: 12bb926fa74ea07c0ba0bfd5bf185ac0fd771b28666a5e8784b9af4bb96bb0c51fc5f494eff7da1d3cd804e4757f640a23c344c1cd5d188f95ab0ab51770d88b + languageName: node + linkType: hard + "@types/json-buffer@npm:~3.0.0": version: 3.0.0 resolution: "@types/json-buffer@npm:3.0.0" @@ -8411,6 +8430,13 @@ __metadata: languageName: node linkType: hard +"@types/parse5@npm:*": + version: 6.0.3 + resolution: "@types/parse5@npm:6.0.3" + checksum: ddb59ee4144af5dfcc508a8dcf32f37879d11e12559561e65788756b95b33e6f03ea027d88e1f5408f9b7bfb656bf630ace31a2169edf44151daaf8dd58df1b7 + languageName: node + linkType: hard + "@types/parse5@npm:^5.0.0": version: 5.0.3 resolution: "@types/parse5@npm:5.0.3" @@ -8933,7 +8959,14 @@ __metadata: languageName: node linkType: hard -"@types/trusted-types@npm:^2.0.2": +"@types/tough-cookie@npm:*": + version: 4.0.2 + resolution: "@types/tough-cookie@npm:4.0.2" + checksum: e055556ffdaa39ad85ede0af192c93f93f986f4bd9e9426efdc2948e3e2632db3a4a584d4937dbf6d7620527419bc99e6182d3daf2b08685e710f2eda5291905 + languageName: node + linkType: hard + +"@types/trusted-types@npm:*, @types/trusted-types@npm:^2.0.2": version: 2.0.2 resolution: "@types/trusted-types@npm:2.0.2" checksum: 3371eef5f1c50e1c3c07a127c1207b262ba65b83dd167a1c460fc1b135a3fb0c97b9f508efebd383f239cc5dd5b7169093686a692a501fde9c3f7208657d9b0d @@ -9616,7 +9649,7 @@ __metadata: languageName: node linkType: hard -"abab@npm:^2.0.3, abab@npm:^2.0.5": +"abab@npm:^2.0.3, abab@npm:^2.0.5, abab@npm:^2.0.6": version: 2.0.6 resolution: "abab@npm:2.0.6" checksum: 6ffc1af4ff315066c62600123990d87551ceb0aafa01e6539da77b0f5987ac7019466780bf480f1787576d4385e3690c81ccc37cfda12819bf510b8ab47e5a3e @@ -14827,6 +14860,13 @@ __metadata: languageName: node linkType: hard +"cssom@npm:^0.5.0": + version: 0.5.0 + resolution: "cssom@npm:0.5.0" + checksum: 823471aa30091c59e0a305927c30e7768939b6af70405808f8d2ce1ca778cddcb24722717392438329d1691f9a87cb0183b64b8d779b56a961546d54854fde01 + languageName: node + linkType: hard + "cssom@npm:~0.3.6": version: 0.3.8 resolution: "cssom@npm:0.3.8" @@ -15578,6 +15618,17 @@ __metadata: languageName: node linkType: hard +"data-urls@npm:^3.0.2": + version: 3.0.2 + resolution: "data-urls@npm:3.0.2" + dependencies: + abab: ^2.0.6 + whatwg-mimetype: ^3.0.0 + whatwg-url: ^11.0.0 + checksum: 033fc3dd0fba6d24bc9a024ddcf9923691dd24f90a3d26f6545d6a2f71ec6956f93462f2cdf2183cc46f10dc01ed3bcb36731a8208456eb1a08147e571fe2a76 + languageName: node + linkType: hard + "date-time@npm:^3.1.0": version: 3.1.0 resolution: "date-time@npm:3.1.0" @@ -15655,7 +15706,7 @@ __metadata: languageName: node linkType: hard -"decimal.js@npm:^10.2.1": +"decimal.js@npm:^10.2.1, decimal.js@npm:^10.3.1": version: 10.3.1 resolution: "decimal.js@npm:10.3.1" checksum: 0351ac9f05fe050f23227aa6a4573bee2d58fa7378fcf28d969a8c789525032effb488a90320fd3fe86a66e17b4bc507d811b15eada5b7f0e7ec5d2af4c24a59 @@ -16412,6 +16463,15 @@ __metadata: languageName: node linkType: hard +"domexception@npm:^4.0.0": + version: 4.0.0 + resolution: "domexception@npm:4.0.0" + dependencies: + webidl-conversions: ^7.0.0 + checksum: ddbc1268edf33a8ba02ccc596735ede80375ee0cf124b30d2f05df5b464ba78ef4f49889b6391df4a04954e63d42d5631c7fcf8b1c4f12bc531252977a5f13d5 + languageName: node + linkType: hard + "domhandler@npm:^2.3.0": version: 2.4.2 resolution: "domhandler@npm:2.4.2" @@ -16942,6 +17002,13 @@ __metadata: languageName: node linkType: hard +"entities@npm:^4.3.0": + version: 4.3.1 + resolution: "entities@npm:4.3.1" + checksum: e8f6d2bac238494b2355e90551893882d2675142be7e7bdfcb15248ed0652a630678ba0e3a8dc750693e736cb6011f504c27dabeb4cd3330560092e88b105090 + languageName: node + linkType: hard + "entities@npm:~2.0.0": version: 2.0.3 resolution: "entities@npm:2.0.3" @@ -20599,6 +20666,15 @@ __metadata: languageName: node linkType: hard +"html-encoding-sniffer@npm:^3.0.0": + version: 3.0.0 + resolution: "html-encoding-sniffer@npm:3.0.0" + dependencies: + whatwg-encoding: ^2.0.0 + checksum: 8d806aa00487e279e5ccb573366a951a9f68f65c90298eac9c3a2b440a7ffe46615aff2995a2f61c6746c639234e6179a97e18ca5ccbbf93d3725ef2099a4502 + languageName: node + linkType: hard + "html-entities@npm:^1.3.1": version: 1.4.0 resolution: "html-entities@npm:1.4.0" @@ -20893,7 +20969,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.0": +"https-proxy-agent@npm:^5.0.0, https-proxy-agent@npm:^5.0.1": version: 5.0.1 resolution: "https-proxy-agent@npm:5.0.1" dependencies: @@ -20985,7 +21061,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:0.6, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3": +"iconv-lite@npm:0.6, iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" dependencies: @@ -23637,6 +23713,46 @@ __metadata: languageName: node linkType: hard +"jsdom@npm:^20.0.0": + version: 20.0.0 + resolution: "jsdom@npm:20.0.0" + dependencies: + abab: ^2.0.6 + acorn: ^8.7.1 + acorn-globals: ^6.0.0 + cssom: ^0.5.0 + cssstyle: ^2.3.0 + data-urls: ^3.0.2 + decimal.js: ^10.3.1 + domexception: ^4.0.0 + escodegen: ^2.0.0 + form-data: ^4.0.0 + html-encoding-sniffer: ^3.0.0 + http-proxy-agent: ^5.0.0 + https-proxy-agent: ^5.0.1 + is-potential-custom-element-name: ^1.0.1 + nwsapi: ^2.2.0 + parse5: ^7.0.0 + saxes: ^6.0.0 + symbol-tree: ^3.2.4 + tough-cookie: ^4.0.0 + w3c-hr-time: ^1.0.2 + w3c-xmlserializer: ^3.0.0 + webidl-conversions: ^7.0.0 + whatwg-encoding: ^2.0.0 + whatwg-mimetype: ^3.0.0 + whatwg-url: ^11.0.0 + ws: ^8.8.0 + xml-name-validator: ^4.0.0 + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + checksum: f69b40679d8cfaee2353615445aaff08b823c53dc7778ede6592d02ed12b3e9fb4e8db2b6d033551b67e08424a3adb2b79d231caa7dcda2d16019c20c705c11f + languageName: node + linkType: hard + "jsesc@npm:^1.3.0": version: 1.3.0 resolution: "jsesc@npm:1.3.0" @@ -28618,6 +28734,15 @@ __metadata: languageName: node linkType: hard +"parse5@npm:^7.0.0": + version: 7.0.0 + resolution: "parse5@npm:7.0.0" + dependencies: + entities: ^4.3.0 + checksum: 7da5d61cc18eb36ffa71fc861e65cbfd1f23d96483a6631254e627be667dbc9c93ac0b0e6cb17a13a2e4033dab19bfb2f76f38e5936cfb57240ed49036a83fcc + languageName: node + linkType: hard + "parseurl@npm:~1.3.2, parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" @@ -34002,6 +34127,15 @@ __metadata: languageName: node linkType: hard +"saxes@npm:^6.0.0": + version: 6.0.0 + resolution: "saxes@npm:6.0.0" + dependencies: + xmlchars: ^2.2.0 + checksum: d3fa3e2aaf6c65ed52ee993aff1891fc47d5e47d515164b5449cbf5da2cbdc396137e55590472e64c5c436c14ae64a8a03c29b9e7389fc6f14035cf4e982ef3b + languageName: node + linkType: hard + "scheduler@npm:^0.17.0": version: 0.17.0 resolution: "scheduler@npm:0.17.0" @@ -38319,6 +38453,15 @@ __metadata: languageName: node linkType: hard +"w3c-xmlserializer@npm:^3.0.0": + version: 3.0.0 + resolution: "w3c-xmlserializer@npm:3.0.0" + dependencies: + xml-name-validator: ^4.0.0 + checksum: 0af8589942eeb11c9fe29eb31a1a09f3d5dd136aea53a9848dfbabff79ac0dd26fe13eb54d330d5555fe27bb50b28dca0715e09f9cc2bfa7670ccc8b7f919ca2 + languageName: node + linkType: hard + "wait-on@npm:^5.2.1": version: 5.3.0 resolution: "wait-on@npm:5.3.0" @@ -38900,6 +39043,15 @@ __metadata: languageName: node linkType: hard +"whatwg-encoding@npm:^2.0.0": + version: 2.0.0 + resolution: "whatwg-encoding@npm:2.0.0" + dependencies: + iconv-lite: 0.6.3 + checksum: 7087810c410aa9b689cbd6af8773341a53cdc1f3aae2a882c163bd5522ec8ca4cdfc269aef417a5792f411807d5d77d50df4c24e3abb00bb60192858a40cc675 + languageName: node + linkType: hard + "whatwg-fetch@npm:^3.0.0, whatwg-fetch@npm:^3.6.2": version: 3.6.2 resolution: "whatwg-fetch@npm:3.6.2" @@ -38914,6 +39066,13 @@ __metadata: languageName: node linkType: hard +"whatwg-mimetype@npm:^3.0.0": + version: 3.0.0 + resolution: "whatwg-mimetype@npm:3.0.0" + checksum: ce08bbb36b6aaf64f3a84da89707e3e6a31e5ab1c1a2379fd68df79ba712a4ab090904f0b50e6693b0dafc8e6343a6157e40bf18fdffd26e513cf95ee2a59824 + languageName: node + linkType: hard + "whatwg-url-without-unicode@npm:8.0.0-3": version: 8.0.0-3 resolution: "whatwg-url-without-unicode@npm:8.0.0-3" @@ -39458,7 +39617,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.4.2": +"ws@npm:^8.4.2, ws@npm:^8.8.0": version: 8.8.0 resolution: "ws@npm:8.8.0" peerDependencies: @@ -39522,6 +39681,13 @@ __metadata: languageName: node linkType: hard +"xml-name-validator@npm:^4.0.0": + version: 4.0.0 + resolution: "xml-name-validator@npm:4.0.0" + checksum: af100b79c29804f05fa35aa3683e29a321db9b9685d5e5febda3fa1e40f13f85abc40f45a6b2bf7bee33f68a1dc5e8eaef4cec100a304a9db565e6061d4cb5ad + languageName: node + linkType: hard + "xmlbuilder@npm:>=11.0.1": version: 15.1.1 resolution: "xmlbuilder@npm:15.1.1"