diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index cd6a19a4..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "env": { - "browser": true, - "es2021": true, - "jest": true, - "node": true - }, - "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 12, - "sourceType": "module" - }, - "plugins": ["@typescript-eslint"], - "rules": { - "@typescript-eslint/no-explicit-any": 0, - "@typescript-eslint/prefer-const": 0, - "@typescript-eslint/no-this-alias": 0, - "@typescript-eslint/ban-ts-comment": 0, - "prefer-rest-params": 0, - "@typescript-eslint/no-unused-vars": 0, - "@typescript-eslint/no-empty-function": 0 - } -} diff --git a/jest.config.js b/jest.config.js index e4b11b5d..9ee3a686 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,6 +11,9 @@ module.exports = { "^.+\\.(ts|tsx)$": "ts-jest", }, coveragePathIgnorePatterns: ["__test__/"], - "collectCoverage": true, - "coverageReporters": ["html"], + collectCoverage: true, + coverageReporters: ["html"], + moduleNameMapper: { + "^lodash-es$": "lodash", + }, }; diff --git a/package-lock.json b/package-lock.json index 099c7e5b..b9d73ffc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,20 +9,18 @@ "version": "1.4.0", "license": "MIT", "dependencies": { - "just-camel-case": "^4.0.2", - "lodash": "^4.17.21", - "morphdom": "^2.6.1", + "lodash-es": "^4.17.21", "mustache": "^4.2.0", "uuid": "^8.3.2" }, "devDependencies": { "@babel/core": "^7.2.2", "@babel/preset-env": "^7.3.1", - "@contentstack/advanced-post-message": "github:contentstack/adv-post-message#EB-676-config-support", + "@contentstack/advanced-post-message": "github:contentstack/adv-post-message#main", "@testing-library/jest-dom": "^5.14.1", "@types/jest": "^27.0.1", "@types/jsdom": "^21.1.6", - "@types/lodash": "^4.14.195", + "@types/lodash-es": "^4.17.12", "@types/mustache": "^4.2.2", "@types/uuid": "^8.3.1", "@typescript-eslint/eslint-plugin": "^4.31.1", @@ -38,6 +36,7 @@ "mini-css-extract-plugin": "^2.3.0", "prettier": "^2.8.4", "prettier-eslint": "^15.0.1", + "rimraf": "^5.0.5", "style-loader": "^2.0.0", "ts-jest": "^27.0.5", "ts-loader": "^9.4.2", @@ -1867,7 +1866,7 @@ }, "node_modules/@contentstack/advanced-post-message": { "version": "0.0.1", - "resolved": "git+ssh://git@github.com/contentstack/adv-post-message.git#515b0da155f5bb81961a61a01d29dd681350c511", + "resolved": "git+ssh://git@github.com/contentstack/adv-post-message.git#70a7241359a358408d6741a093de4594d212a4b5", "dev": true, "license": "ISC", "dependencies": { @@ -2016,6 +2015,102 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2233,6 +2328,21 @@ "node": ">=8" } }, + "node_modules/@jest/core/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@jest/core/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2691,6 +2801,12 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2726,6 +2842,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", @@ -3045,6 +3171,15 @@ "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", "dev": true }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -3063,6 +3198,15 @@ "integrity": "sha512-ZHty/hKoOLZvSz6BtP1g7tc7nUeJhoCf3flLjh8ZEv1vFKBWHXcnMbJMyN/pftSljNyy0kNW/UqI3DccnBnZ8w==", "dev": true }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/prettier": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", @@ -3143,9 +3287,9 @@ "dev": true }, "node_modules/@types/ws": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", - "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "dependencies": { "@types/node": "*" @@ -3678,19 +3822,6 @@ "node": ">= 14" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3865,12 +3996,6 @@ "node": ">=6.0" } }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -3889,15 +4014,6 @@ "node": ">=8" } }, - "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4220,18 +4336,14 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "node_modules/bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "node_modules/bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "dependencies": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, "node_modules/brace-expansion": { @@ -4321,12 +4433,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, "node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -4336,19 +4442,6 @@ "node": ">= 0.8" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4467,15 +4560,6 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -4623,9 +4707,9 @@ "dev": true }, "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, "engines": { "node": ">=0.8" @@ -4922,23 +5006,6 @@ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, - "node_modules/deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "dependencies": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4975,40 +5042,6 @@ "node": ">=8" } }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", - "dev": true, - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -5075,29 +5108,16 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "dev": true }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, "node_modules/dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "dev": true, - "dependencies": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "dependencies": { - "buffer-indexof": "^1.0.0" + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" } }, "node_modules/doctrine": { @@ -5140,6 +5160,12 @@ "node": ">=8" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -5982,6 +6008,21 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/flatted": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", @@ -5989,9 +6030,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "dev": true, "funding": [ { @@ -6008,6 +6049,34 @@ } } }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -6096,20 +6165,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -6258,33 +6313,6 @@ "node": ">=4" } }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -6554,12 +6582,6 @@ "node": ">= 0.10" } }, - "node_modules/ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, "node_modules/ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -6569,22 +6591,6 @@ "node": ">= 10" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -6615,31 +6621,16 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" + "bin": { + "is-docker": "cli.js" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6693,15 +6684,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -6741,22 +6723,6 @@ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -6895,6 +6861,24 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", @@ -8911,27 +8895,6 @@ } } }, - "node_modules/jsdom/node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -8980,11 +8943,6 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, - "node_modules/just-camel-case": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/just-camel-case/-/just-camel-case-4.0.2.tgz", - "integrity": "sha512-df6QI/EIq+6uHe/wtaa9Qq7/pp4wr4pJC/r1+7XhVL6m5j03G6h9u9/rIZr8rDASX7CxwDPQnZjffCo2e6PRLw==" - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -9003,6 +8961,16 @@ "node": ">=6" } }, + "node_modules/launch-editor": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -9069,7 +9037,13 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", @@ -9415,29 +9389,15 @@ "node": "*" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp/node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/morphdom": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.6.1.tgz", - "integrity": "sha512-Y8YRbAEP3eKykroIBWrjcfMw7mmwJfjhqdpSvoqinu8Y702nAwikpXcNFDiIkyvfCLxLM9Wu95RZqo4a9jFBaA==" - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -9445,24 +9405,18 @@ "dev": true }, "node_modules/multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, "dependencies": { - "dns-packet": "^1.3.1", + "dns-packet": "^5.2.2", "thunky": "^1.0.2" }, "bin": { "multicast-dns": "cli.js" } }, - "node_modules/multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, "node_modules/mustache": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", @@ -9553,9 +9507,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz", - "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true, "engines": { "node": ">= 6.13.0" @@ -9600,31 +9554,6 @@ "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", "dev": true }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -9737,21 +9666,6 @@ "node": ">=8" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-retry": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", @@ -9858,6 +9772,31 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -9912,29 +9851,6 @@ "node": ">=8" } }, - "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/postcss": { "version": "8.4.32", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", @@ -10823,22 +10739,6 @@ "@babel/runtime": "^7.8.4" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", - "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -10988,15 +10888,64 @@ } }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "glob": "^10.3.7" }, "bin": { - "rimraf": "bin.js" + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -11080,12 +11029,13 @@ "dev": true }, "node_modules/selfsigned": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", - "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, "dependencies": { - "node-forge": "^1.2.0" + "@types/node-forge": "^1.3.0", + "node-forge": "^1" }, "engines": { "node": ">=10" @@ -11268,6 +11218,15 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/shiki": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.1.tgz", @@ -11517,6 +11476,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -11529,6 +11503,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -12698,41 +12685,41 @@ } }, "node_modules/webpack-dev-server": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz", - "integrity": "sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", + "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", "dev": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", "@types/express": "^4.17.13", "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.2.2", + "@types/ws": "^8.5.5", "ansi-html-community": "^0.0.8", - "bonjour": "^3.5.0", + "bonjour-service": "^1.0.11", "chokidar": "^3.5.3", "colorette": "^2.0.10", "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", + "connect-history-api-fallback": "^2.0.0", "default-gateway": "^6.0.3", - "del": "^6.0.0", - "express": "^4.17.1", + "express": "^4.17.3", "graceful-fs": "^4.2.6", "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.0", + "http-proxy-middleware": "^2.0.3", "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", "open": "^8.0.9", "p-retry": "^4.5.0", - "portfinder": "^1.0.28", + "rimraf": "^3.0.2", "schema-utils": "^4.0.0", - "selfsigned": "^2.0.0", + "selfsigned": "^2.1.1", "serve-index": "^1.9.1", - "sockjs": "^0.3.21", + "sockjs": "^0.3.24", "spdy": "^4.0.2", - "strip-ansi": "^7.0.0", "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" + "ws": "^8.13.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" @@ -12740,10 +12727,17 @@ "engines": { "node": ">= 12.13.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, "peerDependencies": { "webpack": "^4.37.0 || ^5.0.0" }, "peerDependenciesMeta": { + "webpack": { + "optional": true + }, "webpack-cli": { "optional": true } @@ -12777,18 +12771,6 @@ "ajv": "^8.8.2" } }, - "node_modules/webpack-dev-server/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, "node_modules/webpack-dev-server/node_modules/colorette": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", @@ -12801,6 +12783,21 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/webpack-dev-server/node_modules/schema-utils": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", @@ -12820,21 +12817,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack-dev-server/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/webpack-merge": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", @@ -12982,6 +12964,57 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -13034,16 +13067,16 @@ } }, "node_modules/ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -14409,9 +14442,9 @@ "dev": true }, "@contentstack/advanced-post-message": { - "version": "git+ssh://git@github.com/contentstack/adv-post-message.git#515b0da155f5bb81961a61a01d29dd681350c511", + "version": "git+ssh://git@github.com/contentstack/adv-post-message.git#70a7241359a358408d6741a093de4594d212a4b5", "dev": true, - "from": "@contentstack/advanced-post-message@github:contentstack/adv-post-message#EB-676-config-support", + "from": "@contentstack/advanced-post-message@github:contentstack/adv-post-message#main", "requires": { "uuid": "^9.0.0", "zalgo-promise": "^1.0.48" @@ -14517,6 +14550,71 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -14677,6 +14775,15 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -15035,6 +15142,12 @@ } } }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -15061,6 +15174,13 @@ "fastq": "^1.6.0" } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", @@ -15356,6 +15476,15 @@ "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", "dev": true }, + "@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -15374,6 +15503,15 @@ "integrity": "sha512-ZHty/hKoOLZvSz6BtP1g7tc7nUeJhoCf3flLjh8ZEv1vFKBWHXcnMbJMyN/pftSljNyy0kNW/UqI3DccnBnZ8w==", "dev": true }, + "@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/prettier": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", @@ -15454,9 +15592,9 @@ "dev": true }, "@types/ws": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", - "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "requires": { "@types/node": "*" @@ -15859,16 +15997,6 @@ "debug": "^4.3.4" } }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -15996,12 +16124,6 @@ "@babel/runtime-corejs3": "^7.10.2" } }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -16014,15 +16136,6 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -16273,18 +16386,14 @@ } } }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, "brace-expansion": { @@ -16348,28 +16457,12 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -16439,12 +16532,6 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -16575,9 +16662,9 @@ "dev": true }, "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true }, "content-disposition": { @@ -16790,20 +16877,6 @@ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -16831,31 +16904,6 @@ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", - "dev": true, - "requires": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -16907,29 +16955,13 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "dev": true }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, "dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "requires": { - "buffer-indexof": "^1.0.0" + "@leichtgewicht/ip-codec": "^2.0.1" } }, "doctrine": { @@ -16964,6 +16996,12 @@ } } }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -17602,6 +17640,17 @@ "requires": { "flatted": "^3.1.0", "rimraf": "^3.0.2" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "flatted": { @@ -17611,11 +17660,29 @@ "dev": true }, "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "dev": true }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, "form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -17682,17 +17749,6 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -17804,21 +17860,6 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, "hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -18017,28 +18058,12 @@ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, "ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", "dev": true }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -18063,15 +18088,6 @@ "has": "^1.0.3" } }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, "is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -18111,12 +18127,6 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, "is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -18144,16 +18154,6 @@ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -18261,6 +18261,16 @@ "istanbul-lib-report": "^3.0.0" } }, + "jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", @@ -19770,15 +19780,6 @@ "whatwg-url": "^14.0.0", "ws": "^8.16.0", "xml-name-validator": "^5.0.0" - }, - "dependencies": { - "ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true, - "requires": {} - } } }, "jsesc": { @@ -19817,11 +19818,6 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, - "just-camel-case": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/just-camel-case/-/just-camel-case-4.0.2.tgz", - "integrity": "sha512-df6QI/EIq+6uHe/wtaa9Qq7/pp4wr4pJC/r1+7XhVL6m5j03G6h9u9/rIZr8rDASX7CxwDPQnZjffCo2e6PRLw==" - }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -19834,6 +19830,16 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "launch-editor": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", + "dev": true, + "requires": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -19885,7 +19891,13 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, "lodash.clonedeep": { "version": "4.5.0", @@ -20145,27 +20157,11 @@ "brace-expansion": "^1.1.7" } }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - }, - "dependencies": { - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - } - } - }, - "morphdom": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.6.1.tgz", - "integrity": "sha512-Y8YRbAEP3eKykroIBWrjcfMw7mmwJfjhqdpSvoqinu8Y702nAwikpXcNFDiIkyvfCLxLM9Wu95RZqo4a9jFBaA==" + "minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true }, "ms": { "version": "2.1.2", @@ -20174,21 +20170,15 @@ "dev": true }, "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, "requires": { - "dns-packet": "^1.3.1", + "dns-packet": "^5.2.2", "thunky": "^1.0.2" } }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, "mustache": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", @@ -20252,9 +20242,9 @@ } }, "node-forge": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz", - "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true }, "node-int64": { @@ -20290,22 +20280,6 @@ "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", "dev": true }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -20388,15 +20362,6 @@ "p-limit": "^2.2.0" } }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, "p-retry": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", @@ -20473,6 +20438,24 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "requires": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true + } + } + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -20512,28 +20495,6 @@ "find-up": "^4.0.0" } }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, "postcss": { "version": "8.4.32", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", @@ -21156,16 +21117,6 @@ "@babel/runtime": "^7.8.4" } }, - "regexp.prototype.flags": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", - "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -21277,12 +21228,45 @@ "dev": true }, "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dev": true, "requires": { - "glob": "^7.1.3" + "glob": "^10.3.7" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "rrweb-cssom": { @@ -21339,12 +21323,13 @@ "dev": true }, "selfsigned": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", - "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, "requires": { - "node-forge": "^1.2.0" + "@types/node-forge": "^1.3.0", + "node-forge": "^1" } }, "semver": { @@ -21506,6 +21491,12 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true + }, "shiki": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.1.tgz", @@ -21715,6 +21706,17 @@ "strip-ansi": "^6.0.0" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -21724,6 +21726,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -22548,41 +22559,41 @@ } }, "webpack-dev-server": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz", - "integrity": "sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", + "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", "dev": true, "requires": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", "@types/express": "^4.17.13", "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.2.2", + "@types/ws": "^8.5.5", "ansi-html-community": "^0.0.8", - "bonjour": "^3.5.0", + "bonjour-service": "^1.0.11", "chokidar": "^3.5.3", "colorette": "^2.0.10", "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", + "connect-history-api-fallback": "^2.0.0", "default-gateway": "^6.0.3", - "del": "^6.0.0", - "express": "^4.17.1", + "express": "^4.17.3", "graceful-fs": "^4.2.6", "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.0", + "http-proxy-middleware": "^2.0.3", "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", "open": "^8.0.9", "p-retry": "^4.5.0", - "portfinder": "^1.0.28", + "rimraf": "^3.0.2", "schema-utils": "^4.0.0", - "selfsigned": "^2.0.0", + "selfsigned": "^2.1.1", "serve-index": "^1.9.1", - "sockjs": "^0.3.21", + "sockjs": "^0.3.24", "spdy": "^4.0.2", - "strip-ansi": "^7.0.0", "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" + "ws": "^8.13.0" }, "dependencies": { "ajv": { @@ -22606,12 +22617,6 @@ "fast-deep-equal": "^3.1.3" } }, - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, "colorette": { "version": "2.0.16", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", @@ -22624,6 +22629,15 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "schema-utils": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", @@ -22635,15 +22649,6 @@ "ajv-formats": "^2.1.1", "ajv-keywords": "^5.0.0" } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } } } }, @@ -22768,6 +22773,43 @@ } } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -22787,9 +22829,9 @@ } }, "ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "requires": {} }, diff --git a/package.json b/package.json index da69854a..8d0b808c 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "dist" ], "scripts": { - "build": "webpack --config webpack.prod.js", + "build": "rimraf dist/ && webpack --config webpack.prod.js", "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest src --coverage --watchAll --coverageDirectory=\"coverage\"", @@ -29,11 +29,11 @@ "devDependencies": { "@babel/core": "^7.2.2", "@babel/preset-env": "^7.3.1", - "@contentstack/advanced-post-message": "github:contentstack/adv-post-message#EB-676-config-support", + "@contentstack/advanced-post-message": "github:contentstack/adv-post-message#main", "@testing-library/jest-dom": "^5.14.1", "@types/jest": "^27.0.1", "@types/jsdom": "^21.1.6", - "@types/lodash": "^4.14.195", + "@types/lodash-es": "^4.17.12", "@types/mustache": "^4.2.2", "@types/uuid": "^8.3.1", "@typescript-eslint/eslint-plugin": "^4.31.1", @@ -49,6 +49,7 @@ "mini-css-extract-plugin": "^2.3.0", "prettier": "^2.8.4", "prettier-eslint": "^15.0.1", + "rimraf": "^5.0.5", "style-loader": "^2.0.0", "ts-jest": "^27.0.5", "ts-loader": "^9.4.2", @@ -64,9 +65,7 @@ "url": "https://github.com/contentstack/live-preview-sdk.git" }, "dependencies": { - "just-camel-case": "^4.0.2", - "lodash": "^4.17.21", - "morphdom": "^2.6.1", + "lodash-es": "^4.17.21", "mustache": "^4.2.0", "uuid": "^8.3.2" }, diff --git a/src/__test__/data/contentType.ts b/src/__test__/data/contentType.ts index 9d1afe9b..b04460a8 100644 --- a/src/__test__/data/contentType.ts +++ b/src/__test__/data/contentType.ts @@ -1,4 +1,4 @@ -import { IPageSchema } from "../../types/contentTypeSchema.types"; +import { IPageSchema } from "../../cms/types/contentTypeSchema.types"; export function getAllContentTypes(): { all_fields: IPageSchema; diff --git a/src/types/contentTypeSchema.types.ts b/src/cms/types/contentTypeSchema.types.ts similarity index 100% rename from src/types/contentTypeSchema.types.ts rename to src/cms/types/contentTypeSchema.types.ts diff --git a/src/utils/inIframe.ts b/src/common/inIframe.ts similarity index 100% rename from src/utils/inIframe.ts rename to src/common/inIframe.ts diff --git a/src/utils/__test__/confighandler.test.ts b/src/configManager/__test__/configManager.test.ts similarity index 97% rename from src/utils/__test__/confighandler.test.ts rename to src/configManager/__test__/configManager.test.ts index cda7d7ee..6423467d 100644 --- a/src/utils/__test__/confighandler.test.ts +++ b/src/configManager/__test__/configManager.test.ts @@ -1,5 +1,5 @@ -import Config, { updateConfigFromUrl } from "../configHandler"; -import { getDefaultConfig } from "../defaults"; +import Config, { updateConfigFromUrl } from "../configManager"; +import { getDefaultConfig } from "../config.default"; describe("Config", () => { beforeEach(() => { diff --git a/src/utils/__test__/handleUserConfig.test.ts b/src/configManager/__test__/handleUserConfig.test.ts similarity index 99% rename from src/utils/__test__/handleUserConfig.test.ts rename to src/configManager/__test__/handleUserConfig.test.ts index b183bcb5..bbb3e759 100644 --- a/src/utils/__test__/handleUserConfig.test.ts +++ b/src/configManager/__test__/handleUserConfig.test.ts @@ -1,6 +1,6 @@ -import { getDefaultConfig } from "../defaults"; +import { getDefaultConfig } from "../config.default"; import { handleInitData, handleUserConfig } from "../handleUserConfig"; -import { PublicLogger } from "../public-logger"; +import { PublicLogger } from "../../logger/logger"; import { IConfig, IInitData, diff --git a/src/utils/defaults.ts b/src/configManager/config.default.ts similarity index 97% rename from src/utils/defaults.ts rename to src/configManager/config.default.ts index e3864d64..96cbdc2b 100644 --- a/src/utils/defaults.ts +++ b/src/configManager/config.default.ts @@ -4,6 +4,7 @@ export function getUserInitData(): IInitData { return { ssr: true, enable: true, + debug: false, cleanCslpOnProduction: true, editButton: { enable: true, @@ -40,6 +41,7 @@ export function getDefaultConfig(): IConfig { return { ssr: true, enable: true, + debug: false, cleanCslpOnProduction: true, editButton: { enable: true, diff --git a/src/utils/configHandler.ts b/src/configManager/configManager.ts similarity index 95% rename from src/utils/configHandler.ts rename to src/configManager/configManager.ts index 4c724110..0e255791 100644 --- a/src/utils/configHandler.ts +++ b/src/configManager/configManager.ts @@ -1,7 +1,7 @@ import { IConfig, IInitData } from "../types/types"; -import { getDefaultConfig, getUserInitData } from "./defaults"; +import { getDefaultConfig, getUserInitData } from "./config.default"; import { handleInitData } from "./handleUserConfig"; -import { has as lodashHas, set as lodashSet } from "lodash"; +import { has as lodashHas, set as lodashSet } from "lodash-es"; class Config { static config: IConfig = getDefaultConfig(); diff --git a/src/utils/handleUserConfig.ts b/src/configManager/handleUserConfig.ts similarity index 98% rename from src/utils/handleUserConfig.ts rename to src/configManager/handleUserConfig.ts index 8de91dcf..2eb66c8a 100644 --- a/src/utils/handleUserConfig.ts +++ b/src/configManager/handleUserConfig.ts @@ -1,5 +1,5 @@ -import { shouldRenderEditButton } from "."; -import { PublicLogger } from "./public-logger"; +import { shouldRenderEditButton } from "../livePreview/editButton/editButton"; +import { PublicLogger } from "../logger/logger"; import { IClientUrlParams, IConfig, diff --git a/src/configManager/index.ts b/src/configManager/index.ts new file mode 100644 index 00000000..8cffa364 --- /dev/null +++ b/src/configManager/index.ts @@ -0,0 +1 @@ +export * from "./configManager"; diff --git a/src/contentstack-live-preview-HOC.ts b/src/contentstack-live-preview-HOC.ts deleted file mode 100644 index 7f891855..00000000 --- a/src/contentstack-live-preview-HOC.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; -import camelCase from "just-camel-case"; -import packageJson from "../package.json"; - -import { - IInitData, - IStackSdk, - OnEntryChangeCallback, - OnEntryChangeCallbackUID, - OnEntryChangeConfig, -} from "./types/types"; -import LivePreview from "./live-preview"; -import { getUserInitData } from "./utils/defaults"; -import { PublicLogger } from "./utils/public-logger"; -import { setConfigFromParams } from "./utils/configHandler"; - -export class ContentstackLivePreview { - static livePreview: LivePreview | null = null; - static userConfig: Partial | null = null; - static subscribers: { [uid: string]: OnEntryChangeCallback } = {}; - static configs: { - params: ConstructorParameters[0]; - } = { - params: {}, - }; - - static init( - userConfig: Partial = getUserInitData() - ): Promise | undefined { - if (typeof window !== "undefined") { - if (ContentstackLivePreview.livePreview) { - PublicLogger.warn( - "You have already initialized the Live Preview SDK. So, any subsequent initialization returns the existing SDK instance." - ); - return Promise.resolve(ContentstackLivePreview.livePreview); - } else { - ContentstackLivePreview.livePreview = new LivePreview( - userConfig - ); - ContentstackLivePreview.livePreview.setOnChangeCallback( - ContentstackLivePreview.publish - ); - - setConfigFromParams(this.configs.params); - this.configs.params = {}; - - return Promise.resolve(ContentstackLivePreview.livePreview); - } - } else { - ContentstackLivePreview.userConfig = userConfig; - } - } - - /** - * It is the live preview hash. - * This hash could be used when data is fetched manually. - */ - static get hash(): string { - if (!this.livePreview) { - const urlParams = new URLSearchParams(this.configs.params); - return urlParams.get("live_preview") ?? ""; - } - - return this.livePreview.hash; - } - - /** - * Sets the live preview hash from the query param which is - * accessible via `hash` property. - * @param params query param in an object form - * @deprecated The SDK will automatically get the hash from the URL. - * - */ - static setConfigFromParams( - params: ConstructorParameters[0] = {} - ): void { - if (!this.livePreview) { - this.configs.params = params; - return; - } - - setConfigFromParams(params); - } - - private static publish(): void { - Object.values( - ContentstackLivePreview.subscribers - ).forEach((func) => { - func(); - }); - } - - private static subscribe( - callback: OnEntryChangeCallback - ): OnEntryChangeCallbackUID { - const callbackUid = uuidv4(); - ContentstackLivePreview.subscribers[callbackUid] = callback; - return callbackUid; - } - /** - * @type {function} - * @param onChangeCallback A function param to fetch the data from contentstack database - * @param config An optional object param, pass {skipInitialRender:Boolean} to skip init call to onChangeCallback - * @returns Subscribed Callback UID - */ - static onEntryChange( - onChangeCallback: OnEntryChangeCallback, - config?: OnEntryChangeConfig - ): OnEntryChangeCallbackUID { - const { skipInitialRender = false } = config || {}; - if (ContentstackLivePreview.userConfig) { - ContentstackLivePreview.livePreview = new LivePreview( - ContentstackLivePreview.userConfig - ); - ContentstackLivePreview.livePreview.setOnChangeCallback( - ContentstackLivePreview.publish - ); - - setConfigFromParams(this.configs.params); - - this.configs.params = {}; - - ContentstackLivePreview.userConfig = null; - } - const callbackUid = ContentstackLivePreview.subscribe(onChangeCallback); - if (!skipInitialRender) { - onChangeCallback(); - } - return callbackUid; - } - - /** - * @type {function} - * @param onChangeCallback A function param to fetch the data from contentstack database on content change. - * @returns Subscribed Callback UID - */ - static onLiveEdit( - onChangeCallback: OnEntryChangeCallback - ): OnEntryChangeCallbackUID { - return ContentstackLivePreview.onEntryChange(onChangeCallback, { - skipInitialRender: true, - }); - } - - static unsubscribeOnEntryChange( - callback: string | OnEntryChangeCallback - ): void { - if (typeof callback === "string") { - if (!ContentstackLivePreview.subscribers[callback]) { - PublicLogger.warn("No subscriber found with the given id."); - } - delete ContentstackLivePreview.subscribers[callback]; - } else if (typeof callback === "function") { - const isCallbackDeleted = Object.entries<() => void>( - ContentstackLivePreview.subscribers - ).some(([uid, func]) => { - if (func === callback) { - delete ContentstackLivePreview.subscribers[uid]; - return true; - } - return false; - }); - - if (!isCallbackDeleted) { - PublicLogger.warn( - "No subscriber found with the given callback." - ); - } - } - } - - static async getGatsbyDataFormat( - sdkQuery: IStackSdk, - prefix: string - ): Promise { - if (typeof sdkQuery.find === "function") { - return sdkQuery - .toJSON() - .find() - .then((result: { [key: string]: any }) => { - return result.map((res: { [key: string]: any }) => { - return res.map((entry: { [key: string]: any }) => { - const dataTitle = camelCase( - `${prefix}_${sdkQuery.content_type_uid}` - ); - return { [dataTitle]: entry }; - }); - }); - }) - .catch((err: any) => { - console.error(err); - }); - } else if (typeof sdkQuery.fetch === "function") { - return sdkQuery - .toJSON() - .fetch() - .then((ent: { [key: string]: any }) => { - const dataTitle = camelCase( - `${prefix}_${sdkQuery.content_type_uid}` - ); - const entry = { [dataTitle]: ent }; - - return entry; - }) - .catch((err: unknown) => { - console.error(err); - }); - } - } - - static getSdkVersion(): string { - return packageJson.version; - } -} - -export default ContentstackLivePreview; - -module.exports = ContentstackLivePreview; diff --git a/src/utils/__test__/cslpdata.test.ts b/src/cslp/__test__/cslpdata.test.ts similarity index 100% rename from src/utils/__test__/cslpdata.test.ts rename to src/cslp/__test__/cslpdata.test.ts diff --git a/src/utils/cslpdata.ts b/src/cslp/cslpdata.ts similarity index 84% rename from src/utils/cslpdata.ts rename to src/cslp/cslpdata.ts index 454a0899..107bc72f 100644 --- a/src/utils/cslpdata.ts +++ b/src/cslp/cslpdata.ts @@ -1,10 +1,10 @@ -import _ from "lodash"; +import { isNil, isFinite, findLastIndex, findLast } from "lodash-es"; import { CslpData, CslpDataMultipleFieldMetadata, CslpDataParentDetails, -} from "../types/cslp.types"; -import Config from "./configHandler"; +} from "./types/cslp.types"; +import Config from "../configManager/configManager"; /** * Extracts details from a CSLP value string. @@ -16,8 +16,8 @@ export function extractDetailsFromCslp(cslpValue: string): CslpData { cslpValue.split("."); const calculatedPath = fieldPath.filter((path) => { - const isEmpty = _.isNil(path); - const isNumber = _.isFinite(+path); + const isEmpty = isNil(path); + const isNumber = isFinite(+path); return (!isEmpty && !isNumber) || false; }); @@ -34,7 +34,7 @@ export function extractDetailsFromCslp(cslpValue: string): CslpData { * It represents the index of the field in the multiple field. * Hence, we pop it out. */ - if (_.isFinite(+fieldPath[fieldPath.length - 1])) { + if (isFinite(+fieldPath[fieldPath.length - 1])) { fieldPath.pop(); } @@ -63,7 +63,7 @@ function getParentPathDetails( locale: string, fieldPath: string[] ): CslpDataParentDetails | null { - const index = _.findLastIndex(fieldPath, (path) => _.isFinite(+path)); + const index = findLastIndex(fieldPath, (path) => isFinite(+path)); if (index === -1) return null; const parentPath = fieldPath.slice(0, index); @@ -99,14 +99,19 @@ function getMultipleFieldMetadata( fieldPath ); - const index = _.findLast(fieldPath, (path) => _.isFinite(+path)); + const index = findLast(fieldPath, (path) => isFinite(+path)); return { parentDetails: parentDetails, - index: _.isNil(index) ? -1 : +index, + index: isNil(index) ? -1 : +index, }; } +/** + * Adds an outline to the clicked element and triggers a callback function. + * @param e - The MouseEvent object representing the click event. + * @param callback - An optional callback function that will be called with the CSLP tag and highlighted element as arguments. + */ export function addCslpOutline( e: MouseEvent, callback?: (args: { diff --git a/src/cslp/index.ts b/src/cslp/index.ts new file mode 100644 index 00000000..e30fd082 --- /dev/null +++ b/src/cslp/index.ts @@ -0,0 +1 @@ +export * from "./cslpdata"; diff --git a/src/types/cslp.types.ts b/src/cslp/types/cslp.types.ts similarity index 100% rename from src/types/cslp.types.ts rename to src/cslp/types/cslp.types.ts diff --git a/src/index.ts b/src/index.ts index 286ade62..a90dbc26 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import ContentstackLivePreviewHOC from "./contentstack-live-preview-HOC"; +import ContentstackLivePreviewHOC from "./preview/contentstack-live-preview-HOC"; import "./styles.css"; export const ContentstackLivePreview = ContentstackLivePreviewHOC; diff --git a/src/live-preview.ts b/src/live-preview.ts deleted file mode 100644 index 01380f22..00000000 --- a/src/live-preview.ts +++ /dev/null @@ -1,485 +0,0 @@ -import packageJson from "../package.json"; -import { VisualEditor } from "./liveEditor"; -import { - IEditButtonPosition, - IInitData, - ILivePreviewModeConfig, - ILivePreviewReceivePostMessages, - ILivePreviewWindowType, -} from "./types/types"; -import { - addLivePreviewQueryTags, - createMultipleEditButton, - createSingularEditButton, - getEditButtonPosition, -} from "./utils"; -import Config, { - setConfigFromParams, - updateConfigFromUrl, -} from "./utils/configHandler"; -import { addCslpOutline } from "./utils/cslpdata"; -import { getUserInitData } from "./utils/defaults"; -import { PublicLogger } from "./utils/public-logger"; -import { replaceDocumentBody, updateDocumentBody } from "./utils/replaceHtml"; - -export default class LivePreview { - /** - * @hideconstructor - */ - - private tooltip: HTMLButtonElement | null = null; // this tooltip is responsible to redirect user to Contentstack edit page - - private tooltipChild: { - singular: HTMLDivElement | null; - multiple: HTMLDivElement | null; - } = { - singular: null, - multiple: null, - }; - private tooltipCurrentChild: "multiple" | "singular" = "singular"; - - constructor(initData: Partial = getUserInitData()) { - Config.replace(initData); - updateConfigFromUrl(); - const config = Config.get(); - - this.addEditStyleOnHover = this.addEditStyleOnHover.bind(this); - this.generateRedirectUrl = this.generateRedirectUrl.bind(this); - this.scrollHandler = this.scrollHandler.bind(this); - this.linkClickHandler = this.linkClickHandler.bind(this); - this.setOnChangeCallback = this.setOnChangeCallback.bind(this); - this.resolveIncomingMessage = this.resolveIncomingMessage.bind(this); - this.createCslpTooltip = this.createCslpTooltip.bind(this); - this.requestDataSync = this.requestDataSync.bind(this); - this.updateTooltipPosition = this.updateTooltipPosition.bind(this); - this.removeDataCslp = this.removeDataCslp.bind(this); - - // @ts-ignore - if (initData.debug) { - PublicLogger.debug( - "Contentstack Live Preview Debugging mode: config --", - config - ); - } - - if (config.enable) { - if ( - typeof document !== undefined && - document.readyState === "complete" - ) { - this.requestDataSync(); - } else { - window.addEventListener("load", this.requestDataSync); - } - - window.addEventListener("message", this.resolveIncomingMessage); - window.addEventListener("scroll", this.updateTooltipPosition); - // render the hover outline only when edit button enable - if ( - config.editButton.enable || - config.mode >= ILivePreviewModeConfig.EDITOR - ) { - window.addEventListener("mouseover", this.addEditStyleOnHover); - } - - if (config.ssr) { - window.addEventListener("load", (e) => { - const allATags = document.querySelectorAll("a"); - allATags.forEach((tag) => { - const docOrigin: string = document.location.origin; - if (tag.href && tag.href.includes(docOrigin)) { - const newUrl = addLivePreviewQueryTags(tag.href); - tag.href = newUrl; - } - }); - }); - - // Setting the query params to all the click events related to current domain - window.addEventListener("click", (event: any) => { - const target: any = event.target; - const targetHref: string | any = target.href; - const docOrigin: string = document.location.origin; - if ( - targetHref && - targetHref.includes(docOrigin) && - !targetHref.includes("live_preview") - ) { - const newUrl = addLivePreviewQueryTags(target.href); - event.target.href = newUrl || target.href; - } - }); - } - - if (config.mode >= ILivePreviewModeConfig.EDITOR) { - new VisualEditor(); - } - } else if (config.cleanCslpOnProduction) { - this.removeDataCslp(); - } - } - - private addEditStyleOnHover(e: MouseEvent) { - const updateTooltipPosition: Parameters["1"] = ({ - cslpTag, - highlightedElement, - }) => { - if (this.updateTooltipPosition()) { - this.tooltip?.setAttribute("current-data-cslp", cslpTag); - this.tooltip?.setAttribute( - "current-href", - highlightedElement.getAttribute("href") ?? "" - ); - } - }; - - const config = Config.get(); - if ( - (config.windowType === ILivePreviewWindowType.PREVIEW || - config.windowType === ILivePreviewWindowType.INDEPENDENT) && - config.editButton.enable - ) { - addCslpOutline(e, updateTooltipPosition); - } - } - - private generateRedirectUrl( - content_type_uid: string, - locale = "en-us", - entry_uid: string, - preview_field: string - ): string { - const config = Config.get(); - if (!config.stackDetails.apiKey) { - throw `To use edit tags, you must provide the stack API key. Specify the API key while initializing the Live Preview SDK. - - ContentstackLivePreview.init({ - ..., - stackDetails: { - apiKey: 'your-api-key' - }, - ... - })`; - } - - if (!config.stackDetails.environment) { - throw `To use edit tags, you must provide the preview environment. Specify the preview environment while initializing the Live Preview SDK. - - ContentstackLivePreview.init({ - ..., - stackDetails: { - environment: 'Your-environment' - }, - ... - })`; - } - - const protocol = String(config.clientUrlParams.protocol); - const host = String(config.clientUrlParams.host); - const port = String(config.clientUrlParams.port); - const environment = String(config.stackDetails.environment); - - const urlHash = `!/stack/${ - config.stackDetails.apiKey - }/content-type/${content_type_uid}/${ - locale ?? "en-us" - }/entry/${entry_uid}/edit`; - - const url = new URL(`${protocol}://${host}`); - url.port = port; - url.hash = urlHash; - url.searchParams.append("preview-field", preview_field); - url.searchParams.append("preview-locale", locale ?? "en-us"); - url.searchParams.append("preview-environment", environment); - - return `${url.origin}/${url.hash}${url.search}`; - } - - private scrollHandler() { - if (!this.tooltip) return; - - const cslpTag = this.tooltip.getAttribute("current-data-cslp"); - - if (cslpTag) { - const [content_type_uid, entry_uid, locale, ...field] = - cslpTag.split("."); - - // check if opened inside an iframe - if (window.location !== window.parent.location) { - window.parent.postMessage( - { - from: "live-preview", - type: "scroll", - data: { - field: field.join("."), - content_type_uid, - entry_uid, - locale, - }, - }, - "*" - ); - } else { - try { - const redirectUrl = this.generateRedirectUrl( - content_type_uid, - locale, - entry_uid, - field.join(".") - ); - - window.open(redirectUrl, "_blank"); - } catch (error) { - PublicLogger.error(error); - } - } - } - } - - private linkClickHandler() { - if (!this.tooltip) return; - const hrefAttribute = this.tooltip.getAttribute("current-href"); - - if (hrefAttribute) { - window.location.assign(hrefAttribute); - } - } - - setOnChangeCallback(onChangeCallback: () => void): void { - Config.set("onChange", onChangeCallback); - } - - /** - * It is the live preview hash. - * This hash could be used when data is fetched manually. - */ - get hash(): string { - return Config.get().hash; - } - - private resolveIncomingMessage( - e: MessageEvent - ) { - if (typeof e.data !== "object") return; - - if (e.data.from !== "live-preview") return; - - const config = Config.get(); - - switch (e.data.type) { - case "client-data-send": { - const { contentTypeUid, entryUid } = config.stackDetails; - const { hash } = e.data.data; - - setConfigFromParams({ - live_preview: hash, - content_type_uid: contentTypeUid, - entry_uid: entryUid, - }); - - if (config.ssr) { - // Get the content from the server and replace the body - - const fetch_url = new URL(window.location.href); - - fetch_url.searchParams.append("live_preview", hash); - fetch_url.searchParams.append( - "content_type_uid", - contentTypeUid - ); - fetch_url.searchParams.append("entry_uid", entryUid); - - fetch(fetch_url.toString(), { - method: "GET", - }) - .then((res) => res.text()) - .then((res) => { - updateDocumentBody(document, res, { - onPostOperation: this.createCslpTooltip, - shouldReRunScripts: config.runScriptsOnUpdate, - }); - }); - } else { - const config = Config.get(); - config.onChange(); - } - break; - } - case "init-ack": { - const { - contentTypeUid, - entryUid, - windowType = ILivePreviewWindowType.PREVIEW, - } = e.data.data; - - const stackDetails = Config.get().stackDetails; - - stackDetails.contentTypeUid = contentTypeUid; - stackDetails.entryUid = entryUid; - - Config.set("stackDetails", stackDetails); - Config.set("windowType", windowType); - - break; - } - case "history": { - switch (e.data.data.type) { - case "forward": { - window.history.forward(); - break; - } - case "backward": { - window.history.back(); - break; - } - case "reload": { - window.history.go(); - } - } - break; - } - case "document-body-post-scripts-loaded": { - const { body } = e.data.data; - replaceDocumentBody(body, this.createCslpTooltip); - } - } - } - - private createCslpTooltip = () => { - const config = Config.get(); - - if ( - config.mode >= ILivePreviewModeConfig.EDITOR && - config.windowType === ILivePreviewWindowType.EDITOR - ) { - return; - } - - if ( - !document.getElementById("cslp-tooltip") && - config.editButton.enable - ) { - const tooltip = document.createElement("button"); - tooltip.classList.add("cslp-tooltip"); - tooltip.setAttribute("data-test-id", "cs-cslp-tooltip"); - tooltip.id = "cslp-tooltip"; - window.document.body.insertAdjacentElement("beforeend", tooltip); - this.tooltipChild.singular = createSingularEditButton( - this.scrollHandler - ); - this.tooltipChild.multiple = createMultipleEditButton( - this.scrollHandler, - this.linkClickHandler - ); - - tooltip.innerHTML = ""; - tooltip.appendChild(this.tooltipChild.singular); - this.tooltip = tooltip; - } - this.updateTooltipPosition(); - }; - - // Request parent for data sync when document loads - private requestDataSync() { - const config = Config.get(); - - //! TODO: we replaced the handleOnChange() with this. - //! I don't think we need this. Confirm and remove it. - config.onChange(); - - // add edit tooltip - this.createCslpTooltip(); - - window.parent.postMessage( - { - from: "live-preview", - type: "init", - data: { - config: { - shouldReload: config.ssr, - href: window.location.href, - sdkVersion: packageJson.version, - }, - }, - }, - "*" - ); - - // set timeout for client side (use to show warning: You are not editing this page) - if (!config.ssr) { - setInterval(() => { - window.parent.postMessage( - { - from: "live-preview", - type: "check-entry-page", - data: { - href: window.location.href, - }, - }, - "*" - ); - }, 1500); - } - } - - private updateTooltipPosition() { - const { elements } = Config.get(); - if (!elements.highlightedElement || !this.tooltip) return false; - - const config = Config.get(); - - const currentRectOfElement = - elements.highlightedElement.getBoundingClientRect(); - const currentRectOfParentOfElement = - this.tooltip.parentElement?.getBoundingClientRect(); - - if (currentRectOfElement && currentRectOfParentOfElement) { - let { - upperBoundOfTooltip, - // eslint-disable-next-line prefer-const - leftBoundOfTooltip, - }: IEditButtonPosition = getEditButtonPosition( - elements.highlightedElement, - config.editButton.position - ); - - // if scrolled and element is still visible, make sure tooltip is also visible - if (upperBoundOfTooltip < 0) { - if (currentRectOfElement.top < 0) - upperBoundOfTooltip = currentRectOfElement.top; - else upperBoundOfTooltip = 0; - } - - this.tooltip.style.top = upperBoundOfTooltip + "px"; - this.tooltip.style.zIndex = - elements.highlightedElement.style.zIndex || "200"; - this.tooltip.style.left = leftBoundOfTooltip + "px"; - - if (this.tooltipChild.singular && this.tooltipChild.multiple) { - if (elements.highlightedElement.hasAttribute("href")) { - if (this.tooltipCurrentChild !== "multiple") { - this.tooltip.innerHTML = ""; - this.tooltip.appendChild(this.tooltipChild.multiple); - this.tooltipCurrentChild = "multiple"; - } - } else if (this.tooltipCurrentChild !== "singular") { - this.tooltip.innerHTML = ""; - this.tooltip.appendChild(this.tooltipChild.singular); - this.tooltipCurrentChild = "singular"; - } - } - return true; - } - - return false; - } - - // remove attributes when livePreview is false - private removeDataCslp() { - const nodes = document.querySelectorAll("[data-cslp]"); - - nodes.forEach((node) => { - node.removeAttribute("data-cslp"); - node.removeAttribute("data-cslp-button-position"); - }); - } -} diff --git a/src/liveEditor/index.ts b/src/liveEditor/index.ts index fffb921b..552e8993 100644 --- a/src/liveEditor/index.ts +++ b/src/liveEditor/index.ts @@ -1,4 +1,4 @@ -import _ from "lodash"; +import { throttle } from "lodash-es"; import { generateStartEditingButton } from "./utils/generateStartEditingButton"; import { @@ -16,27 +16,35 @@ import { removeAddInstanceButtons, } from "./utils/multipleElementAddButton"; +import { inIframe } from "../common/inIframe"; +import Config from "../configManager/configManager"; +import { addCslpOutline } from "../cslp/cslpdata"; +import { + ILivePreviewModeConfig, + ILivePreviewWindowType, + IVisualEditorInitEvent, +} from "../types/types"; +import { VisualEditorCslpEventDetails } from "./types/liveEditor.types"; import { FieldSchemaMap } from "./utils/fieldSchemaMap"; import { addFocusOverlay, appendFocusedToolbar, hideFocusOverlay, } from "./utils/focusOverlayWrapper"; +import { generateCustomCursor } from "./utils/generateCustomCursor"; import { getCsDataOfElement, getDOMEditStack, } from "./utils/getCsDataOfElement"; -import liveEditorPostMessage from "./utils/liveEditorPostMessage"; -import { LiveEditorPostMessageEvents } from "./utils/types/postMessage.types"; -import { addCslpOutline, extractDetailsFromCslp } from "../utils/cslpdata"; -import Config from "../utils/configHandler"; -import { ILivePreviewWindowType, IVisualEditorInitEvent } from "../types/types"; -import { inIframe } from "../utils/inIframe"; -import { getFieldType } from "./utils/getFieldType"; -import { generateCustomCursor } from "./utils/generateCustomCursor"; -import { VisualEditorCslpEventDetails } from "../types/liveEditor.types"; import { getEntryUidFromCurrentPage } from "./utils/getEntryUidFromCurrentPage"; +import { getFieldType } from "./utils/getFieldType"; import { isFieldDisabled } from "./utils/isFieldDisabled"; +import liveEditorPostMessage from "./utils/liveEditorPostMessage"; +import { LiveEditorPostMessageEvents } from "./utils/types/postMessage.types"; +import { + useHistoryPostMessageEvent, + useOnEntryUpdatePostMessageEvent, +} from "../livePreview/eventManager/postMessageEvent.hooks"; export class VisualEditor { private customCursor: HTMLDivElement | null = null; @@ -105,6 +113,10 @@ export class VisualEditor { this.appendVisualEditorDOM(); this.addFocusedToolbar = this.addFocusedToolbar.bind(this); + const config = Config.get(); + if (!config.enable || config.mode < ILivePreviewModeConfig.EDITOR) { + return; + } liveEditorPostMessage ?.send("init", { isSSR: Config.get().ssr, @@ -132,6 +144,10 @@ export class VisualEditor { LiveEditorPostMessageEvents.GET_ENTRY_UID_IN_CURRENT_PAGE, getEntryUidFromCurrentPage ); + + // These events are used to sync the data when we made some changes in the entry without invoking live preview module. + useHistoryPostMessageEvent(); + useOnEntryUpdatePostMessageEvent(); }) .catch(() => { if (!inIframe()) { @@ -179,7 +195,7 @@ export class VisualEditor { this.previousSelectedEditableDOM = editableElement; }; - handleMouseHover = _.throttle(async (event: MouseEvent) => { + handleMouseHover = throttle(async (event: MouseEvent) => { const eventDetails = getCsDataOfElement(event); if (!eventDetails) { this.resetCustomCursor(); diff --git a/src/types/liveEditor.types.ts b/src/liveEditor/types/liveEditor.types.ts similarity index 70% rename from src/types/liveEditor.types.ts rename to src/liveEditor/types/liveEditor.types.ts index 11ec3670..34b7dbfb 100644 --- a/src/types/liveEditor.types.ts +++ b/src/liveEditor/types/liveEditor.types.ts @@ -1,4 +1,4 @@ -import { CslpData } from "./cslp.types"; +import { CslpData } from "../../cslp/types/cslp.types"; export interface VisualEditorCslpEventDetails { editableElement: Element; diff --git a/src/liveEditor/utils/__test__/generateStartEditingButton.test.ts b/src/liveEditor/utils/__test__/generateStartEditingButton.test.ts index e94a0d04..a27bcbe4 100644 --- a/src/liveEditor/utils/__test__/generateStartEditingButton.test.ts +++ b/src/liveEditor/utils/__test__/generateStartEditingButton.test.ts @@ -1,5 +1,5 @@ -import { getDefaultConfig } from "../../../utils/defaults"; -import { PublicLogger } from "../../../utils/public-logger"; +import { getDefaultConfig } from "../../../configManager/config.default"; +import { PublicLogger } from "../../../logger/logger"; import { IConfig } from "../../../types/types"; import { generateStartEditingButton } from "../generateStartEditingButton"; diff --git a/src/liveEditor/utils/__test__/getFieldType.test.ts b/src/liveEditor/utils/__test__/getFieldType.test.ts index 785b4cb6..5e38bf3f 100644 --- a/src/liveEditor/utils/__test__/getFieldType.test.ts +++ b/src/liveEditor/utils/__test__/getFieldType.test.ts @@ -17,7 +17,7 @@ import { ISelectContentTypeSchema, ISingleLineTextBoxContentTypeSchema, IURLContentTypeSchema, -} from "../../../types/contentTypeSchema.types"; +} from "../../../cms/types/contentTypeSchema.types"; import { getFieldType } from "../getFieldType"; import { FieldDataType, ISchemaFieldMap } from "../types/index.types"; diff --git a/src/liveEditor/utils/__test__/multipleElementAddButton.test.ts b/src/liveEditor/utils/__test__/multipleElementAddButton.test.ts index 5b24a073..2db89cd5 100644 --- a/src/liveEditor/utils/__test__/multipleElementAddButton.test.ts +++ b/src/liveEditor/utils/__test__/multipleElementAddButton.test.ts @@ -1,6 +1,6 @@ import { getFieldSchemaMap } from "../../../__test__/data/fieldSchemaMap"; import { sleep } from "../../../__test__/utils"; -import { VisualEditorCslpEventDetails } from "../../../types/liveEditor.types"; +import { VisualEditorCslpEventDetails } from "../../types/liveEditor.types"; import { FieldSchemaMap } from "../fieldSchemaMap"; import { getCsDataOfElement } from "../getCsDataOfElement"; import { generateAddInstanceButton } from "../instanceButtons"; diff --git a/src/liveEditor/utils/fieldSchemaMap.ts b/src/liveEditor/utils/fieldSchemaMap.ts index e587761a..9e67b8fa 100644 --- a/src/liveEditor/utils/fieldSchemaMap.ts +++ b/src/liveEditor/utils/fieldSchemaMap.ts @@ -1,4 +1,4 @@ -import { has, isEqual } from "lodash"; +import { has, isEqual } from "lodash-es"; import { ISchemaFieldMap, ISchemaIndividualFieldMap, diff --git a/src/liveEditor/utils/focusOverlayWrapper.ts b/src/liveEditor/utils/focusOverlayWrapper.ts index 25ce1c72..0280410c 100644 --- a/src/liveEditor/utils/focusOverlayWrapper.ts +++ b/src/liveEditor/utils/focusOverlayWrapper.ts @@ -1,6 +1,6 @@ -import { CslpData } from "../../types/cslp.types"; -import { VisualEditorCslpEventDetails } from "../../types/liveEditor.types"; -import { extractDetailsFromCslp } from "../../utils/cslpdata"; +import { CslpData } from "../../cslp/types/cslp.types"; +import { VisualEditorCslpEventDetails } from "../types/liveEditor.types"; +import { extractDetailsFromCslp } from "../../cslp/cslpdata"; import { DATA_CSLP_ATTR_SELECTOR, LIVE_PREVIEW_OUTLINE_WIDTH_IN_PX, diff --git a/src/liveEditor/utils/generateFieldSchemaMap.ts b/src/liveEditor/utils/generateFieldSchemaMap.ts deleted file mode 100644 index 186b44ed..00000000 --- a/src/liveEditor/utils/generateFieldSchemaMap.ts +++ /dev/null @@ -1,91 +0,0 @@ -import _ from "lodash"; -import { - ISchemaIndividualFieldMap, - ITraverseSchemaVisitor, -} from "./types/index.types"; -import { - IContentTypeRootBlocks, - IPageSchema, -} from "../../types/contentTypeSchema.types"; - -export function generateFieldSchemaMap( - pageCT: IPageSchema -): ISchemaIndividualFieldMap { - const getFieldSchemaMap: ITraverseSchemaVisitor = { - fieldMap: {}, - should_visit: (_fieldSchema, _path) => { - return true; - }, - visit: function (fieldSchema, path) { - this.fieldMap[path] = fieldSchema; - if (fieldSchema.data_type === "link") { - //handle special key for special fields - this.fieldMap[`${path}.title`] = fieldSchema; - this.fieldMap[`${path}.href`] = fieldSchema; - } - if (fieldSchema.data_type === "file") { - this.fieldMap[`${path}.url`] = fieldSchema; - } - if (fieldSchema.data_type === "blocks") { - if (!fieldSchema.blocks) { - return; - } - fieldSchema.blocks.map((block) => { - this.fieldMap[`${path}.${block.uid}`] = { - ...block, - data_type: "block", - display_name: block.title, - }; - }); - } - }, - }; - traverseSchema(pageCT.schema, [getFieldSchemaMap]); - return getFieldSchemaMap.fieldMap; -} - -function traverseSchema( - schema: IContentTypeRootBlocks[], - visitors: Array -): void { - function genPath(prefix: string, path: string) { - return _.isEmpty(prefix) ? path : [prefix, path].join("."); - } - - function traverse(fields: IContentTypeRootBlocks[], path: string) { - path = path || ""; - for (const field of fields) { - const currPath = genPath(path, field.uid); - - visitors.forEach((visitor) => { - if (visitor.should_visit(field, currPath)) { - visitor.visit(field, currPath); - } - }); - - if (field.data_type === "group") traverse(field.schema, currPath); - else if ( - field.data_type === "global_field" && - _.isUndefined(field.schema) === false && - _.isEmpty(field.schema) === false - ) - traverse(field.schema, currPath); - if (field.data_type === "blocks") { - field.blocks.forEach(function (block) { - if (block.schema) - traverse(block.schema, currPath + "." + block.uid); - }); - } - // if (field.data_type === "experience_container") { - // field.variations.forEach(function (variation: any) { - // if (variation.schema) - // traverse( - // variation.schema, - // currPath + "." + variation.uid - // ); - // }); - // } - } - } - traverse(schema, ""); -} diff --git a/src/liveEditor/utils/generateStartEditingButton.ts b/src/liveEditor/utils/generateStartEditingButton.ts index cad73de0..7a27cab2 100644 --- a/src/liveEditor/utils/generateStartEditingButton.ts +++ b/src/liveEditor/utils/generateStartEditingButton.ts @@ -1,6 +1,6 @@ -import Config from "../../utils/configHandler"; -import { extractDetailsFromCslp } from "../../utils/cslpdata"; -import { PublicLogger } from "../../utils/public-logger"; +import Config from "../../configManager/configManager"; +import { extractDetailsFromCslp } from "../../cslp/cslpdata"; +import { PublicLogger } from "../../logger/logger"; const editIcon = ` diff --git a/src/liveEditor/utils/getCsDataOfElement.ts b/src/liveEditor/utils/getCsDataOfElement.ts index da854845..6fb68ac2 100644 --- a/src/liveEditor/utils/getCsDataOfElement.ts +++ b/src/liveEditor/utils/getCsDataOfElement.ts @@ -1,6 +1,6 @@ -import { CslpData } from "../../types/cslp.types"; -import { VisualEditorCslpEventDetails } from "../../types/liveEditor.types"; -import { extractDetailsFromCslp } from "../../utils/cslpdata"; +import { CslpData } from "../../cslp/types/cslp.types"; +import { VisualEditorCslpEventDetails } from "../types/liveEditor.types"; +import { extractDetailsFromCslp } from "../../cslp/cslpdata"; import { DATA_CSLP_ATTR_SELECTOR } from "./constants"; /** diff --git a/src/liveEditor/utils/getEntryUidFromCurrentPage.ts b/src/liveEditor/utils/getEntryUidFromCurrentPage.ts index 4a7d3af0..dfdd8793 100644 --- a/src/liveEditor/utils/getEntryUidFromCurrentPage.ts +++ b/src/liveEditor/utils/getEntryUidFromCurrentPage.ts @@ -1,4 +1,4 @@ -import { extractDetailsFromCslp } from "../../utils/cslpdata"; +import { extractDetailsFromCslp } from "../../cslp/cslpdata"; export function getEntryUidFromCurrentPage() { const elementsWithCslp = Array.from( diff --git a/src/liveEditor/utils/handleIndividualFields.ts b/src/liveEditor/utils/handleIndividualFields.ts index 7b11370f..4156dfc6 100644 --- a/src/liveEditor/utils/handleIndividualFields.ts +++ b/src/liveEditor/utils/handleIndividualFields.ts @@ -1,4 +1,4 @@ -import { VisualEditorCslpEventDetails } from "../../types/liveEditor.types"; +import { VisualEditorCslpEventDetails } from "../types/liveEditor.types"; import { generateReplaceAssetButton, removeReplaceAssetButton, diff --git a/src/liveEditor/utils/isFieldDisabled.ts b/src/liveEditor/utils/isFieldDisabled.ts index a1810096..2ed71ffd 100644 --- a/src/liveEditor/utils/isFieldDisabled.ts +++ b/src/liveEditor/utils/isFieldDisabled.ts @@ -1,5 +1,5 @@ -import { VisualEditorCslpEventDetails } from "../../types/liveEditor.types"; -import Config from "../../utils/configHandler"; +import { VisualEditorCslpEventDetails } from "../types/liveEditor.types"; +import Config from "../../configManager/configManager"; import { ISchemaFieldMap } from "./types/index.types"; export const isFieldDisabled = ( diff --git a/src/liveEditor/utils/multipleElementAddButton.ts b/src/liveEditor/utils/multipleElementAddButton.ts index 66b3caf5..74886574 100644 --- a/src/liveEditor/utils/multipleElementAddButton.ts +++ b/src/liveEditor/utils/multipleElementAddButton.ts @@ -1,4 +1,4 @@ -import { VisualEditorCslpEventDetails } from "../../types/liveEditor.types"; +import { VisualEditorCslpEventDetails } from "../types/liveEditor.types"; import { FieldSchemaMap } from "./fieldSchemaMap"; import { generateAddInstanceButton, diff --git a/src/liveEditor/utils/pseudoEditableField.ts b/src/liveEditor/utils/pseudoEditableField.ts index 318d2979..90b1d13f 100644 --- a/src/liveEditor/utils/pseudoEditableField.ts +++ b/src/liveEditor/utils/pseudoEditableField.ts @@ -1,5 +1,5 @@ -import { toString } from "lodash"; -import { CslpData } from "../../types/cslp.types"; +import { toString } from "lodash-es"; +import { CslpData } from "../../cslp/types/cslp.types"; import liveEditorPostMessage from "./liveEditorPostMessage"; import { LiveEditorPostMessageEvents } from "./types/postMessage.types"; diff --git a/src/liveEditor/utils/types/index.types.ts b/src/liveEditor/utils/types/index.types.ts index 0fde9296..0bd608c8 100644 --- a/src/liveEditor/utils/types/index.types.ts +++ b/src/liveEditor/utils/types/index.types.ts @@ -1,7 +1,7 @@ import { IContentTypeRootBlocks, IModularBlockSingleBlock, -} from "../../../types/contentTypeSchema.types"; +} from "../../../cms/types/contentTypeSchema.types"; export type ISchemaIndividualFieldMap = Record; diff --git a/src/__test__/live-preview.test.ts b/src/livePreview/__test__/live-preview.test.ts similarity index 93% rename from src/__test__/live-preview.test.ts rename to src/livePreview/__test__/live-preview.test.ts index ebdc2798..60a696f9 100644 --- a/src/__test__/live-preview.test.ts +++ b/src/livePreview/__test__/live-preview.test.ts @@ -1,19 +1,19 @@ -import fetch from "jest-fetch-mock"; - import LivePreview from "../live-preview"; -import * as LiveEditorModule from "../liveEditor"; -import { getDefaultConfig } from "../utils/defaults"; -import { PublicLogger } from "../utils/public-logger"; +import * as LiveEditorModule from "../../liveEditor"; +import { getDefaultConfig } from "../../configManager/config.default"; +import { PublicLogger } from "../../logger/logger"; -import { IInitData, ILivePreviewWindowType } from "../types/types"; -import Config from "../utils/configHandler"; +import { IInitData, ILivePreviewWindowType } from "../../types/types"; +import Config from "../../configManager/configManager"; import { convertObjectToMinifiedString, sendPostmessageToWindow, -} from "./utils"; +} from "../../__test__/utils"; -jest.mock("../liveEditor/utils/liveEditorPostMessage", () => { - const { getAllContentTypes } = jest.requireActual("./data/contentType"); +jest.mock("../../liveEditor/utils/liveEditorPostMessage", () => { + const { getAllContentTypes } = jest.requireActual( + "../../__test__/data/contentType" + ); const contentTypes = getAllContentTypes(); return { __esModule: true, @@ -440,12 +440,11 @@ describe("cslp tooltip", () => { return mockLocation; }); - const parentLocationSpy = jest - .spyOn(window.parent, "location", "get") + const topLocationSpy = jest + .spyOn(window, "top", "get") .mockImplementation(() => { const mockLocation = JSON.parse(JSON.stringify(location)); mockLocation.href = "https://example1.com"; - return mockLocation; }); @@ -484,7 +483,7 @@ describe("cslp tooltip", () => { ); locationSpy.mockRestore(); - parentLocationSpy.mockRestore(); + topLocationSpy.mockRestore(); }); test("should disable the edit button when the editButton config is disabled", async () => { @@ -559,12 +558,11 @@ describe("cslp tooltip", () => { return mockLocation; }); - const parentLocationSpy = jest - .spyOn(window.parent, "location", "get") + const topLocationSpy = jest + .spyOn(window, "top", "get") .mockImplementation(() => { const mockLocation = JSON.parse(JSON.stringify(location)); mockLocation.href = "https://example1.com"; - return mockLocation; }); @@ -598,7 +596,7 @@ describe("cslp tooltip", () => { expect(tooltip?.getAttribute("current-data-cslp")).toBe(undefined); locationSpy.mockRestore(); - parentLocationSpy.mockRestore(); + topLocationSpy.mockRestore(); }); test("should enable the edit button when the editButton config is disabled for outside live preview panel but query parameter is passed", async () => { @@ -726,7 +724,6 @@ describe("debug module", () => { describe("incoming postMessage", () => { beforeEach(() => { - fetch.resetMocks(); Config.reset(); }); afterEach(() => { @@ -773,41 +770,6 @@ describe("incoming postMessage", () => { }); }); - test("should fetch data when client-data-send is sent with ssr: true", async () => { - new LivePreview({ - enable: true, - ssr: true, - stackDetails: { - apiKey: "iiyy", - }, - }); - - await sendPostmessageToWindow("init-ack", { - entryUid: "entryUid", - contentTypeUid: "entryContentTypeUid", - }); - - const expectedLivePreviewDomBody = ` -

