feat: add utils package

This commit is contained in:
Karol Sójko
2022-07-06 11:33:25 +02:00
parent d273770831
commit aef4ceb7f8
41 changed files with 1332 additions and 36 deletions

1
.gitignore vendored
View File

@@ -21,6 +21,7 @@ packages/encryption/dist
packages/files/dist
packages/models/dist
packages/services/dist
packages/utils/dist
**/.pnp.*
**/.yarn/*

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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",

View File

@@ -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"
}
}

View File

@@ -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"
}

View File

@@ -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"
}
}

View File

@@ -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"
}

View File

@@ -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": {

View File

@@ -0,0 +1,2 @@
node_modules
dist

6
packages/utils/.eslintrc Normal file
View File

@@ -0,0 +1,6 @@
{
"extends": "../../.eslintrc",
"parserOptions": {
"project": "./linter.tsconfig.json"
}
}

156
packages/utils/CHANGELOG.md Normal file
View File

@@ -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))

View File

@@ -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
}
}
};

View File

@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["dist"]
}

View File

@@ -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"
}
}

View File

@@ -0,0 +1,15 @@
export const Deferred = <T>() => {
let resolve!: (value: T | PromiseLike<T>) => void
let reject!: () => void
const promise = new Promise<T>((res, rej) => {
resolve = res
reject = rej
})
return {
resolve,
reject,
promise,
}
}

View File

@@ -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 = '<svg><animate onbegin=alert(1) attributeName=x dur=1s>'
const cleaned = DOMPurify.sanitize(dirty)
expect(cleaned).toEqual('<svg></svg>')
})
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)
})
})

View File

@@ -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<T>(dict: Record<any, T>): 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<T, K extends keyof T>(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<T>(array: T[], predicate: Partial<T>): T | undefined {
return find(array, predicate) as T
}
export function sureSearchArray<T>(array: T[], predicate: Partial<T>): 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<T>(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<T>(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<T>(array: T[]): T[] {
return uniq(array)
}
/**
* Returns a new array containing only unique values
* @returns Array containing unique values
*/
export function uniqueArrayByKey<T>(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<T>(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<T, K extends T>(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<T>(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<T>(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<T>(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<T>(
array: T[],
predicate: Partial<Record<keyof T, any>> | ((object: T) => boolean),
): void {
remove(array, predicate)
}
/**
* Returns a new array by removing all elements in subtract from array
*/
export function arrayByDifference<T>(array: T[], subtract: T[]): T[] {
return array.filter((x) => !subtract.includes(x)).concat(subtract.filter((x) => !array.includes(x)))
}
export function compareValues<T>(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<T>(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<T>(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 = <T>(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<T>(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<T>(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 = <T>(array: T[]): T[] => {
return array.slice(0, -1)
}
/**
* Deletes keys of the input object.
*/
export function omitInPlace<T>(object: T, keys: Array<keyof T>) {
if (!object) {
return
}
for (const key of keys) {
delete object[key]
}
}
/**
* Creates a new object by omitting `keys` from `object`
*/
export function omitByCopy<T>(object: T, keys: Array<keyof T>) {
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<T>(object: T, keys: Array<keyof T>) {
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<void> {
if (warn) {
console.warn(`Sleeping for ${milliseconds}ms`)
}
return new Promise<void>((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<T extends AnyRecord>(
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<T>(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
}

View File

@@ -0,0 +1,5 @@
export function Uuids(items: { uuid: string }[]): string[] {
return items.map((item) => {
return item.uuid
})
}

View File

@@ -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()
}
}

View File

@@ -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<Record<Uuid, Uuid[]>> = {}
/** uuid to uuids that have a relationship with us */
private inverseMap: Partial<Record<Uuid, Uuid[]>> = {}
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
}
}

View File

@@ -0,0 +1,5 @@
export * from './Utils/Utils'
export * from './Uuid/UuidGenerator'
export * from './Uuid/UuidMap'
export * from './Uuid/Utils'
export * from './Deferred/Deferred'

View File

@@ -0,0 +1 @@
export * from './Domain'

View File

@@ -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"]
}

226
yarn.lock
View File

@@ -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"