Modified Body

- `; - const expectedLivePreviewFetchUrl = - "http://localhost/?live_preview=livePreviewHash1234&content_type_uid=entryContentTypeUid&entry_uid=entryUid"; - - fetch.mockResponse(expectedLivePreviewDomBody); - - await sendPostmessageToWindow("client-data-send", { - hash: "livePreviewHash1234", - content_type_uid: "entryContentTypeUid", - }); - - const livePreviewFetchUrl = fetch.mock.calls[0][0]; - - expect(livePreviewFetchUrl).toBe(expectedLivePreviewFetchUrl); - expect(document.body.children[0].outerHTML.trim()).toBe( - expectedLivePreviewDomBody.trim() - ); - }); - test("should receive contentTypeUid and EntryUid on init-ack", async () => { const livePreview = new LivePreview({ enable: true, diff --git a/src/livePreview/editButton/__test__/editButton.test.ts b/src/livePreview/editButton/__test__/editButton.test.ts new file mode 100644 index 00000000..60e7f744 --- /dev/null +++ b/src/livePreview/editButton/__test__/editButton.test.ts @@ -0,0 +1,237 @@ +import { DOMRect } from "../../../__test__/utils"; +import { + createMultipleEditButton, + createSingularEditButton, + getEditButtonPosition, +} from "../editButton"; + +let editCallback: jest.Mock | undefined; +let linkCallback: jest.Mock | undefined; +let editButtonForHyperlink: HTMLDivElement | undefined; +let editButtonForNonHyperlink: HTMLDivElement | undefined; +let tooltipChild: HTMLCollectionOf | undefined; + +describe("Edit button", () => { + beforeEach(() => { + editCallback = jest.fn(); + + editButtonForNonHyperlink = createSingularEditButton(editCallback); + + document.body.appendChild(editButtonForNonHyperlink); + + tooltipChild = document.getElementsByClassName( + "cslp-tooltip-child" + ) as HTMLCollectionOf; + }); + + afterEach(() => { + editCallback = undefined; + linkCallback = undefined; + editButtonForNonHyperlink = undefined; + tooltipChild = undefined; + + document.body.innerHTML = ""; + }); + + test("runs edit callback when clicked", () => { + if (!(editCallback && editButtonForNonHyperlink && tooltipChild)) + fail("missing dependencies"); + + const editButton = tooltipChild[0]; + editButton.click(); + expect(editCallback).toBeCalled(); + }); +}); +describe("getEditButtonPosition: Edit button", () => { + beforeAll(() => { + const titlePara = document.createElement("h3"); + titlePara.setAttribute("data-test-id", "title-para"); + if (titlePara) { + titlePara.getBoundingClientRect = jest.fn( + () => new DOMRect(53, 75, 1529, 10) + ); + } + + document.body.appendChild(titlePara); + }); + + afterAll(() => { + document.getElementsByTagName("html")[0].innerHTML = ""; + jest.clearAllMocks(); + }); + + test("should be positioned on top of hovered element", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "top") + ).toStrictEqual({ upperBoundOfTooltip: 36.75, leftBoundOfTooltip: 53 }); + }); + + test("should be positioned on top-left of hovered element", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "top-left") + ).toStrictEqual({ upperBoundOfTooltip: 36.75, leftBoundOfTooltip: 53 }); + }); + + test("should be positioned on top-center of hovered element", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "top-center") + ).toStrictEqual({ + upperBoundOfTooltip: 36.75, + leftBoundOfTooltip: 786.5, + }); + }); + + test("should be positioned on top-right of hovered element", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "top-right") + ).toStrictEqual({ + upperBoundOfTooltip: 36.75, + leftBoundOfTooltip: 1515, + }); + }); + + test("should be positioned on right of hovered element", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "right") + ).toStrictEqual({ + upperBoundOfTooltip: 71.75, + leftBoundOfTooltip: 1592, + }); + }); + + test("should be positioned on bottom-right of hovered element", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "bottom-right") + ).toStrictEqual({ + upperBoundOfTooltip: 114.75, + leftBoundOfTooltip: 1515, + }); + }); + + test("should be positioned on bottom-center of hovered element", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "bottom-center") + ).toStrictEqual({ + upperBoundOfTooltip: 114.75, + leftBoundOfTooltip: 786.5, + }); + }); + + test("should be positioned on bottom-left of hovered element", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "bottom-left") + ).toStrictEqual({ + upperBoundOfTooltip: 114.75, + leftBoundOfTooltip: 53, + }); + }); + + test("should be positioned on bottom of hovered element", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "bottom") + ).toStrictEqual({ + upperBoundOfTooltip: 114.75, + leftBoundOfTooltip: 53, + }); + }); + + test("should be positioned on left of hovered element", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "left") + ).toStrictEqual({ + upperBoundOfTooltip: 71.75, + leftBoundOfTooltip: -19, + }); + }); + + test("should override the default position if position attribute is present", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + titlePara?.setAttribute("data-cslp-button-position", "top-center"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "top-left") + ).toStrictEqual({ + upperBoundOfTooltip: 36.75, + leftBoundOfTooltip: 786.5, + }); + }); + + test("should positioned on top-left if the passed position is not valid ", async () => { + const titlePara = document.querySelector("[data-test-id='title-para']"); + titlePara?.setAttribute("data-cslp-button-position", "random-string"); + expect( + getEditButtonPosition(titlePara as HTMLElement, "top-left") + ).toStrictEqual({ upperBoundOfTooltip: 36.75, leftBoundOfTooltip: 53 }); + }); +}); +describe("Edit button for Link", () => { + beforeEach(() => { + editCallback = jest.fn(); + linkCallback = jest.fn(); + + editButtonForHyperlink = createMultipleEditButton( + editCallback, + linkCallback + ); + + document.body.appendChild(editButtonForHyperlink); + + tooltipChild = document.getElementsByClassName( + "cslp-tooltip-child" + ) as HTMLCollectionOf; + }); + + afterEach(() => { + editCallback = undefined; + linkCallback = undefined; + editButtonForHyperlink = undefined; + tooltipChild = undefined; + + document.body.innerHTML = ""; + }); + + test("edit button must have 2 separate button", () => { + expect(tooltipChild).toHaveLength(2); + }); + + test("runs edit callback on button click", () => { + if ( + !( + editCallback && + linkCallback && + editButtonForHyperlink && + tooltipChild + ) + ) + fail("missing dependencies"); + + const editButton = tooltipChild[0]; + editButton.click(); + expect(editCallback).toBeCalled(); + }); + + test("runs link callback on button click", () => { + if ( + !( + editCallback && + linkCallback && + editButtonForHyperlink && + tooltipChild + ) + ) + fail("missing dependencies"); + + const linkButton = tooltipChild[1]; + linkButton.click(); + expect(linkCallback).toBeCalled(); + }); +}); diff --git a/src/livePreview/editButton/editButton.ts b/src/livePreview/editButton/editButton.ts new file mode 100644 index 00000000..4e185dfb --- /dev/null +++ b/src/livePreview/editButton/editButton.ts @@ -0,0 +1,488 @@ +import { inIframe } from "../../common/inIframe"; +import Config from "../../configManager/configManager"; +import { addCslpOutline, extractDetailsFromCslp } from "../../cslp"; +import { PublicLogger } from "../../logger/logger"; +import { + IConfigEditButton, + IEditButtonPosition, + ILivePreviewWindowType, +} from "../../types/types"; +import livePreviewPostMessage from "../eventManager/livePreviewEventManager"; + +function calculateEditButtonPosition( + currentHoveredElement: HTMLElement, + cslpButtonPosition: string +): IEditButtonPosition { + const editButtonPosition: IEditButtonPosition = { + upperBoundOfTooltip: 0, + leftBoundOfTooltip: 0, + }; + const currentRectOfElement = currentHoveredElement.getBoundingClientRect(); + try { + const buttonMeasurementValues = { + width: 72, + halfWidth: 36, + height: 40, + basicMargin: 5, + widthWithMargin: 77, + }; + + switch (cslpButtonPosition) { + case "top-center": + editButtonPosition.upperBoundOfTooltip = + currentRectOfElement.top - buttonMeasurementValues.height; + editButtonPosition.leftBoundOfTooltip = + currentRectOfElement.width / 2 - + buttonMeasurementValues.halfWidth + + currentRectOfElement.left; + break; + case "top-right": + editButtonPosition.upperBoundOfTooltip = + currentRectOfElement.top - buttonMeasurementValues.height; + editButtonPosition.leftBoundOfTooltip = + currentRectOfElement.right - buttonMeasurementValues.width; + break; + case "right": + editButtonPosition.upperBoundOfTooltip = + currentRectOfElement.top - + buttonMeasurementValues.basicMargin; + editButtonPosition.leftBoundOfTooltip = + currentRectOfElement.right + + buttonMeasurementValues.basicMargin; + break; + case "bottom": + editButtonPosition.upperBoundOfTooltip = + currentRectOfElement.bottom + + buttonMeasurementValues.basicMargin; + editButtonPosition.leftBoundOfTooltip = + currentRectOfElement.left - + buttonMeasurementValues.basicMargin; + break; + case "bottom-left": + editButtonPosition.upperBoundOfTooltip = + currentRectOfElement.bottom + + buttonMeasurementValues.basicMargin; + editButtonPosition.leftBoundOfTooltip = + currentRectOfElement.left - + buttonMeasurementValues.basicMargin; + break; + case "bottom-center": + editButtonPosition.upperBoundOfTooltip = + currentRectOfElement.bottom + + buttonMeasurementValues.basicMargin; + editButtonPosition.leftBoundOfTooltip = + currentRectOfElement.width / 2 - + buttonMeasurementValues.halfWidth + + currentRectOfElement.left; + break; + case "bottom-right": + editButtonPosition.upperBoundOfTooltip = + currentRectOfElement.bottom + + buttonMeasurementValues.basicMargin; + editButtonPosition.leftBoundOfTooltip = + currentRectOfElement.right - buttonMeasurementValues.width; + break; + case "left": + editButtonPosition.upperBoundOfTooltip = + currentRectOfElement.top - + buttonMeasurementValues.basicMargin; + editButtonPosition.leftBoundOfTooltip = + currentRectOfElement.left - + buttonMeasurementValues.widthWithMargin; + break; + // default position => top, top-left or any other string + default: + editButtonPosition.upperBoundOfTooltip = + currentRectOfElement.top - buttonMeasurementValues.height; + editButtonPosition.leftBoundOfTooltip = + currentRectOfElement.left - + buttonMeasurementValues.basicMargin; + break; + } + return editButtonPosition; + } catch (error) { + PublicLogger.error(error); + return editButtonPosition; + } +} + +export const createSingularEditButton = ( + editCallback: (e: MouseEvent) => void +): HTMLDivElement => { + const singularEditButton = document.createElement("div"); + singularEditButton.classList.add("cslp-tooltip-child", "singular"); + singularEditButton.setAttribute( + "data-test-id", + "cslp-singular-edit-button" + ); + singularEditButton.innerHTML = ` + + + Edit`; + + singularEditButton.addEventListener("click", editCallback); + + return singularEditButton; +}; +export const createMultipleEditButton = ( + editCallback: (e: MouseEvent) => void, + linkCallback: (e: MouseEvent) => void +): HTMLDivElement => { + const multipleEditButton = document.createElement("div"); + multipleEditButton.classList.add("cslp-tooltip-child"); + multipleEditButton.setAttribute("data-title", "Edit"); + multipleEditButton.setAttribute( + "data-test-id", + "cslp-multiple-edit-button" + ); + multipleEditButton.innerHTML = ` + + + `; + + multipleEditButton.addEventListener("click", editCallback); + + const multipleExternalLinkButton = document.createElement("div"); + multipleExternalLinkButton.classList.add("cslp-tooltip-child"); + multipleExternalLinkButton.setAttribute("data-title", "Go to link"); + multipleExternalLinkButton.setAttribute( + "data-test-id", + "cslp-multiple-external-link-button" + ); + multipleExternalLinkButton.innerHTML = ` + + + `; + + multipleExternalLinkButton.addEventListener("click", linkCallback); + + const multipleEditFragment = document.createDocumentFragment(); + multipleEditFragment.appendChild(multipleEditButton); + multipleEditFragment.appendChild(multipleExternalLinkButton); + + const multipleDiv = document.createElement("div"); + multipleDiv.appendChild(multipleEditFragment); + multipleDiv.classList.add("multiple"); + + return multipleDiv; +}; + +export function shouldRenderEditButton(editButton: IConfigEditButton): boolean { + if (!editButton.enable) { + if (editButton.enable === undefined) + PublicLogger.error( + "enable key is required inside editButton object" + ); + return false; + } + + // return boolean in case of cslp-buttons query added in url + try { + const currentLocation = new URL(window.location.href); + const cslpButtonQueryValue = + currentLocation.searchParams.get("cslp-buttons"); + if ( + cslpButtonQueryValue !== null && + editButton.includeByQueryParameter !== false + ) + return cslpButtonQueryValue === "false" ? false : true; + } catch (error) { + PublicLogger.error(error); + } + + // case if inside live preview + if ( + inIframe() && + editButton.exclude?.find( + (exclude) => exclude === "insideLivePreviewPortal" + ) + ) { + return false; + } else if (inIframe()) { + return true; + } + + // case outside live preview + if ( + editButton.exclude?.find( + (exclude) => exclude === "outsideLivePreviewPortal" + ) + ) { + return false; + } + + // Priority list => 1. cslpEditButton query value 2. Inside live preview 3. renderCslpButtonByDefault value selected by user + return true; +} +export function getEditButtonPosition( + currentHoveredElement: HTMLElement | null, + defaultPosition: string | undefined +): IEditButtonPosition { + if (!currentHoveredElement) + return { upperBoundOfTooltip: 0, leftBoundOfTooltip: 0 }; + + const cslpButtonPosition = currentHoveredElement.getAttribute( + "data-cslp-button-position" + ); + if (cslpButtonPosition) { + return calculateEditButtonPosition( + currentHoveredElement, + cslpButtonPosition + ); + } + + // NOTE: position "top" and "top-left" will be the position of edit button if no default position passed in config + return calculateEditButtonPosition( + currentHoveredElement, + defaultPosition || "top" + ); +} + +export class LivePreviewEditButton { + private tooltip: HTMLButtonElement | null = null; + private typeOfCurrentChild: "singular" | "multiple" = "singular"; + private tooltipChild: { + singular: HTMLDivElement | null; + multiple: HTMLDivElement | null; + } = { + singular: null, + multiple: null, + }; + + constructor() { + this.createCslpTooltip = this.createCslpTooltip.bind(this); + this.updateTooltipPosition = this.updateTooltipPosition.bind(this); + this.addEditStyleOnHover = this.addEditStyleOnHover.bind(this); + this.scrollHandler = this.scrollHandler.bind(this); + this.generateRedirectUrl = this.generateRedirectUrl.bind(this); + this.linkClickHandler = this.linkClickHandler.bind(this); + this.destroy = this.destroy.bind(this); + + if (this.createCslpTooltip()) { + this.updateTooltipPosition(); + + window.addEventListener("scroll", this.updateTooltipPosition); + window.addEventListener("mouseover", this.addEditStyleOnHover); + } + } + + private createCslpTooltip(): boolean { + const config = Config.get(); + + if ( + !document.getElementById("cslp-tooltip") && + config.editButton.enable + ) { + const tooltip = document.createElement("button"); + this.tooltip = tooltip; + + this.tooltip.classList.add("cslp-tooltip"); + this.tooltip.setAttribute("data-test-id", "cs-cslp-tooltip"); + this.tooltip.id = "cslp-tooltip"; + + window.document.body.insertAdjacentElement( + "beforeend", + this.tooltip + ); + this.tooltipChild.singular = createSingularEditButton( + this.scrollHandler + ); + this.tooltipChild.multiple = createMultipleEditButton( + this.scrollHandler, + this.linkClickHandler + ); + + this.tooltip.appendChild(this.tooltipChild.singular); + return true; + } + return false; + } + + private updateTooltipPosition() { + const config = Config.get(); + const { elements } = config; + + if (!elements.highlightedElement || !this.tooltip) return false; + + const currentRectOfElement = + elements.highlightedElement.getBoundingClientRect(); + const currentRectOfParentOfElement = + this.tooltip.parentElement?.getBoundingClientRect(); + + if (currentRectOfElement && currentRectOfParentOfElement) { + const editButtonPosition = getEditButtonPosition( + elements.highlightedElement, + config.editButton.position + ); + + let upperBoundOfTooltip = editButtonPosition.upperBoundOfTooltip; + const leftBoundOfTooltip = editButtonPosition.leftBoundOfTooltip; + + // if scrolled and element is still visible, make sure tooltip is also visible + if (upperBoundOfTooltip < 0) { + if (currentRectOfElement.top < 0) + upperBoundOfTooltip = currentRectOfElement.top; + else upperBoundOfTooltip = 0; + } + + this.tooltip.style.top = upperBoundOfTooltip + "px"; + this.tooltip.style.zIndex = + elements.highlightedElement.style.zIndex || "200"; + this.tooltip.style.left = leftBoundOfTooltip + "px"; + + if (this.tooltipChild.singular && this.tooltipChild.multiple) { + if ( + elements.highlightedElement.hasAttribute("href") && + this.typeOfCurrentChild !== "multiple" + ) { + this.tooltip.innerHTML = ""; + this.tooltip.appendChild(this.tooltipChild.multiple); + this.typeOfCurrentChild = "multiple"; + } else if (this.typeOfCurrentChild !== "singular") { + this.tooltip.innerHTML = ""; + this.tooltip.appendChild(this.tooltipChild.singular); + this.typeOfCurrentChild = "singular"; + } + } + return true; + } + + return false; + } + + private addEditStyleOnHover(e: MouseEvent) { + const updateTooltipPosition: Parameters["1"] = ({ + cslpTag, + highlightedElement, + }) => { + if (this.updateTooltipPosition()) { + this.tooltip?.setAttribute("current-data-cslp", cslpTag); + this.tooltip?.setAttribute( + "current-href", + highlightedElement.getAttribute("href") ?? "" + ); + } + }; + + const config = Config.get(); + if ( + (config.windowType === ILivePreviewWindowType.PREVIEW || + config.windowType === ILivePreviewWindowType.INDEPENDENT) && + config.editButton.enable + ) { + addCslpOutline(e, updateTooltipPosition); + } + } + + private scrollHandler() { + if (!this.tooltip) return; + + const cslpTag = this.tooltip.getAttribute("current-data-cslp"); + + if (cslpTag) { + const { content_type_uid, entry_uid, locale, fieldPathWithIndex } = + extractDetailsFromCslp(cslpTag); + + if (inIframe()) { + livePreviewPostMessage?.send("scroll", { + field: fieldPathWithIndex, + content_type_uid, + entry_uid, + locale, + }); + } else { + try { + // Redirect to Contentstack edit page + const redirectUrl = this.generateRedirectUrl( + content_type_uid, + locale, + entry_uid, + fieldPathWithIndex + ); + + window.open(redirectUrl, "_blank"); + } catch (error) { + PublicLogger.error(error); + } + } + } + } + + /** + * Generates the redirect URL for editing a specific entry in the Live Preview SDK. + * @param content_type_uid - The UID of the content type. + * @param locale - The locale of the entry (default: "en-us"). + * @param entry_uid - The UID of the entry. + * @param preview_field - The field to be previewed. + * @returns The redirect URL for editing the entry. + */ + private generateRedirectUrl( + content_type_uid: string, + locale = "en-us", + entry_uid: string, + preview_field: string + ): string { + const config = Config.get(); + if (!config.stackDetails.apiKey) { + throw `To use edit tags, you must provide the stack API key. Specify the API key while initializing the Live Preview SDK. + + ContentstackLivePreview.init({ + ..., + stackDetails: { + apiKey: 'your-api-key' + }, + ... + })`; + } + + if (!config.stackDetails.environment) { + throw `To use edit tags, you must provide the preview environment. Specify the preview environment while initializing the Live Preview SDK. + + ContentstackLivePreview.init({ + ..., + stackDetails: { + environment: 'Your-environment' + }, + ... + })`; + } + + const protocol = String(config.clientUrlParams.protocol); + const host = String(config.clientUrlParams.host); + const port = String(config.clientUrlParams.port); + const environment = String(config.stackDetails.environment); + + const urlHash = `!/stack/${ + config.stackDetails.apiKey + }/content-type/${content_type_uid}/${ + locale ?? "en-us" + }/entry/${entry_uid}/edit`; + + const url = new URL(`${protocol}://${host}`); + url.port = port; + url.hash = urlHash; + url.searchParams.append("preview-field", preview_field); + url.searchParams.append("preview-locale", locale ?? "en-us"); + url.searchParams.append("preview-environment", environment); + + return `${url.origin}/${url.hash}${url.search}`; + } + + private linkClickHandler() { + if (!this.tooltip) return; + const hrefAttribute = this.tooltip.getAttribute("current-href"); + + if (hrefAttribute) { + window.location.assign(hrefAttribute); + } + } + + /** + * Destroys the edit button by removing event listeners and removing the tooltip. + */ + destroy(): void { + window.removeEventListener("scroll", this.updateTooltipPosition); + window.removeEventListener("mouseover", this.addEditStyleOnHover); + this.tooltip?.remove(); + } +} diff --git a/src/livePreview/eventManager/livePreviewEventManager.ts b/src/livePreview/eventManager/livePreviewEventManager.ts new file mode 100644 index 00000000..acdec16d --- /dev/null +++ b/src/livePreview/eventManager/livePreviewEventManager.ts @@ -0,0 +1,13 @@ +import { EventManager } from "@contentstack/advanced-post-message"; +import { LIVE_PREVIEW_CHANNEL_ID } from "./livePreviewEventManger.constant"; + +let livePreviewPostMessage: EventManager | undefined; + +if (typeof window !== "undefined") { + livePreviewPostMessage = new EventManager(LIVE_PREVIEW_CHANNEL_ID, { + target: window.parent, + debug: false, + }); +} + +export default livePreviewPostMessage; diff --git a/src/livePreview/eventManager/livePreviewEventManger.constant.ts b/src/livePreview/eventManager/livePreviewEventManger.constant.ts new file mode 100644 index 00000000..00ea7b06 --- /dev/null +++ b/src/livePreview/eventManager/livePreviewEventManger.constant.ts @@ -0,0 +1,8 @@ +export const LIVE_PREVIEW_POST_MESSAGE_EVENTS = { + INIT: "init", + ON_CHANGE: "client-data-send", + HISTORY: "history", + CHECK_ENTRY_PAGE: "check-entry-page", +} as const; + +export const LIVE_PREVIEW_CHANNEL_ID = "live-preview"; diff --git a/src/livePreview/eventManager/postMessageEvent.hooks.ts b/src/livePreview/eventManager/postMessageEvent.hooks.ts new file mode 100644 index 00000000..5d403edb --- /dev/null +++ b/src/livePreview/eventManager/postMessageEvent.hooks.ts @@ -0,0 +1,124 @@ +import packageJson from "../../../package.json"; +import Config, { setConfigFromParams } from "../../configManager/configManager"; +import { ILivePreviewWindowType } from "../../types/types"; +import livePreviewPostMessage from "./livePreviewEventManager"; + +import { LIVE_PREVIEW_POST_MESSAGE_EVENTS } from "./livePreviewEventManger.constant"; +import { + HistoryLivePreviewPostMessageEventData, + OnChangeLivePreviewPostMessageEventData, +} from "./types/livePreviewPostMessageEvent.type"; + +/** + * Registers a post message event listener for history-related events. + * The listener handles events for forward, backward, and reload actions on the browser history. + */ +export function useHistoryPostMessageEvent(): void { + livePreviewPostMessage?.on( + LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY, + (event) => { + switch (event.data.type) { + case "forward": { + window.history.forward(); + break; + } + case "backward": { + window.history.back(); + break; + } + case "reload": { + window.history.go(); + break; + } + default: { + const exhaustiveCheck: never = event.data.type; + throw new Error(`Unhandled event: ${exhaustiveCheck}`); + } + } + } + ); +} + +/** + * Registers a post message event listener for updating the entry in the live preview. + */ +export function useOnEntryUpdatePostMessageEvent(): void { + livePreviewPostMessage?.on( + LIVE_PREVIEW_POST_MESSAGE_EVENTS.ON_CHANGE, + (event) => { + const config = Config.get(); + + setConfigFromParams({ + live_preview: event.data.hash, + }); + + if (!config.ssr) { + const config = Config.get(); + config.onChange(); + } + } + ); +} + +export function sendInitializeLivePreviewPostMessageEvent(): void { + const config = Config.get(); + + livePreviewPostMessage + ?.send<{ + contentTypeUid: string; + entryUid: string; + windowType: ILivePreviewWindowType; + }>(LIVE_PREVIEW_POST_MESSAGE_EVENTS.INIT, { + config: { + shouldReload: config.ssr, + href: window.location.href, + sdkVersion: packageJson.version, + }, + }) + .then((data) => { + const { + contentTypeUid, + entryUid, + windowType = ILivePreviewWindowType.PREVIEW, + } = data; + + const stackDetails = Config.get().stackDetails; + + if (contentTypeUid && entryUid) { + stackDetails.contentTypeUid = contentTypeUid; + stackDetails.entryUid = entryUid; + } else { + // TODO: add debug logs that runs conditionally + // PublicLogger.debug( + // "init message did not contain contentTypeUid or entryUid." + // ); + } + + Config.set("stackDetails", stackDetails); + Config.set("windowType", windowType); + + // set timeout for client side (use to show warning: You are not editing this page) + if (!config.ssr) { + setInterval(() => { + sendCurrentPageUrlPostMessageEvent(); + }, 1500); + } + + useHistoryPostMessageEvent(); + useOnEntryUpdatePostMessageEvent(); + }) + .catch((e) => { + // TODO: add debug logs that runs conditionally + // PublicLogger.debug("Error while sending init message", e); + }); +} + +function sendCurrentPageUrlPostMessageEvent(): void { + livePreviewPostMessage + ?.send(LIVE_PREVIEW_POST_MESSAGE_EVENTS.CHECK_ENTRY_PAGE, { + href: window.location.href, + }) + .catch(() => { + // TODO: add debug logs that runs conditionally + }); +} diff --git a/src/livePreview/eventManager/types/livePreviewPostMessageEvent.type.ts b/src/livePreview/eventManager/types/livePreviewPostMessageEvent.type.ts new file mode 100644 index 00000000..f0bfe0f3 --- /dev/null +++ b/src/livePreview/eventManager/types/livePreviewPostMessageEvent.type.ts @@ -0,0 +1,7 @@ +export interface HistoryLivePreviewPostMessageEventData { + type: "forward" | "backward" | "reload"; +} + +export interface OnChangeLivePreviewPostMessageEventData { + hash: string; +} diff --git a/src/livePreview/live-preview.ts b/src/livePreview/live-preview.ts new file mode 100644 index 00000000..46264194 --- /dev/null +++ b/src/livePreview/live-preview.ts @@ -0,0 +1,128 @@ +import Config from "../configManager/configManager"; +import { PublicLogger } from "../logger/logger"; +import { ILivePreviewModeConfig } from "../types/types"; +import { addLivePreviewQueryTags } from "../utils"; +import { LivePreviewEditButton } from "./editButton/editButton"; +import { sendInitializeLivePreviewPostMessageEvent } from "./eventManager/postMessageEvent.hooks"; +import { removeDataCslp } from "./livePreviewProductionCleanup"; +import { removeFromOnChangeSubscribers } from "./removeFromOnChangeSubscribers"; +import { + OnEntryChangeCallback, + OnEntryChangeCallbackSubscribers, + OnEntryChangeCallbackUID, + OnEntryChangeUnsubscribeParameters, +} from "./types/onEntryChangeCallback.type"; + +export default class LivePreview { + /** + * @hideconstructor + */ + + private subscribers: OnEntryChangeCallbackSubscribers = {}; + + constructor() { + const config = Config.get(); + + this.requestDataSync = this.requestDataSync.bind(this); + this.subscribeToOnEntryChange = + this.subscribeToOnEntryChange.bind(this); + this.publish = this.publish.bind(this); + this.unsubscribeOnEntryChange = + this.unsubscribeOnEntryChange.bind(this); + + if (config.debug) { + PublicLogger.debug( + "Contentstack Live Preview Debugging mode: config --", + config + ); + } + + if (config.enable) { + if ( + typeof document !== undefined && + document.readyState === "complete" + ) { + this.requestDataSync(); + } else { + window.addEventListener("load", this.requestDataSync); + } + + // TODO: mjrf: Check if we need the second condition here. + // We are already handling the functions separately in the live editor. + // render the hover outline only when edit button enable + if ( + config.editButton.enable || + config.mode >= ILivePreviewModeConfig.EDITOR + ) { + new LivePreviewEditButton(); + } + + //NOTE - I think we are already handling the link click event here. Let's move it to a function. + if (config.ssr) { + // NOTE: what are we doing here? + window.addEventListener("load", (e) => { + const allATags = document.querySelectorAll("a"); + allATags.forEach((tag) => { + const docOrigin: string = document.location.origin; + if (tag.href && tag.href.includes(docOrigin)) { + const newUrl = addLivePreviewQueryTags(tag.href); + tag.href = newUrl; + } + }); + }); + + // Setting the query params to all the click events related to current domain + window.addEventListener("click", (event: any) => { + const target: any = event.target; + const targetHref: string | any = target.href; + const docOrigin: string = document.location.origin; + if ( + targetHref && + targetHref.includes(docOrigin) && + !targetHref.includes("live_preview") + ) { + const newUrl = addLivePreviewQueryTags(target.href); + event.target.href = newUrl || target.href; + } + }); + } + } else if (config.cleanCslpOnProduction) { + removeDataCslp(); + } + } + + // Request parent for data sync when document loads + private requestDataSync() { + const config = Config.get(); + + Config.set("onChange", this.publish); + + //! TODO: we replaced the handleOnChange() with this. + //! I don't think we need this. Confirm and remove it. + config.onChange(); + + sendInitializeLivePreviewPostMessageEvent(); + } + + subscribeToOnEntryChange( + callback: OnEntryChangeCallback, + callbackUid: OnEntryChangeCallbackUID + ): string { + this.subscribers[callbackUid] = callback; + return callbackUid; + } + + private publish(): void { + Object.values(this.subscribers).forEach( + (func) => { + func(); + } + ); + } + + unsubscribeOnEntryChange( + callback: OnEntryChangeUnsubscribeParameters + ): void { + removeFromOnChangeSubscribers(this.subscribers, callback); + } +} diff --git a/src/livePreview/livePreviewProductionCleanup.ts b/src/livePreview/livePreviewProductionCleanup.ts new file mode 100644 index 00000000..8cb85d4f --- /dev/null +++ b/src/livePreview/livePreviewProductionCleanup.ts @@ -0,0 +1,11 @@ +/** + * Removes the "data-cslp" and "data-cslp-button-position" attributes from all nodes with the "data-cslp" attribute. + */ +export function removeDataCslp(): void { + const nodes = document.querySelectorAll("[data-cslp]"); + + nodes.forEach((node) => { + node.removeAttribute("data-cslp"); + node.removeAttribute("data-cslp-button-position"); + }); +} diff --git a/src/livePreview/removeFromOnChangeSubscribers.ts b/src/livePreview/removeFromOnChangeSubscribers.ts new file mode 100644 index 00000000..c294c33c --- /dev/null +++ b/src/livePreview/removeFromOnChangeSubscribers.ts @@ -0,0 +1,33 @@ +import { PublicLogger } from "../logger/logger"; +import { + OnEntryChangeCallbackUID, + OnEntryChangeCallback, +} from "./types/onEntryChangeCallback.type"; + +export function removeFromOnChangeSubscribers( + callbackStack: { + [callbackUid: OnEntryChangeCallbackUID]: OnEntryChangeCallback; + }, + callback: OnEntryChangeCallbackUID | OnEntryChangeCallback +): void { + if (typeof callback === "string") { + if (!callbackStack[callback]) { + PublicLogger.warn("No subscriber found with the given id."); + } + delete callbackStack[callback]; + } else if (typeof callback === "function") { + const isCallbackDeleted = Object.entries<() => void>( + callbackStack + ).some(([uid, func]) => { + if (func === callback) { + delete callbackStack[uid]; + return true; + } + return false; + }); + + if (!isCallbackDeleted) { + PublicLogger.warn("No subscriber found with the given callback."); + } + } +} diff --git a/src/livePreview/types/onEntryChangeCallback.type.ts b/src/livePreview/types/onEntryChangeCallback.type.ts new file mode 100644 index 00000000..00d0e9e9 --- /dev/null +++ b/src/livePreview/types/onEntryChangeCallback.type.ts @@ -0,0 +1,16 @@ +export declare type OnEntryChangeCallback = () => void; + +export declare type OnEntryChangeConfig = { + skipInitialRender?: boolean; +}; + +export declare type OnEntryChangeCallbackUID = string; + +export type OnEntryChangeCallbackSubscribers = Record< + OnEntryChangeCallbackUID, + OnEntryChangeCallback +>; + +export type OnEntryChangeUnsubscribeParameters = + | OnEntryChangeCallbackUID + | OnEntryChangeCallback; diff --git a/src/utils/public-logger.ts b/src/logger/logger.ts similarity index 100% rename from src/utils/public-logger.ts rename to src/logger/logger.ts diff --git a/src/__test__/contentstack-live-preview-HOC.test.ts b/src/preview/__test__/contentstack-live-preview-HOC.test.ts similarity index 97% rename from src/__test__/contentstack-live-preview-HOC.test.ts rename to src/preview/__test__/contentstack-live-preview-HOC.test.ts index 9ec83ff2..73d5b729 100644 --- a/src/__test__/contentstack-live-preview-HOC.test.ts +++ b/src/preview/__test__/contentstack-live-preview-HOC.test.ts @@ -1,9 +1,9 @@ import ContentstackLivePreview from "../contentstack-live-preview-HOC"; -import { PublicLogger } from "../utils/public-logger"; -import { IInitData } from "../types/types"; -import { sendPostmessageToWindow } from "./utils"; -import packageJson from "../../package.json"; -import Config from "../utils/configHandler"; +import { PublicLogger } from "../../logger/logger"; +import { IInitData } from "../../types/types"; +import { sendPostmessageToWindow } from "../../__test__/utils"; +import packageJson from "../../../package.json"; +import Config from "../../configManager/configManager"; describe("Live preview HOC Callback Pub Sub", () => { afterEach(() => { diff --git a/src/preview/contentstack-live-preview-HOC.ts b/src/preview/contentstack-live-preview-HOC.ts new file mode 100644 index 00000000..bd23b55c --- /dev/null +++ b/src/preview/contentstack-live-preview-HOC.ts @@ -0,0 +1,227 @@ +import { v4 as uuidv4 } from "uuid"; + +import { isEmpty } from "lodash-es"; +import packageJson from "../../package.json"; +import { getUserInitData } from "../configManager/config.default"; +import Config, { updateConfigFromUrl } from "../configManager/configManager"; +import { VisualEditor } from "../liveEditor"; +import LivePreview from "../livePreview/live-preview"; +import { removeFromOnChangeSubscribers } from "../livePreview/removeFromOnChangeSubscribers"; +import { + OnEntryChangeCallback, + OnEntryChangeCallbackSubscribers, + OnEntryChangeCallbackUID, + OnEntryChangeConfig, + OnEntryChangeUnsubscribeParameters, +} from "../livePreview/types/onEntryChangeCallback.type"; +import { PublicLogger } from "../logger/logger"; +import { IInitData } from "../types/types"; + +class ContentstackLivePreview { + private static previewConstructors: + | { + livePreview: LivePreview; + liveEditor: VisualEditor; + } + | Record = {}; + + /** + * The subscribers for the onEntryChange event. We store them here when the SDK is not initialized. + */ + private static onEntryChangeCallbacks: OnEntryChangeCallbackSubscribers = + {}; + + /** + * Initializes the Live Preview SDK with the provided user configuration. + * If the SDK is already initialized, subsequent calls to this method will return the existing SDK instance. + * @param userConfig - The user configuration to initialize the SDK with. See {@link https://github.com/contentstack/live-preview-sdk/blob/main/docs/live-preview-configs.md#initconfig-iconfig|Live preview User config} for more details. + * @returns A promise that resolves to the constructors of the Live Preview SDK. + */ + static init( + userConfig: Partial = getUserInitData() + ): Promise { + // handle user config + Config.replace(userConfig); + updateConfigFromUrl(); + + if (typeof window === "undefined") { + PublicLogger.warn("The SDK is not initialized in the browser."); + return Promise.resolve(ContentstackLivePreview.previewConstructors); + } + + if (ContentstackLivePreview.isInitialized()) { + PublicLogger.warn( + "You have already initialized the Live Preview SDK. So, any subsequent initialization returns the existing SDK instance." + ); + return Promise.resolve(ContentstackLivePreview.previewConstructors); + } else { + return ContentstackLivePreview.initializePreview(); + } + } + + /** + * It is the live preview hash. + * This hash could be used when data is fetched manually. + */ + static get hash(): string { + if (!ContentstackLivePreview.isInitialized()) { + updateConfigFromUrl(); // check if we could extract from the URL + } + return Config.get().hash; + } + + private static isInitialized(): boolean { + return !isEmpty(ContentstackLivePreview.previewConstructors); + } + + private static initializePreview() { + ContentstackLivePreview.previewConstructors = { + livePreview: new LivePreview(), + liveEditor: new VisualEditor(), + }; + + // set up onEntryChange callbacks added when the SDK was not initialized + const livePreview = + ContentstackLivePreview.previewConstructors.livePreview; + Object.entries(ContentstackLivePreview.onEntryChangeCallbacks).forEach( + ([callbackUid, callback]) => { + livePreview.subscribeToOnEntryChange(callback, callbackUid); + } + ); + + ContentstackLivePreview.onEntryChangeCallbacks = {}; + + return Promise.resolve(ContentstackLivePreview.previewConstructors); + } + + /** + * Registers a callback function to be called when an entry changes. + * @param onChangeCallback The callback function to be called when an entry changes. + * @param config Optional configuration for the callback. + * @param config.skipInitialRender If true, the callback will not be called when it is first registered. + * @returns A unique identifier for the registered callback. + * + * @example + * ```js + * const callbackUid = ContentstackLivePreview.onEntryChange(() => { + * console.log("Entry changed"); + * }); + * + * // Unsubscribe the callback + * ContentstackLivePreview.unsubscribeOnEntryChange(callbackUid); + * ``` + */ + static onEntryChange( + onChangeCallback: OnEntryChangeCallback, + config: OnEntryChangeConfig = {} + ): OnEntryChangeCallbackUID { + const { skipInitialRender = false } = config; + + const callbackUid = uuidv4(); + + if (ContentstackLivePreview.isInitialized()) { + ContentstackLivePreview.previewConstructors.livePreview.subscribeToOnEntryChange( + onChangeCallback, + callbackUid + ); + } else { + ContentstackLivePreview.onEntryChangeCallbacks[callbackUid] = + onChangeCallback; + } + + if (!skipInitialRender) { + onChangeCallback(); + } + + return callbackUid; + } + + /** + * Registers a callback function to be called when there is a change in the entry being edited in live preview mode. The difference between this and `onEntryChange` is that this callback will not be called when it is first registered. + * @param onChangeCallback The callback function to be called when there is a change in the entry. + * @returns A unique identifier for the registered callback. + * + * @example + * ```js + * const callbackUid = ContentstackLivePreview.onLiveEdit(() => { + * console.log("Entry changed"); + * }); + * + * // Unsubscribe the callback + * ContentstackLivePreview.unsubscribeOnEntryChange(callbackUid); + * ``` + * + */ + static onLiveEdit( + onChangeCallback: OnEntryChangeCallback + ): OnEntryChangeCallbackUID { + return ContentstackLivePreview.onEntryChange(onChangeCallback, { + skipInitialRender: true, + }); + } + + /** + * Unsubscribes from the entry change event. + * @param callback - The callback function to be unsubscribed. + * + * @example + * ```js + * // unsubscribing using the Callback UID + * const callbackUid = ContentstackLivePreview.onEntryChange(() => { + * console.log("Entry changed"); + * }); + * + * // Unsubscribe the callback + * ContentstackLivePreview.unsubscribeOnEntryChange(callbackUid); + * ``` + * + * @example + * ```js + * // unsubscribing using the callback function + * const callback = () => {console.log("Entry changed")}; + * ContentstackLivePreview.onEntryChange(callback); + * + * // Unsubscribe the callback + * ContentstackLivePreview.unsubscribeOnEntryChange(callback); + * ``` + * + * @example + * ```js + * // The same is applicable to onLiveEdit + * const callbackUid = ContentstackLivePreview.onLiveEdit(() => { + * console.log("Entry changed"); + * }); + * + * // Unsubscribe the callback + * ContentstackLivePreview.unsubscribeOnEntryChange(callbackUid); + * ``` + * + * + */ + static unsubscribeOnEntryChange( + callback: OnEntryChangeUnsubscribeParameters + ): void { + if (!ContentstackLivePreview.isInitialized()) { + removeFromOnChangeSubscribers( + ContentstackLivePreview.onEntryChangeCallbacks, + callback + ); + return; + } + ContentstackLivePreview.previewConstructors.livePreview.unsubscribeOnEntryChange( + callback + ); + } + + /** + * Retrieves the version of the SDK. + * @returns The version of the SDK as a string. + */ + static getSdkVersion(): string { + return packageJson.version; + } +} + +export default ContentstackLivePreview; + +module.exports = ContentstackLivePreview; diff --git a/src/types/types.ts b/src/types/types.ts index 64928230..4ee5d026 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -70,6 +70,10 @@ export declare const enum ILivePreviewWindowType { export declare interface IConfig { ssr: boolean; enable: boolean; + /** + * @default false + */ + debug: boolean; cleanCslpOnProduction: boolean; stackDetails: IStackDetails; clientUrlParams: IClientUrlParams; @@ -106,6 +110,10 @@ export declare interface IInitData { ssr: boolean; runScriptsOnUpdate: boolean; enable: boolean; + /** + * @default false + */ + debug: boolean; cleanCslpOnProduction: boolean; stackDetails: Partial; clientUrlParams: Partial>; @@ -174,42 +182,6 @@ export declare interface ILivePreviewMessageCommon { from: "live-preview"; } -export type ILivePreviewReceivePostMessages = - | IClientDataMessage - | IInitAckMessage - | IHistoryMessage - | IDocWithScriptMessage; -export declare interface IClientDataMessage extends ILivePreviewMessageCommon { - type: "client-data-send"; - data: { - hash: string; - }; -} - -export declare interface IInitAckMessage extends ILivePreviewMessageCommon { - type: "init-ack"; - data: { - contentTypeUid: string; - entryUid: string; - windowType: ILivePreviewWindowType; - }; -} - -export declare interface IHistoryMessage extends ILivePreviewMessageCommon { - type: "history"; - data: { - type: "forward" | "backward" | "reload"; - }; -} - -export declare interface IDocWithScriptMessage - extends ILivePreviewMessageCommon { - type: "document-body-post-scripts-loaded"; - data: { - body: string; - }; -} - export declare interface IEditButtonPosition { upperBoundOfTooltip: number; leftBoundOfTooltip: number; @@ -217,14 +189,6 @@ export declare interface IEditButtonPosition { // end of Post message types -export declare type OnEntryChangeCallback = () => void; - -export declare type OnEntryChangeConfig = { - skipInitialRender?: boolean; -}; - -export declare type OnEntryChangeCallbackUID = string; - export interface IVisualEditorInitEvent { windowType: ILivePreviewWindowType; stackDetails: { diff --git a/src/utils/__test__/index.test.ts b/src/utils/__test__/index.test.ts index b53c2c25..d09f1b90 100644 --- a/src/utils/__test__/index.test.ts +++ b/src/utils/__test__/index.test.ts @@ -1,246 +1,7 @@ -import { DOMRect } from "../../__test__/utils"; -import { - createMultipleEditButton, - createSingularEditButton, - getEditButtonPosition, - hasWindow, -} from "../index"; - -let editCallback: jest.Mock | undefined; -let linkCallback: jest.Mock | undefined; -let editButtonForHyperlink: HTMLDivElement | undefined; -let editButtonForNonHyperlink: HTMLDivElement | undefined; -let tooltipChild: HTMLCollectionOf | undefined; - -describe("Edit button for Link", () => { - beforeEach(() => { - editCallback = jest.fn(); - linkCallback = jest.fn(); - - editButtonForHyperlink = createMultipleEditButton( - editCallback, - linkCallback - ); - - document.body.appendChild(editButtonForHyperlink); - - tooltipChild = document.getElementsByClassName( - "cslp-tooltip-child" - ) as HTMLCollectionOf; - }); - - afterEach(() => { - editCallback = undefined; - linkCallback = undefined; - editButtonForHyperlink = undefined; - tooltipChild = undefined; - - document.body.innerHTML = ""; - }); - - test("edit button must have 2 separate button", () => { - expect(tooltipChild).toHaveLength(2); - }); - - test("runs edit callback on button click", () => { - if ( - !( - editCallback && - linkCallback && - editButtonForHyperlink && - tooltipChild - ) - ) - fail("missing dependencies"); - - const editButton = tooltipChild[0]; - editButton.click(); - expect(editCallback).toBeCalled(); - }); - - test("runs link callback on button click", () => { - if ( - !( - editCallback && - linkCallback && - editButtonForHyperlink && - tooltipChild - ) - ) - fail("missing dependencies"); - - const linkButton = tooltipChild[1]; - linkButton.click(); - expect(linkCallback).toBeCalled(); - }); -}); +import { hasWindow } from "../index"; describe("hasWindow() function", () => { test("must check if window is available", () => { expect(hasWindow()).toBe(typeof window !== "undefined"); }); }); - -describe("Edit button", () => { - beforeEach(() => { - editCallback = jest.fn(); - - editButtonForNonHyperlink = createSingularEditButton(editCallback); - - document.body.appendChild(editButtonForNonHyperlink); - - tooltipChild = document.getElementsByClassName( - "cslp-tooltip-child" - ) as HTMLCollectionOf; - }); - - afterEach(() => { - editCallback = undefined; - linkCallback = undefined; - editButtonForNonHyperlink = undefined; - tooltipChild = undefined; - - document.body.innerHTML = ""; - }); - - test("runs edit callback when clicked", () => { - if (!(editCallback && editButtonForNonHyperlink && tooltipChild)) - fail("missing dependencies"); - - const editButton = tooltipChild[0]; - editButton.click(); - expect(editCallback).toBeCalled(); - }); -}); - -describe("getEditButtonPosition: Edit button", () => { - beforeAll(() => { - const titlePara = document.createElement("h3"); - titlePara.setAttribute("data-test-id", "title-para"); - if (titlePara) { - titlePara.getBoundingClientRect = jest.fn( - () => new DOMRect(53, 75, 1529, 10) - ); - } - - document.body.appendChild(titlePara); - }); - - afterAll(() => { - document.getElementsByTagName("html")[0].innerHTML = ""; - jest.clearAllMocks(); - }); - - test("should be positioned on top of hovered element", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "top") - ).toStrictEqual({ upperBoundOfTooltip: 36.75, leftBoundOfTooltip: 53 }); - }); - - test("should be positioned on top-left of hovered element", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "top-left") - ).toStrictEqual({ upperBoundOfTooltip: 36.75, leftBoundOfTooltip: 53 }); - }); - - test("should be positioned on top-center of hovered element", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "top-center") - ).toStrictEqual({ - upperBoundOfTooltip: 36.75, - leftBoundOfTooltip: 786.5, - }); - }); - - test("should be positioned on top-right of hovered element", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "top-right") - ).toStrictEqual({ - upperBoundOfTooltip: 36.75, - leftBoundOfTooltip: 1515, - }); - }); - - test("should be positioned on right of hovered element", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "right") - ).toStrictEqual({ - upperBoundOfTooltip: 71.75, - leftBoundOfTooltip: 1592, - }); - }); - - test("should be positioned on bottom-right of hovered element", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "bottom-right") - ).toStrictEqual({ - upperBoundOfTooltip: 114.75, - leftBoundOfTooltip: 1515, - }); - }); - - test("should be positioned on bottom-center of hovered element", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "bottom-center") - ).toStrictEqual({ - upperBoundOfTooltip: 114.75, - leftBoundOfTooltip: 786.5, - }); - }); - - test("should be positioned on bottom-left of hovered element", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "bottom-left") - ).toStrictEqual({ - upperBoundOfTooltip: 114.75, - leftBoundOfTooltip: 53, - }); - }); - - test("should be positioned on bottom of hovered element", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "bottom") - ).toStrictEqual({ - upperBoundOfTooltip: 114.75, - leftBoundOfTooltip: 53, - }); - }); - - test("should be positioned on left of hovered element", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "left") - ).toStrictEqual({ - upperBoundOfTooltip: 71.75, - leftBoundOfTooltip: -19, - }); - }); - - test("should override the default position if position attribute is present", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - titlePara?.setAttribute("data-cslp-button-position", "top-center"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "top-left") - ).toStrictEqual({ - upperBoundOfTooltip: 36.75, - leftBoundOfTooltip: 786.5, - }); - }); - - test("should positioned on top-left if the passed position is not valid ", async () => { - const titlePara = document.querySelector("[data-test-id='title-para']"); - titlePara?.setAttribute("data-cslp-button-position", "random-string"); - expect( - getEditButtonPosition(titlePara as HTMLElement, "top-left") - ).toStrictEqual({ upperBoundOfTooltip: 36.75, leftBoundOfTooltip: 53 }); - }); -}); diff --git a/src/utils/__test__/replaceHtml.test.ts b/src/utils/__test__/replaceHtml.test.ts deleted file mode 100644 index e7f6fc86..00000000 --- a/src/utils/__test__/replaceHtml.test.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { - replaceDocumentBody, - rerunScriptsInDocument, - updateDocumentBody, -} from "../replaceHtml"; - -describe("replaceDocumentBody", () => { - const receivedHTML = ` -

The title is new

-

hello I am updated

- `; - beforeEach(() => { - document.body.innerHTML = ` -

The title is old

-

hello I am not updated

- `; - }); - - afterEach(() => { - document.body.innerHTML = ""; - }); - - test("should replace HTML Body", () => { - replaceDocumentBody(receivedHTML); - - expect(document.body.innerHTML.trim()).toBe(receivedHTML.trim()); - }); - test("should run user script after update", () => { - const onPostOperation = jest.fn(); - replaceDocumentBody(receivedHTML, onPostOperation); - - expect(onPostOperation).toHaveBeenCalled(); - }); -}); - -describe("updateDocumentBody", () => { - let receivedHTML: string; - beforeEach(() => { - const backTickedString = "hello I am `back ticked`"; - document.body.innerHTML = ` -

The title is old

-

hello I am not updated

- `; - - receivedHTML = ` -

The title is new

-

hello I am not updated

- - `; - }); - - afterEach(() => { - receivedHTML = ""; - //@ts-ignore - // window.scriptRanTimes = undefined; - document.body.innerHTML = ""; - }); - - test("should just update the body without rerunning scripts when shouldReRunScripts is false", () => { - updateDocumentBody(document, receivedHTML, { - shouldReRunScripts: false, - onPostOperation: () => {}, - }); - - expect(document.body.innerHTML.trim()).toBe(receivedHTML.trim()); - //@ts-ignore - // expect(window.scriptRanTimes).toBe(0); - }); - - test("should run scripts with the new body when shouldReRunScripts is true", async () => { - updateDocumentBody(document, receivedHTML, { - shouldReRunScripts: true, - onPostOperation: () => {}, - }); - - await new Promise((r) => setTimeout(r, 100)); - //@ts-ignore - // expect(window.scriptRanTimes).toBe(2); - }); - test("should remove existing iframe when shouldReRunScripts is true", () => { - const cslpIframe = document.createElement("iframe"); - cslpIframe.id = "contentstack-live-preview-iframe"; - document.body.appendChild(cslpIframe); - - expect( - document.querySelectorAll("iframe#contentstack-live-preview-iframe") - .length - ).toBe(1); - - updateDocumentBody(document, receivedHTML, { - shouldReRunScripts: true, - onPostOperation: () => {}, - }); - - setTimeout(() => { - expect( - document.querySelectorAll( - "iframe#contentstack-live-preview-iframe" - ).length - ).toBe(0); - }, 5); - }); -}); - -describe("rerunScriptsInDocument", () => { - beforeEach(() => { - document.body.innerHTML = ` -

The title is old

-

hello I am not updated

- - `; - }); - afterEach(() => { - document.body.innerHTML = ""; - //@ts-ignore - window.scriptRan = undefined; - }); - test("should run internal scripts", async () => { - rerunScriptsInDocument(); - - //@ts-ignore - expect(window.scriptRan).toBe(1); - }); - - test("should run external script", () => { - document.body.innerHTML = ` -

The title is old

-

hello I am not updated

- - - `; - - rerunScriptsInDocument(); - - //@ts-ignore - expect(window.scriptRan).toBe(1); - }); - - test("should trigger document-body-post-scripts-loaded with correct body", async () => { - jest.spyOn(window, "postMessage"); - - rerunScriptsInDocument(); - - expect(window.postMessage).toBeCalledWith( - { - from: "live-preview", - type: "document-body-post-scripts-loaded", - data: { - body: ` -

The title is old

-

hello I am not updated

- - `, - }, - }, - "*" - ); - }); -}); diff --git a/src/utils/index.ts b/src/utils/index.ts index a690b25e..70bb2a4a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,92 +1,8 @@ -import { PublicLogger } from "./public-logger"; -import { IConfigEditButton, IEditButtonPosition } from "../types/types"; +import { PublicLogger } from "../logger/logger"; export function hasWindow(): boolean { return typeof window !== "undefined"; } - -export const tooltipSingularInnerHtml = `
- - - - Edit -
`; - -export const tooltipMultipleInnerHtml = `
- - - - -
- `; - -export const createSingularEditButton = ( - editCallback: (e: MouseEvent) => void -): HTMLDivElement => { - const singularEditButton = document.createElement("div"); - singularEditButton.classList.add("cslp-tooltip-child", "singular"); - singularEditButton.setAttribute( - "data-test-id", - "cslp-singular-edit-button" - ); - singularEditButton.innerHTML = ` - - - Edit`; - - singularEditButton.addEventListener("click", editCallback); - - return singularEditButton; -}; - -export const createMultipleEditButton = ( - editCallback: (e: MouseEvent) => void, - linkCallback: (e: MouseEvent) => void -): HTMLDivElement => { - const multipleEditButton = document.createElement("div"); - multipleEditButton.classList.add("cslp-tooltip-child"); - multipleEditButton.setAttribute("data-title", "Edit"); - multipleEditButton.setAttribute( - "data-test-id", - "cslp-multiple-edit-button" - ); - multipleEditButton.innerHTML = ` - - - `; - - multipleEditButton.addEventListener("click", editCallback); - - const multipleExternalLinkButton = document.createElement("div"); - multipleExternalLinkButton.classList.add("cslp-tooltip-child"); - multipleExternalLinkButton.setAttribute("data-title", "Go to link"); - multipleExternalLinkButton.setAttribute( - "data-test-id", - "cslp-multiple-external-link-button" - ); - multipleExternalLinkButton.innerHTML = ` - - - `; - - multipleExternalLinkButton.addEventListener("click", linkCallback); - - const multipleEditFragment = document.createDocumentFragment(); - multipleEditFragment.appendChild(multipleEditButton); - multipleEditFragment.appendChild(multipleExternalLinkButton); - - const multipleDiv = document.createElement("div"); - multipleDiv.appendChild(multipleEditFragment); - multipleDiv.classList.add("multiple"); - - return multipleDiv; -}; - export function addLivePreviewQueryTags(link: string): string { try { const docUrl: URL = new URL(document.location.href); @@ -107,176 +23,3 @@ export function addLivePreviewQueryTags(link: string): string { return link; } } - -function inIframe() { - return window.location !== window.parent.location; -} - -export function shouldRenderEditButton(editButton: IConfigEditButton): boolean { - if (!editButton.enable) { - if (editButton.enable === undefined) - PublicLogger.error( - "enable key is required inside editButton object" - ); - return false; - } - - // return boolean in case of cslp-buttons query added in url - try { - const currentLocation = new URL(window.location.href); - const cslpButtonQueryValue = - currentLocation.searchParams.get("cslp-buttons"); - if ( - cslpButtonQueryValue !== null && - editButton.includeByQueryParameter !== false - ) - return cslpButtonQueryValue === "false" ? false : true; - } catch (error) { - PublicLogger.error(error); - } - - // case if inside live preview - if ( - inIframe() && - editButton.exclude?.find( - (exclude) => exclude === "insideLivePreviewPortal" - ) - ) { - return false; - } else if (inIframe()) { - return true; - } - - // case outside live preview - if ( - editButton.exclude?.find( - (exclude) => exclude === "outsideLivePreviewPortal" - ) - ) { - return false; - } - - // Priority list => 1. cslpEditButton query value 2. Inside live preview 3. renderCslpButtonByDefault value selected by user - return true; -} - -export function getEditButtonPosition( - currentHoveredElement: HTMLElement | null, - defaultPosition: string | undefined -): IEditButtonPosition { - if (!currentHoveredElement) - return { upperBoundOfTooltip: 0, leftBoundOfTooltip: 0 }; - - const cslpButtonPosition = currentHoveredElement.getAttribute( - "data-cslp-button-position" - ); - if (cslpButtonPosition) { - return calculateEditButtonPosition( - currentHoveredElement, - cslpButtonPosition - ); - } - - // NOTE: position "top" and "top-left" will be the position of edit button if no default position passed in config - return calculateEditButtonPosition( - currentHoveredElement, - defaultPosition || "top" - ); -} - -function calculateEditButtonPosition( - currentHoveredElement: HTMLElement, - cslpButtonPosition: string -): IEditButtonPosition { - const editButtonPosition: IEditButtonPosition = { - upperBoundOfTooltip: 0, - leftBoundOfTooltip: 0, - }; - const currentRectOfElement = currentHoveredElement.getBoundingClientRect(); - try { - const buttonMeasurementValues = { - width: 72, - halfWidth: 36, - height: 40, - basicMargin: 5, - widthWithMargin: 77, - }; - - switch (cslpButtonPosition) { - case "top-center": - editButtonPosition.upperBoundOfTooltip = - currentRectOfElement.top - buttonMeasurementValues.height; - editButtonPosition.leftBoundOfTooltip = - currentRectOfElement.width / 2 - - buttonMeasurementValues.halfWidth + - currentRectOfElement.left; - break; - case "top-right": - editButtonPosition.upperBoundOfTooltip = - currentRectOfElement.top - buttonMeasurementValues.height; - editButtonPosition.leftBoundOfTooltip = - currentRectOfElement.right - buttonMeasurementValues.width; - break; - case "right": - editButtonPosition.upperBoundOfTooltip = - currentRectOfElement.top - - buttonMeasurementValues.basicMargin; - editButtonPosition.leftBoundOfTooltip = - currentRectOfElement.right + - buttonMeasurementValues.basicMargin; - break; - case "bottom": - editButtonPosition.upperBoundOfTooltip = - currentRectOfElement.bottom + - buttonMeasurementValues.basicMargin; - editButtonPosition.leftBoundOfTooltip = - currentRectOfElement.left - - buttonMeasurementValues.basicMargin; - break; - case "bottom-left": - editButtonPosition.upperBoundOfTooltip = - currentRectOfElement.bottom + - buttonMeasurementValues.basicMargin; - editButtonPosition.leftBoundOfTooltip = - currentRectOfElement.left - - buttonMeasurementValues.basicMargin; - break; - case "bottom-center": - editButtonPosition.upperBoundOfTooltip = - currentRectOfElement.bottom + - buttonMeasurementValues.basicMargin; - editButtonPosition.leftBoundOfTooltip = - currentRectOfElement.width / 2 - - buttonMeasurementValues.halfWidth + - currentRectOfElement.left; - break; - case "bottom-right": - editButtonPosition.upperBoundOfTooltip = - currentRectOfElement.bottom + - buttonMeasurementValues.basicMargin; - editButtonPosition.leftBoundOfTooltip = - currentRectOfElement.right - buttonMeasurementValues.width; - break; - case "left": - editButtonPosition.upperBoundOfTooltip = - currentRectOfElement.top - - buttonMeasurementValues.basicMargin; - editButtonPosition.leftBoundOfTooltip = - currentRectOfElement.left - - buttonMeasurementValues.widthWithMargin; - break; - // default position => top, top-left or any other string - default: - editButtonPosition.upperBoundOfTooltip = - currentRectOfElement.top - buttonMeasurementValues.height; - editButtonPosition.leftBoundOfTooltip = - currentRectOfElement.left - - buttonMeasurementValues.basicMargin; - break; - } - return editButtonPosition; - } catch (error) { - PublicLogger.error(error); - return editButtonPosition; - } -} diff --git a/src/utils/onCookieChange.ts b/src/utils/onCookieChange.ts deleted file mode 100644 index ef40a450..00000000 --- a/src/utils/onCookieChange.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * An event listener for cookie changes. - * @param {Function} callback A callback function to be called when the cookie changes - * @param {number} interval number of milliseconds to wait between each check - */ - -export function onCookieChange( - callback: (event: { - previousCookies: string; - currentCookies: string; - }) => void, - interval = 1000 -) { - let lastCookie = document.cookie; - setInterval(() => { - let cookie = document.cookie; - if (cookie !== lastCookie) { - try { - callback({ - previousCookies: lastCookie, - currentCookies: cookie, - }); - } finally { - lastCookie = cookie; - } - } - }, interval); -} diff --git a/src/utils/replaceHtml.ts b/src/utils/replaceHtml.ts deleted file mode 100644 index 8c421ed8..00000000 --- a/src/utils/replaceHtml.ts +++ /dev/null @@ -1,134 +0,0 @@ -/* eslint-disable no-useless-escape */ - -import morphdom from "morphdom"; - -export function rerunScriptsInDocument(): void { - const scriptsInTheDOM = document.querySelectorAll("script"); - let scriptsLeftToExecute = scriptsInTheDOM.length; - - let scriptIndex = 0; - - function isScriptRemaining() { - return scriptIndex < scriptsInTheDOM.length; - } - - function decrementNumberOfScriptsLeft() { - scriptsLeftToExecute--; - - if (scriptsLeftToExecute === 0) { - window.parent.postMessage( - { - from: "live-preview", - type: "document-body-post-scripts-loaded", - data: { - body: document.body.outerHTML, - }, - }, - "*" - ); - } - } - - function postScriptExecute() { - scriptIndex++; - decrementNumberOfScriptsLeft(); - executeNextScript(); - } - function executeNextScript() { - if (isScriptRemaining()) { - const currentScript = scriptsInTheDOM[scriptIndex]; - - const parent = currentScript.parentNode; - const newScript = document.createElement("script"); - - if (currentScript.src) { - newScript.onerror = postScriptExecute; - newScript.onload = postScriptExecute; - newScript.src = currentScript.src; - } else { - newScript.text = currentScript.text; - postScriptExecute(); - } - parent?.replaceChild(newScript, currentScript); - } - } - - if (scriptsInTheDOM.length) { - executeNextScript(); - } -} - -export async function updateDocumentBody( - document: Document, - receivedHTML: string, - options: { - shouldReRunScripts: boolean; - onPostOperation: () => void; - } -): Promise { - const { shouldReRunScripts, onPostOperation } = options; - - if (!shouldReRunScripts) { - replaceDocumentBody(receivedHTML, onPostOperation); - } else { - const existingIframes = document.querySelectorAll( - "iframe#contentstack-live-preview-iframe" - ); - - existingIframes.forEach((existingFrame) => { - existingFrame.remove(); - }); - - const iframe = document.createElement("iframe"); - iframe.style.display = "none"; - iframe.id = "contentstack-live-preview-iframe"; - - // escape backticks and script tags - const SCRIPT_AND_BACKTICKS = /(<\s*\/\s*script\s*>)|(`)/gm; - - const escapedHTML = receivedHTML.replace( - SCRIPT_AND_BACKTICKS, - (_substring, _scriptTag, backTick) => { - if (backTick) { - return "\\`"; - } else { - return "<\\/script>"; - } - } - ); - - iframe.srcdoc = ` - -