From be38607698b12309af5b79259afbbf037e7027bc Mon Sep 17 00:00:00 2001
From: Thomas Zemp
Date: Wed, 21 Dec 2022 09:44:00 +0100
Subject: [PATCH 01/10] feat: ideas for plugin wrappers [LIBS-397]
---
runtime/package.json | 8 ++++--
runtime/src/PluginSender.tsx | 53 +++++++++++++++++++++++++++++++++++
runtime/src/PluginWrapper.tsx | 43 ++++++++++++++++++++++++++++
runtime/src/index.ts | 4 +++
yarn.lock | 49 ++++++++++++++++++++++++++++++++
5 files changed, 155 insertions(+), 2 deletions(-)
create mode 100644 runtime/src/PluginSender.tsx
create mode 100644 runtime/src/PluginWrapper.tsx
diff --git a/runtime/package.json b/runtime/package.json
index 5be1ff035..abbda9628 100644
--- a/runtime/package.json
+++ b/runtime/package.json
@@ -23,10 +23,11 @@
"build/**"
],
"dependencies": {
+ "@dhis2/app-service-alerts": "3.7.0",
"@dhis2/app-service-config": "3.7.0",
"@dhis2/app-service-data": "3.7.0",
- "@dhis2/app-service-alerts": "3.7.0",
- "@dhis2/app-service-offline": "3.7.0"
+ "@dhis2/app-service-offline": "3.7.0",
+ "post-robot": "^10.0.46"
},
"peerDependencies": {
"prop-types": "^15.7.2",
@@ -38,5 +39,8 @@
"build:package": "d2-app-scripts build",
"build": "concurrently -n build,types \"yarn build:package\" \"yarn build:types\"",
"test": "echo \"No tests yet!\""
+ },
+ "devDependencies": {
+ "@types/post-robot": "^10.0.3"
}
}
diff --git a/runtime/src/PluginSender.tsx b/runtime/src/PluginSender.tsx
new file mode 100644
index 000000000..3e16bcd6b
--- /dev/null
+++ b/runtime/src/PluginSender.tsx
@@ -0,0 +1,53 @@
+import postRobot from 'post-robot'
+import React, { useEffect, useRef, useState } from 'react'
+
+export const PluginSender = ({
+ pluginSource,
+ ...propsToPass
+}: {
+ pluginSource: string
+ propsToPass: any
+}): JSX.Element => {
+ const iframeRef = useRef()
+
+ const [missingProps, setMissingProps] = useState(null)
+
+ useEffect(() => {
+ if (iframeRef?.current) {
+ const iframeProps = { ...propsToPass, setMissingProps }
+ const listener = postRobot.on(
+ 'getPropsFromParent',
+ // listen for messages coming only from the iframe rendered by this component
+ { window: iframeRef.current.contentWindow },
+ (): any => {
+ return iframeProps
+ }
+ )
+
+ return () => listener.cancel()
+ }
+ }, [propsToPass])
+
+ if (missingProps && missingProps?.length > 0) {
+ return (
+ {`Plugin could not load because required props are missing: ${missingProps.join()}`}
+ )
+ }
+
+ return (
+
+ {pluginSource ? (
+
+ ) : null}
+
+ )
+}
diff --git a/runtime/src/PluginWrapper.tsx b/runtime/src/PluginWrapper.tsx
new file mode 100644
index 000000000..713c44296
--- /dev/null
+++ b/runtime/src/PluginWrapper.tsx
@@ -0,0 +1,43 @@
+import postRobot from 'post-robot'
+import { useEffect, useState } from 'react'
+
+export const PluginWrapper = ({
+ requiredProps,
+ children,
+}: {
+ requiredProps: [string]
+ children: any
+}): any => {
+ const [propsFromParent, setPropsFromParent] = useState()
+
+ const receivePropsFromParent = (event: any) => {
+ const { data: receivedProps } = event
+ const { setMissingProps, ...explictlyPassedProps } = receivedProps
+
+ setPropsFromParent(explictlyPassedProps)
+
+ // check for required props
+ const missingProps = requiredProps?.filter(
+ (prop) => !receivedProps[prop]
+ )
+ if (missingProps && missingProps.length > 0) {
+ setMissingProps(missingProps)
+ console.error(
+ `The following required props were not provided: ${missingProps.join(
+ ','
+ )}`
+ )
+ } else {
+ setMissingProps([])
+ }
+ }
+
+ useEffect(() => {
+ postRobot
+ .send(window.top, 'getPropsFromParent')
+ .then(receivePropsFromParent)
+ .catch((err: Error) => console.error(err))
+ })
+
+ return children({ ...propsFromParent })
+}
diff --git a/runtime/src/index.ts b/runtime/src/index.ts
index a3f580d98..06048f496 100644
--- a/runtime/src/index.ts
+++ b/runtime/src/index.ts
@@ -22,4 +22,8 @@ export {
clearSensitiveCaches,
} from '@dhis2/app-service-offline'
+export { PluginSender } from './PluginSender'
+
+export { PluginWrapper } from './PluginWrapper'
+
export { Provider } from './Provider'
diff --git a/yarn.lock b/yarn.lock
index 3a51d5723..a3b53d947 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2440,6 +2440,11 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
+"@types/post-robot@^10.0.3":
+ version "10.0.3"
+ resolved "https://registry.yarnpkg.com/@types/post-robot/-/post-robot-10.0.3.tgz#d1429085f2faf4c87f841dab4e51472457edbf31"
+ integrity sha512-y8ysuxddaG8V/oA1Ay6Err7nSADRa9Bv1rl0ZQpJ0qgdIQ7ks3CHcOsYL4qE8w75+/XYDS94dBeXDs0xexm3tA==
+
"@types/prettier@^2.0.0":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.1.tgz#374e31645d58cb18a07b3ecd8e9dede4deb2cccd"
@@ -3805,6 +3810,15 @@ bcryptjs@^2.3.0:
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
+belter@^1.0.41:
+ version "1.0.190"
+ resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
+ integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
+ dependencies:
+ cross-domain-safe-weakmap "^1"
+ cross-domain-utils "^2"
+ zalgo-promise "^1"
+
bfj@^7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.0.2.tgz#1988ce76f3add9ac2913fd8ba47aad9e651bfbb2"
@@ -5011,6 +5025,20 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
+cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
+ version "1.0.29"
+ resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
+ integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
+ dependencies:
+ cross-domain-utils "^2.0.0"
+
+cross-domain-utils@^2, cross-domain-utils@^2.0.0:
+ version "2.0.38"
+ resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
+ integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
+ dependencies:
+ zalgo-promise "^1.0.11"
+
cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -11503,6 +11531,17 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+post-robot@^10.0.46:
+ version "10.0.46"
+ resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
+ integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
+ dependencies:
+ belter "^1.0.41"
+ cross-domain-safe-weakmap "^1.0.1"
+ cross-domain-utils "^2.0.0"
+ universal-serialize "^1.0.4"
+ zalgo-promise "^1.0.3"
+
postcss-attribute-case-insensitive@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
@@ -15086,6 +15125,11 @@ unique-string@^1.0.0:
dependencies:
crypto-random-string "^1.0.0"
+universal-serialize@^1.0.4:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
+ integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==
+
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -16065,6 +16109,11 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
+ version "1.0.48"
+ resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
+ integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==
+
zip-stream@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.3.tgz#26cc4bdb93641a8590dd07112e1f77af1758865b"
From 30c963c112b2865ae824c7e3ce06279ed374983c Mon Sep 17 00:00:00 2001
From: Thomas Zemp
Date: Mon, 23 Jan 2023 14:05:33 +0100
Subject: [PATCH 02/10] feat: update plugin wrappers
---
examples/cra/package.json | 3 +-
examples/cra/yarn.lock | 94 ++++++++++++++++---
examples/query-playground/package.json | 3 +-
examples/query-playground/yarn.lock | 96 ++++++++++++++++---
runtime/package.json | 2 +
runtime/src/PluginSender.tsx | 53 -----------
runtime/src/PluginWrapper.tsx | 43 ---------
runtime/src/index.ts | 4 +-
services/plugin/.gitignore | 5 +
services/plugin/README.md | 11 +++
services/plugin/d2.config.js | 9 ++
services/plugin/jest.config.js | 10 ++
services/plugin/package.json | 41 ++++++++
services/plugin/src/PluginError.tsx | 58 ++++++++++++
services/plugin/src/PluginSender.tsx | 125 +++++++++++++++++++++++++
services/plugin/src/PluginWrapper.tsx | 69 ++++++++++++++
services/plugin/src/index.ts | 3 +
services/plugin/src/setupRTL.ts | 5 +
services/plugin/src/types.ts | 1 +
services/plugin/tsconfig.json | 10 ++
20 files changed, 517 insertions(+), 128 deletions(-)
delete mode 100644 runtime/src/PluginSender.tsx
delete mode 100644 runtime/src/PluginWrapper.tsx
create mode 100644 services/plugin/.gitignore
create mode 100644 services/plugin/README.md
create mode 100644 services/plugin/d2.config.js
create mode 100644 services/plugin/jest.config.js
create mode 100644 services/plugin/package.json
create mode 100644 services/plugin/src/PluginError.tsx
create mode 100644 services/plugin/src/PluginSender.tsx
create mode 100644 services/plugin/src/PluginWrapper.tsx
create mode 100644 services/plugin/src/index.ts
create mode 100644 services/plugin/src/setupRTL.ts
create mode 100644 services/plugin/src/types.ts
create mode 100644 services/plugin/tsconfig.json
diff --git a/examples/cra/package.json b/examples/cra/package.json
index 4553c9729..e7eec2caf 100644
--- a/examples/cra/package.json
+++ b/examples/cra/package.json
@@ -16,7 +16,8 @@
"@dhis2/app-service-alerts": "file:../../services/alerts",
"@dhis2/app-service-config": "file:../../services/config",
"@dhis2/app-service-data": "file:../../services/data",
- "@dhis2/app-service-offline": "file:../../services/offline"
+ "@dhis2/app-service-offline": "file:../../services/offline",
+ "@dhis2/app-service-plugin": "file:../../services/plugin"
},
"scripts": {
"start": "react-scripts start",
diff --git a/examples/cra/yarn.lock b/examples/cra/yarn.lock
index 1db982bee..ae729ea73 100644
--- a/examples/cra/yarn.lock
+++ b/examples/cra/yarn.lock
@@ -1054,29 +1054,43 @@
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
"@dhis2/app-runtime@file:../../runtime":
- version "3.5.0"
+ version "3.7.0"
dependencies:
- "@dhis2/app-service-alerts" "3.5.0"
- "@dhis2/app-service-config" "3.5.0"
- "@dhis2/app-service-data" "3.5.0"
- "@dhis2/app-service-offline" "3.5.0"
+ "@dhis2/app-service-alerts" "3.7.0"
+ "@dhis2/app-service-config" "3.7.0"
+ "@dhis2/app-service-data" "3.7.0"
+ "@dhis2/app-service-offline" "3.7.0"
+ "@dhis2/app-service-plugin" "3.7.0"
+ "@dhis2/d2-i18n" "^1.1.0"
+ post-robot "^10.0.46"
-"@dhis2/app-service-alerts@3.5.0", "@dhis2/app-service-alerts@file:../../services/alerts":
- version "3.5.0"
+"@dhis2/app-service-alerts@3.7.0", "@dhis2/app-service-alerts@file:../../services/alerts":
+ version "3.7.0"
-"@dhis2/app-service-config@3.5.0", "@dhis2/app-service-config@file:../../services/config":
- version "3.5.0"
+"@dhis2/app-service-config@3.7.0", "@dhis2/app-service-config@file:../../services/config":
+ version "3.7.0"
-"@dhis2/app-service-data@3.5.0", "@dhis2/app-service-data@file:../../services/data":
- version "3.5.0"
+"@dhis2/app-service-data@3.7.0", "@dhis2/app-service-data@file:../../services/data":
+ version "3.7.0"
dependencies:
react-query "^3.13.11"
-"@dhis2/app-service-offline@3.5.0", "@dhis2/app-service-offline@file:../../services/offline":
- version "3.5.0"
+"@dhis2/app-service-offline@3.7.0", "@dhis2/app-service-offline@file:../../services/offline":
+ version "3.7.0"
dependencies:
lodash "^4.17.21"
+"@dhis2/app-service-plugin@3.7.0", "@dhis2/app-service-plugin@file:../../services/plugin":
+ version "3.7.0"
+
+"@dhis2/d2-i18n@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@dhis2/d2-i18n/-/d2-i18n-1.1.0.tgz#ec777c5091f747e4c5aa4f9801c62ba4d1ef3d16"
+ integrity sha512-x3u58goDQsMfBzy50koxNrJjofJTtjRZOfz6f6Py/wMMJfp/T6vZjWMQgcfWH0JrV6d04K1RTt6bI05wqsVQvg==
+ dependencies:
+ i18next "^10.3"
+ moment "^2.24.0"
+
"@hapi/address@2.x.x":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
@@ -2259,6 +2273,15 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
+belter@^1.0.41:
+ version "1.0.190"
+ resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
+ integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
+ dependencies:
+ cross-domain-safe-weakmap "^1"
+ cross-domain-utils "^2"
+ zalgo-promise "^1"
+
big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
@@ -3143,6 +3166,20 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
+cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
+ version "1.0.29"
+ resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
+ integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
+ dependencies:
+ cross-domain-utils "^2.0.0"
+
+cross-domain-utils@^2, cross-domain-utils@^2.0.0:
+ version "2.0.38"
+ resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
+ integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
+ dependencies:
+ zalgo-promise "^1.0.11"
+
cross-spawn@7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
@@ -5149,6 +5186,11 @@ https-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
+i18next@^10.3:
+ version "10.6.0"
+ resolved "https://registry.yarnpkg.com/i18next/-/i18next-10.6.0.tgz#90ffd9f9bc617f34b9a12e037260f524445f7684"
+ integrity sha512-ycRlN145kQf8EsyDAzMfjqv1ZT1Jwp7P2H/07bP8JLWm+7cSLD4XqlJOvq4mKVS2y2mMIy10lX9ZeYUdQ0qSRw==
+
iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -6878,6 +6920,11 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
dependencies:
minimist "^1.2.5"
+moment@^2.24.0:
+ version "2.29.4"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
+ integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
+
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@@ -7718,6 +7765,17 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+post-robot@^10.0.46:
+ version "10.0.46"
+ resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
+ integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
+ dependencies:
+ belter "^1.0.41"
+ cross-domain-safe-weakmap "^1.0.1"
+ cross-domain-utils "^2.0.0"
+ universal-serialize "^1.0.4"
+ zalgo-promise "^1.0.3"
+
postcss-attribute-case-insensitive@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
@@ -10326,6 +10384,11 @@ unique-slug@^2.0.0:
dependencies:
imurmurhash "^0.1.4"
+universal-serialize@^1.0.4:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
+ integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==
+
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -11043,3 +11106,8 @@ yargs@^15.3.1:
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^18.1.1"
+
+zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
+ version "1.0.48"
+ resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
+ integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==
diff --git a/examples/query-playground/package.json b/examples/query-playground/package.json
index ccfa94237..60a58f94b 100644
--- a/examples/query-playground/package.json
+++ b/examples/query-playground/package.json
@@ -29,6 +29,7 @@
"@dhis2/app-service-alerts": "file:../../services/alerts",
"@dhis2/app-service-config": "file:../../services/config",
"@dhis2/app-service-data": "file:../../services/data",
- "@dhis2/app-service-offline": "file:../../services/offline"
+ "@dhis2/app-service-offline": "file:../../services/offline",
+ "@dhis2/app-service-plugin": "file:../../services/plugin"
}
}
diff --git a/examples/query-playground/yarn.lock b/examples/query-playground/yarn.lock
index 9ac5d7f62..38258da7f 100644
--- a/examples/query-playground/yarn.lock
+++ b/examples/query-playground/yarn.lock
@@ -1789,30 +1789,46 @@
dependencies:
moment "^2.24.0"
-"@dhis2/app-runtime@*", "@dhis2/app-runtime@^2.2.2", "@dhis2/app-runtime@file:../../runtime":
- version "3.5.0"
+"@dhis2/app-runtime@*":
+ version "3.8.0"
+ resolved "https://registry.yarnpkg.com/@dhis2/app-runtime/-/app-runtime-3.8.0.tgz#4ec7fc4ec6647dc8428e3c0d2e14b2d188a993b9"
+ integrity sha512-f5M1RfUJb4yZaPDywTfogVXjzWcuYGJ7JQzny6iWXrJu1+qrRKYbfFutYNhB+4bXD4bB59DelHWqVaHtrGvbVA==
dependencies:
- "@dhis2/app-service-alerts" "3.5.0"
- "@dhis2/app-service-config" "3.5.0"
- "@dhis2/app-service-data" "3.5.0"
- "@dhis2/app-service-offline" "3.5.0"
+ "@dhis2/app-service-alerts" "3.8.0"
+ "@dhis2/app-service-config" "3.8.0"
+ "@dhis2/app-service-data" "3.8.0"
+ "@dhis2/app-service-offline" "3.8.0"
-"@dhis2/app-service-alerts@3.5.0", "@dhis2/app-service-alerts@file:../../services/alerts":
- version "3.5.0"
+"@dhis2/app-runtime@^2.2.2", "@dhis2/app-runtime@file:../../runtime":
+ version "3.7.0"
+ dependencies:
+ "@dhis2/app-service-alerts" "3.7.0"
+ "@dhis2/app-service-config" "3.7.0"
+ "@dhis2/app-service-data" "3.7.0"
+ "@dhis2/app-service-offline" "3.7.0"
+ "@dhis2/app-service-plugin" "3.7.0"
+ "@dhis2/d2-i18n" "^1.1.0"
+ post-robot "^10.0.46"
-"@dhis2/app-service-config@3.5.0", "@dhis2/app-service-config@file:../../services/config":
- version "3.5.0"
+"@dhis2/app-service-alerts@3.7.0", "@dhis2/app-service-alerts@3.8.0", "@dhis2/app-service-alerts@file:../../services/alerts":
+ version "3.7.0"
-"@dhis2/app-service-data@3.5.0", "@dhis2/app-service-data@file:../../services/data":
- version "3.5.0"
+"@dhis2/app-service-config@3.7.0", "@dhis2/app-service-config@3.8.0", "@dhis2/app-service-config@file:../../services/config":
+ version "3.7.0"
+
+"@dhis2/app-service-data@3.7.0", "@dhis2/app-service-data@3.8.0", "@dhis2/app-service-data@file:../../services/data":
+ version "3.7.0"
dependencies:
react-query "^3.13.11"
-"@dhis2/app-service-offline@3.5.0", "@dhis2/app-service-offline@file:../../services/offline":
- version "3.5.0"
+"@dhis2/app-service-offline@3.7.0", "@dhis2/app-service-offline@3.8.0", "@dhis2/app-service-offline@file:../../services/offline":
+ version "3.7.0"
dependencies:
lodash "^4.17.21"
+"@dhis2/app-service-plugin@3.7.0", "@dhis2/app-service-plugin@file:../../services/plugin":
+ version "3.7.0"
+
"@dhis2/app-shell@5.2.0":
version "5.2.0"
resolved "https://registry.yarnpkg.com/@dhis2/app-shell/-/app-shell-5.2.0.tgz#19fc3c6b18ea18048d3cdd1680ce535417edb6b3"
@@ -1900,6 +1916,14 @@
i18next "^10.3"
moment "^2.24.0"
+"@dhis2/d2-i18n@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@dhis2/d2-i18n/-/d2-i18n-1.1.0.tgz#ec777c5091f747e4c5aa4f9801c62ba4d1ef3d16"
+ integrity sha512-x3u58goDQsMfBzy50koxNrJjofJTtjRZOfz6f6Py/wMMJfp/T6vZjWMQgcfWH0JrV6d04K1RTt6bI05wqsVQvg==
+ dependencies:
+ i18next "^10.3"
+ moment "^2.24.0"
+
"@dhis2/prop-types@^1.6.4":
version "1.6.4"
resolved "https://registry.yarnpkg.com/@dhis2/prop-types/-/prop-types-1.6.4.tgz#ec4d256c9440d4d00071524422a727c61ddaa6f6"
@@ -3359,6 +3383,15 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
+belter@^1.0.41:
+ version "1.0.190"
+ resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
+ integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
+ dependencies:
+ cross-domain-safe-weakmap "^1"
+ cross-domain-utils "^2"
+ zalgo-promise "^1"
+
big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
@@ -4422,6 +4455,20 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
+cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
+ version "1.0.29"
+ resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
+ integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
+ dependencies:
+ cross-domain-utils "^2.0.0"
+
+cross-domain-utils@^2, cross-domain-utils@^2.0.0:
+ version "2.0.38"
+ resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
+ integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
+ dependencies:
+ zalgo-promise "^1.0.11"
+
cross-spawn@7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
@@ -9611,6 +9658,17 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+post-robot@^10.0.46:
+ version "10.0.46"
+ resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
+ integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
+ dependencies:
+ belter "^1.0.41"
+ cross-domain-safe-weakmap "^1.0.1"
+ cross-domain-utils "^2.0.0"
+ universal-serialize "^1.0.4"
+ zalgo-promise "^1.0.3"
+
postcss-attribute-case-insensitive@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
@@ -12665,6 +12723,11 @@ unique-string@^1.0.0:
dependencies:
crypto-random-string "^1.0.0"
+universal-serialize@^1.0.4:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
+ integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==
+
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -13499,6 +13562,11 @@ yargs@^15.0.0, yargs@^15.3.1:
y18n "^4.0.0"
yargs-parser "^18.1.1"
+zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
+ version "1.0.48"
+ resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
+ integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==
+
zip-stream@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.3.tgz#26cc4bdb93641a8590dd07112e1f77af1758865b"
diff --git a/runtime/package.json b/runtime/package.json
index abbda9628..d5f718ce4 100644
--- a/runtime/package.json
+++ b/runtime/package.json
@@ -27,6 +27,8 @@
"@dhis2/app-service-config": "3.7.0",
"@dhis2/app-service-data": "3.7.0",
"@dhis2/app-service-offline": "3.7.0",
+ "@dhis2/app-service-plugin": "3.7.0",
+ "@dhis2/d2-i18n": "^1.1.0",
"post-robot": "^10.0.46"
},
"peerDependencies": {
diff --git a/runtime/src/PluginSender.tsx b/runtime/src/PluginSender.tsx
deleted file mode 100644
index 3e16bcd6b..000000000
--- a/runtime/src/PluginSender.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import postRobot from 'post-robot'
-import React, { useEffect, useRef, useState } from 'react'
-
-export const PluginSender = ({
- pluginSource,
- ...propsToPass
-}: {
- pluginSource: string
- propsToPass: any
-}): JSX.Element => {
- const iframeRef = useRef()
-
- const [missingProps, setMissingProps] = useState(null)
-
- useEffect(() => {
- if (iframeRef?.current) {
- const iframeProps = { ...propsToPass, setMissingProps }
- const listener = postRobot.on(
- 'getPropsFromParent',
- // listen for messages coming only from the iframe rendered by this component
- { window: iframeRef.current.contentWindow },
- (): any => {
- return iframeProps
- }
- )
-
- return () => listener.cancel()
- }
- }, [propsToPass])
-
- if (missingProps && missingProps?.length > 0) {
- return (
- {`Plugin could not load because required props are missing: ${missingProps.join()}`}
- )
- }
-
- return (
-
- {pluginSource ? (
-
- ) : null}
-
- )
-}
diff --git a/runtime/src/PluginWrapper.tsx b/runtime/src/PluginWrapper.tsx
deleted file mode 100644
index 713c44296..000000000
--- a/runtime/src/PluginWrapper.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import postRobot from 'post-robot'
-import { useEffect, useState } from 'react'
-
-export const PluginWrapper = ({
- requiredProps,
- children,
-}: {
- requiredProps: [string]
- children: any
-}): any => {
- const [propsFromParent, setPropsFromParent] = useState()
-
- const receivePropsFromParent = (event: any) => {
- const { data: receivedProps } = event
- const { setMissingProps, ...explictlyPassedProps } = receivedProps
-
- setPropsFromParent(explictlyPassedProps)
-
- // check for required props
- const missingProps = requiredProps?.filter(
- (prop) => !receivedProps[prop]
- )
- if (missingProps && missingProps.length > 0) {
- setMissingProps(missingProps)
- console.error(
- `The following required props were not provided: ${missingProps.join(
- ','
- )}`
- )
- } else {
- setMissingProps([])
- }
- }
-
- useEffect(() => {
- postRobot
- .send(window.top, 'getPropsFromParent')
- .then(receivePropsFromParent)
- .catch((err: Error) => console.error(err))
- })
-
- return children({ ...propsFromParent })
-}
diff --git a/runtime/src/index.ts b/runtime/src/index.ts
index 06048f496..3339b4d45 100644
--- a/runtime/src/index.ts
+++ b/runtime/src/index.ts
@@ -22,8 +22,6 @@ export {
clearSensitiveCaches,
} from '@dhis2/app-service-offline'
-export { PluginSender } from './PluginSender'
-
-export { PluginWrapper } from './PluginWrapper'
+export { PluginSender, PluginWrapper } from '@dhis2/app-service-plugin'
export { Provider } from './Provider'
diff --git a/services/plugin/.gitignore b/services/plugin/.gitignore
new file mode 100644
index 000000000..6570aa5cb
--- /dev/null
+++ b/services/plugin/.gitignore
@@ -0,0 +1,5 @@
+# DHIS2 Platform
+node_modules
+.d2
+src/locales
+build
\ No newline at end of file
diff --git a/services/plugin/README.md b/services/plugin/README.md
new file mode 100644
index 000000000..589924263
--- /dev/null
+++ b/services/plugin/README.md
@@ -0,0 +1,11 @@
+# DHIS2 App Data Service
+
+Application configuration for [DHIS2](https://dhis2.org) applications
+
+This library is intended for use with the [DHIS2 Application Platform](https://github.com/dhis2/app-platform).
+
+## Installation
+
+This package is internal to `@dhis2/app-runtime` and generally should not be installed independently.
+
+See [the docs](https://runtime.dhis2.nu) for more.
diff --git a/services/plugin/d2.config.js b/services/plugin/d2.config.js
new file mode 100644
index 000000000..84bec20f1
--- /dev/null
+++ b/services/plugin/d2.config.js
@@ -0,0 +1,9 @@
+const config = {
+ type: 'lib',
+
+ entryPoints: {
+ lib: './src/index.ts',
+ },
+}
+
+module.exports = config
diff --git a/services/plugin/jest.config.js b/services/plugin/jest.config.js
new file mode 100644
index 000000000..66cd80913
--- /dev/null
+++ b/services/plugin/jest.config.js
@@ -0,0 +1,10 @@
+module.exports = {
+ collectCoverageFrom: [
+ 'src/**/*.(js|jsx|ts|tsx)',
+ '!src/index.ts',
+ '!src/types.ts',
+ ],
+
+ // Setup react-testing-library
+ setupFilesAfterEnv: ['/src/setupRTL.ts'],
+}
diff --git a/services/plugin/package.json b/services/plugin/package.json
new file mode 100644
index 000000000..ec3830cff
--- /dev/null
+++ b/services/plugin/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "@dhis2/app-service-plugin",
+ "version": "3.7.0",
+ "main": "./build/cjs/index.js",
+ "module": "./build/es/index.js",
+ "types": "build/types/index.d.ts",
+ "exports": {
+ "import": "./build/es/index.js",
+ "require": "./build/cjs/index.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/dhis2/app-runtime.git",
+ "directory": "services/plugin"
+ },
+ "author": "Austin McGee ",
+ "license": "BSD-3-Clause",
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "build/**"
+ ],
+ "peerDependencies": {
+ "prop-types": "^15.7.2",
+ "react": "^16.8.6",
+ "react-dom": "^16.8.6",
+ "@dhis2/app-service-data": "3.7.0"
+ },
+ "scripts": {
+ "clean": "rimraf ./build/*",
+ "build:types": "tsc --emitDeclarationOnly --outDir ./build/types",
+ "build:package": "d2-app-scripts build",
+ "build": "concurrently -n build,types \"yarn build:package\" \"yarn build:types\"",
+ "watch": "NODE_ENV=development concurrently -n build,types \"yarn build:package --watch\" \"yarn build:types --watch\"",
+ "type-check": "tsc --noEmit --allowJs --checkJs",
+ "type-check:watch": "yarn type-check --watch",
+ "test": "d2-app-scripts test",
+ "coverage": "yarn test --coverage"
+ }
+}
diff --git a/services/plugin/src/PluginError.tsx b/services/plugin/src/PluginError.tsx
new file mode 100644
index 000000000..186293e3f
--- /dev/null
+++ b/services/plugin/src/PluginError.tsx
@@ -0,0 +1,58 @@
+import { useDataQuery } from '@dhis2/app-service-data'
+import React from 'react'
+
+const meQuery = {
+ me: {
+ resource: 'me',
+ fields: ['authorities'],
+ },
+}
+
+const PluginError = ({
+ missingEntryPoint,
+ showDownload,
+ appShortName,
+ missingProps,
+}: {
+ missingEntryPoint: boolean
+ showDownload: boolean
+ appShortName?: string
+ missingProps: string[] | null
+}) => {
+ const { data }: { data?: any } = useDataQuery(meQuery) // cast to deal with types for now...
+ const canAddApp =
+ data?.me?.authorities?.includes('ALL') ||
+ data?.me?.authorities?.includes('M_dhis-web-app-management')
+ return (
+ <>
+ Plugin unavailable
+ {missingEntryPoint ? (
+ <>
+ A suitable plugin was not found.
+ {showDownload && canAddApp ? (
+
+ e.stopPropagation()}
+ target="_blank"
+ rel="noopener noreferrer"
+ href="/dhis-web-app-management/index.html#/app-hub"
+ >
+ {`Install the ${appShortName}} app from the App Hub`}
+
+
+ ) : null}
+ >
+ ) : (
+ <>
+ {!missingEntryPoint &&
+ missingProps &&
+ missingProps?.length > 0 && (
+ {`The following required props were not provided: ${missingProps.join()}`}
+ )}
+ >
+ )}
+ >
+ )
+}
+
+export default PluginError
diff --git a/services/plugin/src/PluginSender.tsx b/services/plugin/src/PluginSender.tsx
new file mode 100644
index 000000000..67c36fe15
--- /dev/null
+++ b/services/plugin/src/PluginSender.tsx
@@ -0,0 +1,125 @@
+import { useDataQuery } from '@dhis2/app-service-data'
+import postRobot from 'post-robot'
+import React, { useCallback, useEffect, useRef, useState } from 'react'
+import PluginError from './PluginError'
+
+const appsInfoQuery = {
+ apps: {
+ resource: 'apps',
+ },
+}
+
+// sample logic subject to change depending on actual endpoint details
+const getPluginEntryPoint = ({
+ apps,
+ appShortName,
+}: {
+ apps: any
+ appShortName?: string
+}): string => {
+ return apps.find(
+ ({ short_name }: { short_name: string }) =>
+ short_name && short_name === appShortName
+ )?.pluginLaunchUrl
+}
+
+export const PluginSender = ({
+ pluginSource,
+ pluginShortName,
+ ...propsToPass
+}: {
+ pluginSource?: string
+ pluginShortName?: string
+ propsToPass: any
+}): JSX.Element => {
+ const iframeRef = useRef(null)
+
+ const [missingProps, setMissingProps] = useState(null)
+
+ const { data } = useDataQuery(appsInfoQuery)
+ const pluginEntryPoint =
+ pluginSource ??
+ getPluginEntryPoint({
+ apps: data?.apps || [],
+ appShortName: pluginShortName,
+ })
+
+ const [communicationReceived, setCommunicationReceived] =
+ useState(false)
+
+ const updateMissingProps = useCallback(
+ (propsIdentified: string[] | null) => {
+ if (propsIdentified?.join() !== missingProps?.join()) {
+ setMissingProps(propsIdentified)
+ }
+ },
+ [missingProps, setMissingProps]
+ )
+
+ useEffect(() => {
+ console.log('pluginsender props update')
+ if (iframeRef?.current) {
+ const iframeProps = { ...propsToPass, updateMissingProps }
+
+ // if iframe has not sent initial request, set up a listener
+ if (!communicationReceived) {
+ const listener = postRobot.on(
+ 'getPropsFromParent',
+ // listen for messages coming only from the iframe rendered by this component
+ { window: iframeRef.current.contentWindow },
+ (): any => {
+ setCommunicationReceived(true)
+ return iframeProps
+ }
+ )
+ return () => listener.cancel()
+ }
+
+ // if iframe has sent initial request, send new props
+ if (communicationReceived && iframeRef.current.contentWindow) {
+ postRobot.send(
+ iframeRef.current.contentWindow,
+ 'updated',
+ iframeProps
+ )
+ }
+ }
+ }, [propsToPass, communicationReceived, updateMissingProps])
+
+ if (data && !pluginEntryPoint) {
+ return (
+
+ )
+ }
+
+ return (
+ <>
+ {/* if props are missing, keep iframe hidden so that it can received updated props */}
+ {missingProps && missingProps?.length > 0 && (
+
+ )}
+ {pluginEntryPoint ? (
+
+ ) : null}
+ >
+ )
+}
diff --git a/services/plugin/src/PluginWrapper.tsx b/services/plugin/src/PluginWrapper.tsx
new file mode 100644
index 000000000..1de6ff0ee
--- /dev/null
+++ b/services/plugin/src/PluginWrapper.tsx
@@ -0,0 +1,69 @@
+import postRobot from 'post-robot'
+import { useCallback, useEffect, useState } from 'react'
+
+export const PluginWrapper = ({
+ requiredProps,
+ children,
+}: {
+ requiredProps: [string]
+ children: any
+}): any => {
+ const [propsFromParent, setPropsFromParent] = useState()
+
+ const receivePropsFromParent = useCallback(
+ (event: any): void => {
+ const { data: receivedProps } = event
+ const { updateMissingProps, ...explictlyPassedProps } =
+ receivedProps
+
+ setPropsFromParent(explictlyPassedProps)
+
+ // check for required props
+ const missingProps = requiredProps?.filter(
+ (prop) => !explictlyPassedProps[prop]
+ )
+
+ // if there are missing props, pass those back to parent
+ if (missingProps && missingProps.length > 0) {
+ updateMissingProps(missingProps.sort())
+ console.error(
+ `The following required props were not provided: ${missingProps.join(
+ ','
+ )}`
+ )
+ } else {
+ updateMissingProps(null)
+ }
+ },
+ [requiredProps]
+ )
+
+ useEffect(() => {
+ // make first request for props to communicate that iframe is ready
+ postRobot
+ .send(window.top, 'getPropsFromParent')
+ .then(receivePropsFromParent)
+ .catch((err: Error) => {
+ console.error(err)
+ })
+ }, [receivePropsFromParent])
+
+ useEffect(() => {
+ // set up listener to listen for subsequent sends from parent window
+ const listener = postRobot.on(
+ 'updated',
+ { window: window.top },
+ (event): any => {
+ receivePropsFromParent(event)
+ }
+ )
+
+ return () => listener.cancel()
+ }, [receivePropsFromParent])
+
+ useEffect(() => {
+ console.log('pluginwrapper rerender')
+ })
+
+ return children({ ...propsFromParent })
+}
diff --git a/services/plugin/src/index.ts b/services/plugin/src/index.ts
new file mode 100644
index 000000000..846e52059
--- /dev/null
+++ b/services/plugin/src/index.ts
@@ -0,0 +1,3 @@
+export { PluginSender } from './PluginSender'
+
+export { PluginWrapper } from './PluginWrapper'
diff --git a/services/plugin/src/setupRTL.ts b/services/plugin/src/setupRTL.ts
new file mode 100644
index 000000000..0deaa77ec
--- /dev/null
+++ b/services/plugin/src/setupRTL.ts
@@ -0,0 +1,5 @@
+import '@testing-library/jest-dom/extend-expect'
+
+process.on('unhandledRejection', (err) => {
+ throw err
+})
diff --git a/services/plugin/src/types.ts b/services/plugin/src/types.ts
new file mode 100644
index 000000000..715161bde
--- /dev/null
+++ b/services/plugin/src/types.ts
@@ -0,0 +1 @@
+import { ReactNode } from 'react'
diff --git a/services/plugin/tsconfig.json b/services/plugin/tsconfig.json
new file mode 100644
index 000000000..3e229a39d
--- /dev/null
+++ b/services/plugin/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../tsconfig.json",
+ "include": ["src"],
+ "exclude": [
+ "src/setupRTL.ts",
+ "src/__tests__",
+ "**/*.test.ts",
+ "**/*.test.tsx"
+ ]
+}
From b14952b137971f625283c8de60afac061176b80e Mon Sep 17 00:00:00 2001
From: Thomas Zemp
Date: Wed, 25 Jan 2023 16:11:14 +0100
Subject: [PATCH 03/10] fix: clean up, add useless test
---
services/plugin/src/PluginSender.tsx | 1 -
services/plugin/src/PluginWrapper.tsx | 4 ----
.../plugin/src/__tests__/integration.test.tsx | 20 +++++++++++++++++++
3 files changed, 20 insertions(+), 5 deletions(-)
create mode 100644 services/plugin/src/__tests__/integration.test.tsx
diff --git a/services/plugin/src/PluginSender.tsx b/services/plugin/src/PluginSender.tsx
index 67c36fe15..e0e30a818 100644
--- a/services/plugin/src/PluginSender.tsx
+++ b/services/plugin/src/PluginSender.tsx
@@ -57,7 +57,6 @@ export const PluginSender = ({
)
useEffect(() => {
- console.log('pluginsender props update')
if (iframeRef?.current) {
const iframeProps = { ...propsToPass, updateMissingProps }
diff --git a/services/plugin/src/PluginWrapper.tsx b/services/plugin/src/PluginWrapper.tsx
index 1de6ff0ee..638670195 100644
--- a/services/plugin/src/PluginWrapper.tsx
+++ b/services/plugin/src/PluginWrapper.tsx
@@ -61,9 +61,5 @@ export const PluginWrapper = ({
return () => listener.cancel()
}, [receivePropsFromParent])
- useEffect(() => {
- console.log('pluginwrapper rerender')
- })
-
return children({ ...propsFromParent })
}
diff --git a/services/plugin/src/__tests__/integration.test.tsx b/services/plugin/src/__tests__/integration.test.tsx
new file mode 100644
index 000000000..f0f67e223
--- /dev/null
+++ b/services/plugin/src/__tests__/integration.test.tsx
@@ -0,0 +1,20 @@
+import * as React from 'react'
+import PluginError from '../PluginError'
+
+// empty tests (to no trigger test failure)
+describe('', () => {
+ it('should render without failing', async () => {
+ const missingEntryPoint = false
+ const showDownload = false
+ const appShortName = 'some_app'
+ const missingProps = null
+ const wrapper = () => (
+
+ )
+ })
+})
From 46c9219692b130ffde513b35e1e3e3cc278ae7f2 Mon Sep 17 00:00:00 2001
From: Thomas Zemp
Date: Tue, 31 Jan 2023 17:01:06 +0100
Subject: [PATCH 04/10] chore: start pluginwrapper error boundary
---
services/plugin/src/PluginErrorBoundary.tsx | 31 +++++++++++++++++++++
services/plugin/src/PluginWrapper.tsx | 9 ++++--
2 files changed, 38 insertions(+), 2 deletions(-)
create mode 100644 services/plugin/src/PluginErrorBoundary.tsx
diff --git a/services/plugin/src/PluginErrorBoundary.tsx b/services/plugin/src/PluginErrorBoundary.tsx
new file mode 100644
index 000000000..9775f764c
--- /dev/null
+++ b/services/plugin/src/PluginErrorBoundary.tsx
@@ -0,0 +1,31 @@
+import * as React from 'react'
+
+type ErrorBoundaryState = { error: Error | null }
+
+export class PluginErrorBoundary extends React.Component<
+ {},
+ ErrorBoundaryState
+> {
+ constructor(props: any) {
+ super(props)
+ this.state = {
+ error: null,
+ }
+ }
+
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
+ console.log('special handling for within plugin errors would be here')
+ console.error(error)
+ this.setState({
+ error,
+ })
+ }
+
+ render() {
+ if (this.state.error) {
+ return this is the pluginwrapper error boundary
+ }
+
+ return this.props.children
+ }
+}
diff --git a/services/plugin/src/PluginWrapper.tsx b/services/plugin/src/PluginWrapper.tsx
index 638670195..91c4e95ba 100644
--- a/services/plugin/src/PluginWrapper.tsx
+++ b/services/plugin/src/PluginWrapper.tsx
@@ -1,5 +1,6 @@
import postRobot from 'post-robot'
-import { useCallback, useEffect, useState } from 'react'
+import React, { useCallback, useEffect, useState } from 'react'
+import { PluginErrorBoundary } from './PluginErrorBoundary'
export const PluginWrapper = ({
requiredProps,
@@ -61,5 +62,9 @@ export const PluginWrapper = ({
return () => listener.cancel()
}, [receivePropsFromParent])
- return children({ ...propsFromParent })
+ return (
+
+ {children({ ...propsFromParent })}
+
+ )
}
From ddc425186657b7457022f45614c6e8ef5c5a421f Mon Sep 17 00:00:00 2001
From: Thomas Zemp
Date: Wed, 1 Feb 2023 20:14:49 +0100
Subject: [PATCH 05/10] chore: sample error
---
services/plugin/src/PluginErrorBoundary.tsx | 6 +++++-
services/plugin/src/PluginWrapper.tsx | 2 +-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/services/plugin/src/PluginErrorBoundary.tsx b/services/plugin/src/PluginErrorBoundary.tsx
index 9775f764c..489f154ea 100644
--- a/services/plugin/src/PluginErrorBoundary.tsx
+++ b/services/plugin/src/PluginErrorBoundary.tsx
@@ -1,9 +1,10 @@
import * as React from 'react'
+type ErrorBoundaryProps = { children: any; onCustomError: Function | null }
type ErrorBoundaryState = { error: Error | null }
export class PluginErrorBoundary extends React.Component<
- {},
+ ErrorBoundaryProps,
ErrorBoundaryState
> {
constructor(props: any) {
@@ -15,6 +16,9 @@ export class PluginErrorBoundary extends React.Component<
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.log('special handling for within plugin errors would be here')
+ if (this.props.onCustomError) {
+ this.props.onCustomError(error)
+ }
console.error(error)
this.setState({
error,
diff --git a/services/plugin/src/PluginWrapper.tsx b/services/plugin/src/PluginWrapper.tsx
index 91c4e95ba..48cdb69d4 100644
--- a/services/plugin/src/PluginWrapper.tsx
+++ b/services/plugin/src/PluginWrapper.tsx
@@ -63,7 +63,7 @@ export const PluginWrapper = ({
}, [receivePropsFromParent])
return (
-
+
{children({ ...propsFromParent })}
)
From c72fc6eac576ce043ab706e13030497a0dab3d3a Mon Sep 17 00:00:00 2001
From: Thomas Zemp
Date: Mon, 6 Feb 2023 15:28:06 +0100
Subject: [PATCH 06/10] fix: custom error handling
---
runtime/src/index.ts | 7 ++++-
services/plugin/src/PluginContext.tsx | 28 +++++++++++++++++++
services/plugin/src/PluginSender.tsx | 15 +++++++----
services/plugin/src/PluginWrapper.tsx | 39 +++++++++++++++++----------
services/plugin/src/index.ts | 2 ++
5 files changed, 71 insertions(+), 20 deletions(-)
create mode 100644 services/plugin/src/PluginContext.tsx
diff --git a/runtime/src/index.ts b/runtime/src/index.ts
index 3339b4d45..e0e9dd5d7 100644
--- a/runtime/src/index.ts
+++ b/runtime/src/index.ts
@@ -22,6 +22,11 @@ export {
clearSensitiveCaches,
} from '@dhis2/app-service-offline'
-export { PluginSender, PluginWrapper } from '@dhis2/app-service-plugin'
+export {
+ PluginSender,
+ PluginWrapper,
+ usePluginErrorContext,
+ PluginErrorProvider,
+} from '@dhis2/app-service-plugin'
export { Provider } from './Provider'
diff --git a/services/plugin/src/PluginContext.tsx b/services/plugin/src/PluginContext.tsx
new file mode 100644
index 000000000..cefc26c64
--- /dev/null
+++ b/services/plugin/src/PluginContext.tsx
@@ -0,0 +1,28 @@
+import React, { createContext, Dispatch, useContext, useState } from 'react'
+
+type PluginErrorContextType = {
+ onPluginError: any
+ setOnPluginError: any
+}
+
+const PluginErrorContext = createContext({
+ onPluginError: null,
+ setOnPluginError: null,
+})
+
+const PluginErrorProvider = ({ children }: { children: React.Component }) => {
+ const [onPluginError, setOnPluginError] = useState(null)
+ const providerValue = {
+ onPluginError,
+ setOnPluginError,
+ }
+ return (
+
+ {children}
+
+ )
+}
+
+const usePluginErrorContext = () => useContext(PluginErrorContext)
+
+export { PluginErrorContext, PluginErrorProvider, usePluginErrorContext }
diff --git a/services/plugin/src/PluginSender.tsx b/services/plugin/src/PluginSender.tsx
index e0e30a818..4c00429b0 100644
--- a/services/plugin/src/PluginSender.tsx
+++ b/services/plugin/src/PluginSender.tsx
@@ -76,11 +76,16 @@ export const PluginSender = ({
// if iframe has sent initial request, send new props
if (communicationReceived && iframeRef.current.contentWindow) {
- postRobot.send(
- iframeRef.current.contentWindow,
- 'updated',
- iframeProps
- )
+ postRobot
+ .send(
+ iframeRef.current.contentWindow,
+ 'updated',
+ iframeProps
+ )
+ .catch((err) => {
+ // log postRobot errors, but do not bubble them up
+ console.error(err)
+ })
}
}
}, [propsToPass, communicationReceived, updateMissingProps])
diff --git a/services/plugin/src/PluginWrapper.tsx b/services/plugin/src/PluginWrapper.tsx
index 48cdb69d4..7de2e087e 100644
--- a/services/plugin/src/PluginWrapper.tsx
+++ b/services/plugin/src/PluginWrapper.tsx
@@ -1,6 +1,7 @@
import postRobot from 'post-robot'
import React, { useCallback, useEffect, useState } from 'react'
import { PluginErrorBoundary } from './PluginErrorBoundary'
+import { usePluginErrorContext } from './PluginContext'
export const PluginWrapper = ({
requiredProps,
@@ -9,6 +10,7 @@ export const PluginWrapper = ({
requiredProps: [string]
children: any
}): any => {
+ const { setOnPluginError } = usePluginErrorContext()
const [propsFromParent, setPropsFromParent] = useState()
const receivePropsFromParent = useCallback(
@@ -35,19 +37,27 @@ export const PluginWrapper = ({
} else {
updateMissingProps(null)
}
+
+ if (explictlyPassedProps.onError && setOnPluginError) {
+ setOnPluginError(() => (error: Error) => {
+ explictlyPassedProps.onError(error)
+ })
+ }
},
- [requiredProps]
+ [requiredProps, setOnPluginError]
)
useEffect(() => {
- // make first request for props to communicate that iframe is ready
- postRobot
- .send(window.top, 'getPropsFromParent')
- .then(receivePropsFromParent)
- .catch((err: Error) => {
- console.error(err)
- })
- }, [receivePropsFromParent])
+ if (setOnPluginError) {
+ // make first request for props to communicate that iframe is ready
+ postRobot
+ .send(window.top, 'getPropsFromParent')
+ .then(receivePropsFromParent)
+ .catch((err: Error) => {
+ console.error(err)
+ })
+ }
+ }, [receivePropsFromParent, setOnPluginError])
useEffect(() => {
// set up listener to listen for subsequent sends from parent window
@@ -62,9 +72,10 @@ export const PluginWrapper = ({
return () => listener.cancel()
}, [receivePropsFromParent])
- return (
-
- {children({ ...propsFromParent })}
-
- )
+ return children({ ...propsFromParent })
+ // return (
+ //
+ // {children({ ...propsFromParent })}
+ //
+ // )
}
diff --git a/services/plugin/src/index.ts b/services/plugin/src/index.ts
index 846e52059..2136a0033 100644
--- a/services/plugin/src/index.ts
+++ b/services/plugin/src/index.ts
@@ -1,3 +1,5 @@
export { PluginSender } from './PluginSender'
export { PluginWrapper } from './PluginWrapper'
+
+export * from './PluginContext'
From bda6a4352fe0ad8a076f55040e3fe702f9d0c4eb Mon Sep 17 00:00:00 2001
From: Thomas Zemp
Date: Thu, 9 Feb 2023 08:44:43 +0100
Subject: [PATCH 07/10] feat: plugin wrappers, errors + alerts
---
runtime/src/Provider.tsx | 41 +++++++--
runtime/src/index.ts | 4 +-
services/alerts/src/AlertsManagerContext.ts | 3 +
services/alerts/src/AlertsProvider.tsx | 12 ++-
services/alerts/src/index.ts | 1 +
services/alerts/src/makeAlertsManager.ts | 4 +-
services/alerts/src/types.ts | 3 +
services/alerts/src/useAlert.ts | 29 +++++--
services/plugin/package.json | 3 +-
services/plugin/src/PluginContext.tsx | 43 ++++++++--
services/plugin/src/PluginError.tsx | 14 +--
services/plugin/src/PluginErrorBoundary.tsx | 35 --------
services/plugin/src/PluginSender.tsx | 72 +++++++---------
services/plugin/src/PluginWrapper.tsx | 94 +++++++++++++++------
services/plugin/src/types.ts | 2 +
15 files changed, 218 insertions(+), 142 deletions(-)
delete mode 100644 services/plugin/src/PluginErrorBoundary.tsx
diff --git a/runtime/src/Provider.tsx b/runtime/src/Provider.tsx
index 19ad016d8..10e0d1453 100644
--- a/runtime/src/Provider.tsx
+++ b/runtime/src/Provider.tsx
@@ -3,26 +3,53 @@ import { ConfigProvider } from '@dhis2/app-service-config'
import { Config } from '@dhis2/app-service-config/build/types/types'
import { DataProvider } from '@dhis2/app-service-data'
import { OfflineProvider } from '@dhis2/app-service-offline'
+import { PluginProvider, usePluginContext } from '@dhis2/app-service-plugin'
import React from 'react'
+// passed this way to avoid setting plugin service as peer dependency in alerts
+const DemoPass = ({
+ plugin,
+ offlineInterface,
+ children,
+}: {
+ plugin: boolean
+ offlineInterface?: any
+ children: React.ReactNode
+}) => {
+ const { parentAlertsAdd, showAlertsInPlugin } = usePluginContext()
+ return (
+
+
+
+ {children}
+
+
+
+ )
+}
+
type ProviderInput = {
config: Config
children: React.ReactNode
offlineInterface?: any // temporary until offline service has types
+ plugin: boolean
}
export const Provider = ({
config,
children,
offlineInterface,
+ plugin,
}: ProviderInput) => (
-
-
-
- {children}
-
-
-
+
+
+ {children}
+
+
)
diff --git a/runtime/src/index.ts b/runtime/src/index.ts
index e0e9dd5d7..748267127 100644
--- a/runtime/src/index.ts
+++ b/runtime/src/index.ts
@@ -25,8 +25,8 @@ export {
export {
PluginSender,
PluginWrapper,
- usePluginErrorContext,
- PluginErrorProvider,
+ usePluginContext,
+ PluginProvider,
} from '@dhis2/app-service-plugin'
export { Provider } from './Provider'
diff --git a/services/alerts/src/AlertsManagerContext.ts b/services/alerts/src/AlertsManagerContext.ts
index 0c3ed4d8b..9c3527fa9 100644
--- a/services/alerts/src/AlertsManagerContext.ts
+++ b/services/alerts/src/AlertsManagerContext.ts
@@ -9,6 +9,9 @@ const placeholder = () => {
const defaultAlertsManager: AlertsManager = {
add: placeholder,
+ plugin: false,
+ parentAlertsAdd: null,
+ showAlertsInPlugin: false,
}
export const AlertsManagerContext =
diff --git a/services/alerts/src/AlertsProvider.tsx b/services/alerts/src/AlertsProvider.tsx
index e59cbf766..fc8876ce4 100644
--- a/services/alerts/src/AlertsProvider.tsx
+++ b/services/alerts/src/AlertsProvider.tsx
@@ -5,17 +5,25 @@ import { makeAlertsManager } from './makeAlertsManager'
import type { Alert, AlertsManager } from './types'
export const AlertsProvider = ({
+ plugin,
+ parentAlertsAdd,
+ showAlertsInPlugin,
children,
}: {
+ plugin: boolean
+ parentAlertsAdd: any
+ showAlertsInPlugin: boolean
children: React.ReactNode
}): ReactElement => {
const [alerts, setAlerts] = useState([])
const [alertsManager] = useState(() =>
- makeAlertsManager(setAlerts)
+ makeAlertsManager(setAlerts, plugin)
)
return (
-
+
{children}
diff --git a/services/alerts/src/index.ts b/services/alerts/src/index.ts
index f2e0367bd..f02489ab6 100644
--- a/services/alerts/src/index.ts
+++ b/services/alerts/src/index.ts
@@ -1,3 +1,4 @@
export { AlertsProvider } from './AlertsProvider'
export { useAlerts } from './useAlerts'
export { useAlert } from './useAlert'
+export { AlertsManagerContext } from './AlertsManagerContext'
diff --git a/services/alerts/src/makeAlertsManager.ts b/services/alerts/src/makeAlertsManager.ts
index b97bf264e..6963d58db 100644
--- a/services/alerts/src/makeAlertsManager.ts
+++ b/services/alerts/src/makeAlertsManager.ts
@@ -4,7 +4,8 @@ const toVisibleAlertsArray = (alertsMap: AlertsMap) =>
Array.from(alertsMap.values())
export const makeAlertsManager = (
- setAlerts: React.Dispatch>
+ setAlerts: React.Dispatch>,
+ plugin: boolean
): AlertsManager => {
const alertsMap: AlertsMap = new Map()
let id = 0
@@ -29,5 +30,6 @@ export const makeAlertsManager = (
return {
add,
+ plugin,
}
}
diff --git a/services/alerts/src/types.ts b/services/alerts/src/types.ts
index 73c563852..654a0560b 100644
--- a/services/alerts/src/types.ts
+++ b/services/alerts/src/types.ts
@@ -15,4 +15,7 @@ export type AlertsMap = Map
export type AlertsManager = {
add: (alert: Alert, alertRef: AlertRef) => Alert
+ plugin: boolean
+ parentAlertsAdd?: any
+ showAlertsInPlugin?: boolean
}
diff --git a/services/alerts/src/useAlert.ts b/services/alerts/src/useAlert.ts
index 87454cace..2faba53fd 100644
--- a/services/alerts/src/useAlert.ts
+++ b/services/alerts/src/useAlert.ts
@@ -6,7 +6,8 @@ export const useAlert = (
message: string | ((props: any) => string),
options: AlertOptions | ((props: any) => AlertOptions) = {}
): { show: (props?: any) => void; hide: () => void } => {
- const { add }: AlertsManager = useContext(AlertsManagerContext)
+ const { add, plugin, parentAlertsAdd, showAlertsInPlugin }: AlertsManager =
+ useContext(AlertsManagerContext)
const alertRef = useRef(null)
const show = useCallback(
@@ -17,15 +18,25 @@ export const useAlert = (
const resolvedOptions =
typeof options === 'function' ? options(props) : options
- alertRef.current = add(
- {
- message: resolvedMessage,
- options: resolvedOptions,
- },
- alertRef
- )
+ if (plugin && parentAlertsAdd && !showAlertsInPlugin) {
+ alertRef.current = parentAlertsAdd(
+ {
+ message: resolvedMessage,
+ options: resolvedOptions,
+ },
+ alertRef
+ )
+ } else {
+ alertRef.current = add(
+ {
+ message: resolvedMessage,
+ options: resolvedOptions,
+ },
+ alertRef
+ )
+ }
},
- [add, message, options]
+ [add, parentAlertsAdd, message, options]
)
const hide = useCallback(() => {
diff --git a/services/plugin/package.json b/services/plugin/package.json
index ec3830cff..cb8e64325 100644
--- a/services/plugin/package.json
+++ b/services/plugin/package.json
@@ -25,7 +25,8 @@
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
- "@dhis2/app-service-data": "3.7.0"
+ "@dhis2/app-service-alerts": "3.7.0",
+ "@dhis2/app-service-data": "3.7.0"
},
"scripts": {
"clean": "rimraf ./build/*",
diff --git a/services/plugin/src/PluginContext.tsx b/services/plugin/src/PluginContext.tsx
index cefc26c64..c66352307 100644
--- a/services/plugin/src/PluginContext.tsx
+++ b/services/plugin/src/PluginContext.tsx
@@ -1,28 +1,55 @@
-import React, { createContext, Dispatch, useContext, useState } from 'react'
+import React, { ReactElement, createContext, useContext, useState } from 'react'
-type PluginErrorContextType = {
+type PluginContextType = {
onPluginError: any
setOnPluginError: any
+ parentAlertsAdd: any
+ setParentAlertsAdd: any
+ showAlertsInPlugin: boolean
+ setShowAlertsInPlugin: any
+ clearPluginError: any
+ setClearPluginError: any
}
-const PluginErrorContext = createContext({
+const PluginContext = createContext({
onPluginError: null,
setOnPluginError: null,
+ parentAlertsAdd: null,
+ setParentAlertsAdd: null,
+ showAlertsInPlugin: false,
+ setShowAlertsInPlugin: null,
+ clearPluginError: null,
+ setClearPluginError: null,
})
-const PluginErrorProvider = ({ children }: { children: React.Component }) => {
+// TO DO: implement different component if not plugin
+
+const PluginProvider = ({
+ children,
+}: {
+ children: React.ReactNode
+}): ReactElement => {
const [onPluginError, setOnPluginError] = useState(null)
+ const [parentAlertsAdd, setParentAlertsAdd] = useState(null)
+ const [showAlertsInPlugin, setShowAlertsInPlugin] = useState(false)
+ const [clearPluginError, setClearPluginError] = useState(null)
const providerValue = {
onPluginError,
setOnPluginError,
+ parentAlertsAdd,
+ setParentAlertsAdd,
+ showAlertsInPlugin,
+ setShowAlertsInPlugin,
+ clearPluginError,
+ setClearPluginError,
}
return (
-
+
{children}
-
+
)
}
-const usePluginErrorContext = () => useContext(PluginErrorContext)
+const usePluginContext = () => useContext(PluginContext)
-export { PluginErrorContext, PluginErrorProvider, usePluginErrorContext }
+export { PluginContext, PluginProvider, usePluginContext }
diff --git a/services/plugin/src/PluginError.tsx b/services/plugin/src/PluginError.tsx
index 186293e3f..145fb5f1a 100644
--- a/services/plugin/src/PluginError.tsx
+++ b/services/plugin/src/PluginError.tsx
@@ -8,16 +8,16 @@ const meQuery = {
},
}
+// plugin error component (e.g. for dealing with missing/inaccessible plugin)
+
const PluginError = ({
missingEntryPoint,
showDownload,
appShortName,
- missingProps,
}: {
missingEntryPoint: boolean
showDownload: boolean
appShortName?: string
- missingProps: string[] | null
}) => {
const { data }: { data?: any } = useDataQuery(meQuery) // cast to deal with types for now...
const canAddApp =
@@ -42,15 +42,7 @@ const PluginError = ({
) : null}
>
- ) : (
- <>
- {!missingEntryPoint &&
- missingProps &&
- missingProps?.length > 0 && (
- {`The following required props were not provided: ${missingProps.join()}`}
- )}
- >
- )}
+ ) : null}
>
)
}
diff --git a/services/plugin/src/PluginErrorBoundary.tsx b/services/plugin/src/PluginErrorBoundary.tsx
deleted file mode 100644
index 489f154ea..000000000
--- a/services/plugin/src/PluginErrorBoundary.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import * as React from 'react'
-
-type ErrorBoundaryProps = { children: any; onCustomError: Function | null }
-type ErrorBoundaryState = { error: Error | null }
-
-export class PluginErrorBoundary extends React.Component<
- ErrorBoundaryProps,
- ErrorBoundaryState
-> {
- constructor(props: any) {
- super(props)
- this.state = {
- error: null,
- }
- }
-
- componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
- console.log('special handling for within plugin errors would be here')
- if (this.props.onCustomError) {
- this.props.onCustomError(error)
- }
- console.error(error)
- this.setState({
- error,
- })
- }
-
- render() {
- if (this.state.error) {
- return this is the pluginwrapper error boundary
- }
-
- return this.props.children
- }
-}
diff --git a/services/plugin/src/PluginSender.tsx b/services/plugin/src/PluginSender.tsx
index 4c00429b0..66d8cf5ed 100644
--- a/services/plugin/src/PluginSender.tsx
+++ b/services/plugin/src/PluginSender.tsx
@@ -1,6 +1,7 @@
+import { AlertsManagerContext } from '@dhis2/app-service-alerts'
import { useDataQuery } from '@dhis2/app-service-data'
import postRobot from 'post-robot'
-import React, { useCallback, useEffect, useRef, useState } from 'react'
+import React, { useContext, useEffect, useRef, useState } from 'react'
import PluginError from './PluginError'
const appsInfoQuery = {
@@ -34,7 +35,7 @@ export const PluginSender = ({
}): JSX.Element => {
const iframeRef = useRef(null)
- const [missingProps, setMissingProps] = useState(null)
+ const { add: alertsAdd } = useContext(AlertsManagerContext)
const { data } = useDataQuery(appsInfoQuery)
const pluginEntryPoint =
@@ -47,21 +48,19 @@ export const PluginSender = ({
const [communicationReceived, setCommunicationReceived] =
useState(false)
- const updateMissingProps = useCallback(
- (propsIdentified: string[] | null) => {
- if (propsIdentified?.join() !== missingProps?.join()) {
- setMissingProps(propsIdentified)
- }
- },
- [missingProps, setMissingProps]
- )
+ const [inErrorState, setInErrorState] = useState(false)
useEffect(() => {
if (iframeRef?.current) {
- const iframeProps = { ...propsToPass, updateMissingProps }
+ const iframeProps = {
+ ...propsToPass,
+ alertsAdd,
+ setInErrorState,
+ setCommunicationReceived,
+ }
// if iframe has not sent initial request, set up a listener
- if (!communicationReceived) {
+ if (!communicationReceived && !inErrorState) {
const listener = postRobot.on(
'getPropsFromParent',
// listen for messages coming only from the iframe rendered by this component
@@ -75,7 +74,11 @@ export const PluginSender = ({
}
// if iframe has sent initial request, send new props
- if (communicationReceived && iframeRef.current.contentWindow) {
+ if (
+ communicationReceived &&
+ iframeRef.current.contentWindow &&
+ !inErrorState
+ ) {
postRobot
.send(
iframeRef.current.contentWindow,
@@ -88,7 +91,7 @@ export const PluginSender = ({
})
}
}
- }, [propsToPass, communicationReceived, updateMissingProps])
+ }, [propsToPass, communicationReceived, inErrorState, alertsAdd])
if (data && !pluginEntryPoint) {
return (
@@ -96,34 +99,23 @@ export const PluginSender = ({
missingEntryPoint={true}
showDownload={false}
appShortName={pluginShortName}
- missingProps={missingProps}
/>
)
}
- return (
- <>
- {/* if props are missing, keep iframe hidden so that it can received updated props */}
- {missingProps && missingProps?.length > 0 && (
-
- )}
- {pluginEntryPoint ? (
-
- ) : null}
- >
- )
+ if (pluginEntryPoint) {
+ return (
+
+ )
+ }
+
+ return <>>
}
diff --git a/services/plugin/src/PluginWrapper.tsx b/services/plugin/src/PluginWrapper.tsx
index 7de2e087e..518e84093 100644
--- a/services/plugin/src/PluginWrapper.tsx
+++ b/services/plugin/src/PluginWrapper.tsx
@@ -1,7 +1,6 @@
import postRobot from 'post-robot'
-import React, { useCallback, useEffect, useState } from 'react'
-import { PluginErrorBoundary } from './PluginErrorBoundary'
-import { usePluginErrorContext } from './PluginContext'
+import { useCallback, useEffect, useState } from 'react'
+import { usePluginContext } from './PluginContext'
export const PluginWrapper = ({
requiredProps,
@@ -10,41 +9,80 @@ export const PluginWrapper = ({
requiredProps: [string]
children: any
}): any => {
- const { setOnPluginError } = usePluginErrorContext()
+ const {
+ setOnPluginError,
+ setClearPluginError,
+ setParentAlertsAdd,
+ setShowAlertsInPlugin,
+ } = usePluginContext()
const [propsFromParent, setPropsFromParent] = useState()
+ const [propsThatAreMissing, setPropsThatAreMissing] = useState<
+ Array
+ >([])
const receivePropsFromParent = useCallback(
(event: any): void => {
const { data: receivedProps } = event
- const { updateMissingProps, ...explictlyPassedProps } =
- receivedProps
+ const {
+ setInErrorState,
+ setCommunicationReceived,
+ alertsAdd,
+ showAlertsInPlugin,
+ onError,
+ ...explicitlyPassedProps
+ } = receivedProps
- setPropsFromParent(explictlyPassedProps)
+ setPropsFromParent(explicitlyPassedProps)
// check for required props
const missingProps = requiredProps?.filter(
- (prop) => !explictlyPassedProps[prop]
+ (prop) => !explicitlyPassedProps[prop]
)
- // if there are missing props, pass those back to parent
+ // if there are missing props, set to state to throw error
if (missingProps && missingProps.length > 0) {
- updateMissingProps(missingProps.sort())
- console.error(
- `The following required props were not provided: ${missingProps.join(
- ','
- )}`
- )
- } else {
- updateMissingProps(null)
+ console.error(`These props are missing: ${missingProps.join()}`)
+ setPropsThatAreMissing(missingProps)
}
- if (explictlyPassedProps.onError && setOnPluginError) {
- setOnPluginError(() => (error: Error) => {
- explictlyPassedProps.onError(error)
+ if (setOnPluginError && setInErrorState) {
+ if (onError) {
+ setOnPluginError(() => (error: Error) => {
+ setCommunicationReceived(false)
+ setInErrorState(true)
+ onError(error)
+ })
+ } else {
+ setOnPluginError(() => () => {
+ setCommunicationReceived(false)
+ setInErrorState(true)
+ })
+ }
+ }
+
+ if (setClearPluginError && setInErrorState) {
+ setClearPluginError(() => () => {
+ setInErrorState(false)
+ })
+ }
+
+ if (setParentAlertsAdd && alertsAdd) {
+ setParentAlertsAdd(() => (alert: any, alertRef: any) => {
+ alertsAdd(alert, alertRef)
})
}
+
+ if (showAlertsInPlugin && setShowAlertsInPlugin) {
+ setShowAlertsInPlugin(Boolean(showAlertsInPlugin))
+ }
},
- [requiredProps, setOnPluginError]
+ [
+ requiredProps,
+ setOnPluginError,
+ setClearPluginError,
+ setParentAlertsAdd,
+ setShowAlertsInPlugin,
+ ]
)
useEffect(() => {
@@ -72,10 +110,14 @@ export const PluginWrapper = ({
return () => listener.cancel()
}, [receivePropsFromParent])
+ // throw error if props are missing
+ useEffect(() => {
+ if (propsThatAreMissing.length > 0) {
+ throw new Error(
+ `These props are missing: ${propsThatAreMissing.join()}`
+ )
+ }
+ }, [propsThatAreMissing])
+
return children({ ...propsFromParent })
- // return (
- //
- // {children({ ...propsFromParent })}
- //
- // )
}
diff --git a/services/plugin/src/types.ts b/services/plugin/src/types.ts
index 715161bde..f00b281de 100644
--- a/services/plugin/src/types.ts
+++ b/services/plugin/src/types.ts
@@ -1 +1,3 @@
import { ReactNode } from 'react'
+
+// file is a placeholder to allow build
From 1681f6ec557d64771d9c352b6e819afd0e6be763 Mon Sep 17 00:00:00 2001
From: Thomas Zemp
Date: Fri, 17 Feb 2023 16:14:25 +0100
Subject: [PATCH 08/10] chore: move plugin wrapper logic to app-platform
---
examples/cra/yarn.lock | 45 -------
examples/query-playground/yarn.lock | 45 -------
runtime/package.json | 6 +-
runtime/src/Provider.tsx | 47 +++----
runtime/src/index.ts | 7 +-
services/alerts/src/useAlert.ts | 2 +-
services/plugin/package.json | 6 +-
services/plugin/src/PluginContext.tsx | 55 --------
services/plugin/src/PluginError.tsx | 51 ++------
services/plugin/src/PluginSender.tsx | 1 -
services/plugin/src/PluginWrapper.tsx | 123 ------------------
.../plugin/src/__tests__/integration.test.tsx | 4 -
services/plugin/src/index.ts | 4 -
13 files changed, 35 insertions(+), 361 deletions(-)
delete mode 100644 services/plugin/src/PluginContext.tsx
delete mode 100644 services/plugin/src/PluginWrapper.tsx
diff --git a/examples/cra/yarn.lock b/examples/cra/yarn.lock
index ae729ea73..08d7548ad 100644
--- a/examples/cra/yarn.lock
+++ b/examples/cra/yarn.lock
@@ -1062,7 +1062,6 @@
"@dhis2/app-service-offline" "3.7.0"
"@dhis2/app-service-plugin" "3.7.0"
"@dhis2/d2-i18n" "^1.1.0"
- post-robot "^10.0.46"
"@dhis2/app-service-alerts@3.7.0", "@dhis2/app-service-alerts@file:../../services/alerts":
version "3.7.0"
@@ -2273,15 +2272,6 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
-belter@^1.0.41:
- version "1.0.190"
- resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
- integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
- dependencies:
- cross-domain-safe-weakmap "^1"
- cross-domain-utils "^2"
- zalgo-promise "^1"
-
big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
@@ -3166,20 +3156,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
- version "1.0.29"
- resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
- integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
- dependencies:
- cross-domain-utils "^2.0.0"
-
-cross-domain-utils@^2, cross-domain-utils@^2.0.0:
- version "2.0.38"
- resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
- integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
- dependencies:
- zalgo-promise "^1.0.11"
-
cross-spawn@7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
@@ -7765,17 +7741,6 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
-post-robot@^10.0.46:
- version "10.0.46"
- resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
- integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
- dependencies:
- belter "^1.0.41"
- cross-domain-safe-weakmap "^1.0.1"
- cross-domain-utils "^2.0.0"
- universal-serialize "^1.0.4"
- zalgo-promise "^1.0.3"
-
postcss-attribute-case-insensitive@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
@@ -10384,11 +10349,6 @@ unique-slug@^2.0.0:
dependencies:
imurmurhash "^0.1.4"
-universal-serialize@^1.0.4:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
- integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==
-
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -11106,8 +11066,3 @@ yargs@^15.3.1:
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^18.1.1"
-
-zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
- version "1.0.48"
- resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
- integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==
diff --git a/examples/query-playground/yarn.lock b/examples/query-playground/yarn.lock
index 38258da7f..232e71aad 100644
--- a/examples/query-playground/yarn.lock
+++ b/examples/query-playground/yarn.lock
@@ -1808,7 +1808,6 @@
"@dhis2/app-service-offline" "3.7.0"
"@dhis2/app-service-plugin" "3.7.0"
"@dhis2/d2-i18n" "^1.1.0"
- post-robot "^10.0.46"
"@dhis2/app-service-alerts@3.7.0", "@dhis2/app-service-alerts@3.8.0", "@dhis2/app-service-alerts@file:../../services/alerts":
version "3.7.0"
@@ -3383,15 +3382,6 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
-belter@^1.0.41:
- version "1.0.190"
- resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
- integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
- dependencies:
- cross-domain-safe-weakmap "^1"
- cross-domain-utils "^2"
- zalgo-promise "^1"
-
big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
@@ -4455,20 +4445,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
- version "1.0.29"
- resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
- integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
- dependencies:
- cross-domain-utils "^2.0.0"
-
-cross-domain-utils@^2, cross-domain-utils@^2.0.0:
- version "2.0.38"
- resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
- integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
- dependencies:
- zalgo-promise "^1.0.11"
-
cross-spawn@7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
@@ -9658,17 +9634,6 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
-post-robot@^10.0.46:
- version "10.0.46"
- resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
- integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
- dependencies:
- belter "^1.0.41"
- cross-domain-safe-weakmap "^1.0.1"
- cross-domain-utils "^2.0.0"
- universal-serialize "^1.0.4"
- zalgo-promise "^1.0.3"
-
postcss-attribute-case-insensitive@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
@@ -12723,11 +12688,6 @@ unique-string@^1.0.0:
dependencies:
crypto-random-string "^1.0.0"
-universal-serialize@^1.0.4:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
- integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==
-
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -13562,11 +13522,6 @@ yargs@^15.0.0, yargs@^15.3.1:
y18n "^4.0.0"
yargs-parser "^18.1.1"
-zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
- version "1.0.48"
- resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
- integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==
-
zip-stream@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.3.tgz#26cc4bdb93641a8590dd07112e1f77af1758865b"
diff --git a/runtime/package.json b/runtime/package.json
index d5f718ce4..99c6c3aba 100644
--- a/runtime/package.json
+++ b/runtime/package.json
@@ -28,8 +28,7 @@
"@dhis2/app-service-data": "3.7.0",
"@dhis2/app-service-offline": "3.7.0",
"@dhis2/app-service-plugin": "3.7.0",
- "@dhis2/d2-i18n": "^1.1.0",
- "post-robot": "^10.0.46"
+ "@dhis2/d2-i18n": "^1.1.0"
},
"peerDependencies": {
"prop-types": "^15.7.2",
@@ -41,8 +40,5 @@
"build:package": "d2-app-scripts build",
"build": "concurrently -n build,types \"yarn build:package\" \"yarn build:types\"",
"test": "echo \"No tests yet!\""
- },
- "devDependencies": {
- "@types/post-robot": "^10.0.3"
}
}
diff --git a/runtime/src/Provider.tsx b/runtime/src/Provider.tsx
index 10e0d1453..312af148c 100644
--- a/runtime/src/Provider.tsx
+++ b/runtime/src/Provider.tsx
@@ -3,53 +3,36 @@ import { ConfigProvider } from '@dhis2/app-service-config'
import { Config } from '@dhis2/app-service-config/build/types/types'
import { DataProvider } from '@dhis2/app-service-data'
import { OfflineProvider } from '@dhis2/app-service-offline'
-import { PluginProvider, usePluginContext } from '@dhis2/app-service-plugin'
import React from 'react'
-// passed this way to avoid setting plugin service as peer dependency in alerts
-const DemoPass = ({
- plugin,
- offlineInterface,
- children,
-}: {
- plugin: boolean
- offlineInterface?: any
- children: React.ReactNode
-}) => {
- const { parentAlertsAdd, showAlertsInPlugin } = usePluginContext()
- return (
-
-
-
- {children}
-
-
-
- )
-}
-
type ProviderInput = {
config: Config
children: React.ReactNode
offlineInterface?: any // temporary until offline service has types
plugin: boolean
+ parentAlertsAdd: any
+ showAlertsInPlugin: boolean
}
export const Provider = ({
config,
children,
offlineInterface,
plugin,
+ parentAlertsAdd,
+ showAlertsInPlugin,
}: ProviderInput) => (
-
-
- {children}
-
-
+
+
+
+ {children}
+
+
+
)
diff --git a/runtime/src/index.ts b/runtime/src/index.ts
index 748267127..37607eb5d 100644
--- a/runtime/src/index.ts
+++ b/runtime/src/index.ts
@@ -22,11 +22,6 @@ export {
clearSensitiveCaches,
} from '@dhis2/app-service-offline'
-export {
- PluginSender,
- PluginWrapper,
- usePluginContext,
- PluginProvider,
-} from '@dhis2/app-service-plugin'
+export { PluginSender } from '@dhis2/app-service-plugin'
export { Provider } from './Provider'
diff --git a/services/alerts/src/useAlert.ts b/services/alerts/src/useAlert.ts
index 2faba53fd..adc84e211 100644
--- a/services/alerts/src/useAlert.ts
+++ b/services/alerts/src/useAlert.ts
@@ -36,7 +36,7 @@ export const useAlert = (
)
}
},
- [add, parentAlertsAdd, message, options]
+ [add, parentAlertsAdd, message, options, plugin, showAlertsInPlugin]
)
const hide = useCallback(() => {
diff --git a/services/plugin/package.json b/services/plugin/package.json
index cb8e64325..7dc810784 100644
--- a/services/plugin/package.json
+++ b/services/plugin/package.json
@@ -26,7 +26,11 @@
"react": "^16.8.6",
"react-dom": "^16.8.6",
"@dhis2/app-service-alerts": "3.7.0",
- "@dhis2/app-service-data": "3.7.0"
+ "@dhis2/app-service-data": "3.7.0",
+ "post-robot": "^10.0.46"
+ },
+ "devDependencies": {
+ "@types/post-robot": "^10.0.3"
},
"scripts": {
"clean": "rimraf ./build/*",
diff --git a/services/plugin/src/PluginContext.tsx b/services/plugin/src/PluginContext.tsx
deleted file mode 100644
index c66352307..000000000
--- a/services/plugin/src/PluginContext.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import React, { ReactElement, createContext, useContext, useState } from 'react'
-
-type PluginContextType = {
- onPluginError: any
- setOnPluginError: any
- parentAlertsAdd: any
- setParentAlertsAdd: any
- showAlertsInPlugin: boolean
- setShowAlertsInPlugin: any
- clearPluginError: any
- setClearPluginError: any
-}
-
-const PluginContext = createContext({
- onPluginError: null,
- setOnPluginError: null,
- parentAlertsAdd: null,
- setParentAlertsAdd: null,
- showAlertsInPlugin: false,
- setShowAlertsInPlugin: null,
- clearPluginError: null,
- setClearPluginError: null,
-})
-
-// TO DO: implement different component if not plugin
-
-const PluginProvider = ({
- children,
-}: {
- children: React.ReactNode
-}): ReactElement => {
- const [onPluginError, setOnPluginError] = useState(null)
- const [parentAlertsAdd, setParentAlertsAdd] = useState(null)
- const [showAlertsInPlugin, setShowAlertsInPlugin] = useState(false)
- const [clearPluginError, setClearPluginError] = useState(null)
- const providerValue = {
- onPluginError,
- setOnPluginError,
- parentAlertsAdd,
- setParentAlertsAdd,
- showAlertsInPlugin,
- setShowAlertsInPlugin,
- clearPluginError,
- setClearPluginError,
- }
- return (
-
- {children}
-
- )
-}
-
-const usePluginContext = () => useContext(PluginContext)
-
-export { PluginContext, PluginProvider, usePluginContext }
diff --git a/services/plugin/src/PluginError.tsx b/services/plugin/src/PluginError.tsx
index 145fb5f1a..2883f88a5 100644
--- a/services/plugin/src/PluginError.tsx
+++ b/services/plugin/src/PluginError.tsx
@@ -1,50 +1,23 @@
-import { useDataQuery } from '@dhis2/app-service-data'
import React from 'react'
-const meQuery = {
- me: {
- resource: 'me',
- fields: ['authorities'],
- },
-}
-
-// plugin error component (e.g. for dealing with missing/inaccessible plugin)
+// PLACEHOLDER plugin error component (e.g. for dealing with missing/inaccessible plugin)
+// note that d2-i18n does not work with typescript projects, so we cannot currently translate
const PluginError = ({
missingEntryPoint,
- showDownload,
appShortName,
}: {
missingEntryPoint: boolean
- showDownload: boolean
appShortName?: string
-}) => {
- const { data }: { data?: any } = useDataQuery(meQuery) // cast to deal with types for now...
- const canAddApp =
- data?.me?.authorities?.includes('ALL') ||
- data?.me?.authorities?.includes('M_dhis-web-app-management')
- return (
- <>
- Plugin unavailable
- {missingEntryPoint ? (
- <>
- A suitable plugin was not found.
- {showDownload && canAddApp ? (
-
- e.stopPropagation()}
- target="_blank"
- rel="noopener noreferrer"
- href="/dhis-web-app-management/index.html#/app-hub"
- >
- {`Install the ${appShortName}} app from the App Hub`}
-
-
- ) : null}
- >
- ) : null}
- >
- )
-}
+}) => (
+ <>
+ Plugin unavailable
+ {missingEntryPoint ? (
+ <>
+ {`You do not have access to the requested plugin ${appShortName}, or it is not installed`}
+ >
+ ) : null}
+ >
+)
export default PluginError
diff --git a/services/plugin/src/PluginSender.tsx b/services/plugin/src/PluginSender.tsx
index 66d8cf5ed..a72663d2f 100644
--- a/services/plugin/src/PluginSender.tsx
+++ b/services/plugin/src/PluginSender.tsx
@@ -97,7 +97,6 @@ export const PluginSender = ({
return (
)
diff --git a/services/plugin/src/PluginWrapper.tsx b/services/plugin/src/PluginWrapper.tsx
deleted file mode 100644
index 518e84093..000000000
--- a/services/plugin/src/PluginWrapper.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import postRobot from 'post-robot'
-import { useCallback, useEffect, useState } from 'react'
-import { usePluginContext } from './PluginContext'
-
-export const PluginWrapper = ({
- requiredProps,
- children,
-}: {
- requiredProps: [string]
- children: any
-}): any => {
- const {
- setOnPluginError,
- setClearPluginError,
- setParentAlertsAdd,
- setShowAlertsInPlugin,
- } = usePluginContext()
- const [propsFromParent, setPropsFromParent] = useState()
- const [propsThatAreMissing, setPropsThatAreMissing] = useState<
- Array
- >([])
-
- const receivePropsFromParent = useCallback(
- (event: any): void => {
- const { data: receivedProps } = event
- const {
- setInErrorState,
- setCommunicationReceived,
- alertsAdd,
- showAlertsInPlugin,
- onError,
- ...explicitlyPassedProps
- } = receivedProps
-
- setPropsFromParent(explicitlyPassedProps)
-
- // check for required props
- const missingProps = requiredProps?.filter(
- (prop) => !explicitlyPassedProps[prop]
- )
-
- // if there are missing props, set to state to throw error
- if (missingProps && missingProps.length > 0) {
- console.error(`These props are missing: ${missingProps.join()}`)
- setPropsThatAreMissing(missingProps)
- }
-
- if (setOnPluginError && setInErrorState) {
- if (onError) {
- setOnPluginError(() => (error: Error) => {
- setCommunicationReceived(false)
- setInErrorState(true)
- onError(error)
- })
- } else {
- setOnPluginError(() => () => {
- setCommunicationReceived(false)
- setInErrorState(true)
- })
- }
- }
-
- if (setClearPluginError && setInErrorState) {
- setClearPluginError(() => () => {
- setInErrorState(false)
- })
- }
-
- if (setParentAlertsAdd && alertsAdd) {
- setParentAlertsAdd(() => (alert: any, alertRef: any) => {
- alertsAdd(alert, alertRef)
- })
- }
-
- if (showAlertsInPlugin && setShowAlertsInPlugin) {
- setShowAlertsInPlugin(Boolean(showAlertsInPlugin))
- }
- },
- [
- requiredProps,
- setOnPluginError,
- setClearPluginError,
- setParentAlertsAdd,
- setShowAlertsInPlugin,
- ]
- )
-
- useEffect(() => {
- if (setOnPluginError) {
- // make first request for props to communicate that iframe is ready
- postRobot
- .send(window.top, 'getPropsFromParent')
- .then(receivePropsFromParent)
- .catch((err: Error) => {
- console.error(err)
- })
- }
- }, [receivePropsFromParent, setOnPluginError])
-
- useEffect(() => {
- // set up listener to listen for subsequent sends from parent window
- const listener = postRobot.on(
- 'updated',
- { window: window.top },
- (event): any => {
- receivePropsFromParent(event)
- }
- )
-
- return () => listener.cancel()
- }, [receivePropsFromParent])
-
- // throw error if props are missing
- useEffect(() => {
- if (propsThatAreMissing.length > 0) {
- throw new Error(
- `These props are missing: ${propsThatAreMissing.join()}`
- )
- }
- }, [propsThatAreMissing])
-
- return children({ ...propsFromParent })
-}
diff --git a/services/plugin/src/__tests__/integration.test.tsx b/services/plugin/src/__tests__/integration.test.tsx
index f0f67e223..b5270713f 100644
--- a/services/plugin/src/__tests__/integration.test.tsx
+++ b/services/plugin/src/__tests__/integration.test.tsx
@@ -5,15 +5,11 @@ import PluginError from '../PluginError'
describe('', () => {
it('should render without failing', async () => {
const missingEntryPoint = false
- const showDownload = false
const appShortName = 'some_app'
- const missingProps = null
const wrapper = () => (
)
})
diff --git a/services/plugin/src/index.ts b/services/plugin/src/index.ts
index 2136a0033..f6913966f 100644
--- a/services/plugin/src/index.ts
+++ b/services/plugin/src/index.ts
@@ -1,5 +1 @@
export { PluginSender } from './PluginSender'
-
-export { PluginWrapper } from './PluginWrapper'
-
-export * from './PluginContext'
From c537590176b2e6aebf278653a87705b3417bcc38 Mon Sep 17 00:00:00 2001
From: Thomas Zemp
Date: Fri, 24 Feb 2023 18:57:41 +0100
Subject: [PATCH 09/10] fix: add documentation, clean up
---
docs/components/Plugin.md | 63 +++++++++++++++++++
runtime/src/index.ts | 2 +-
.../src/{PluginSender.tsx => Plugin.tsx} | 2 +-
services/plugin/src/index.ts | 2 +-
yarn.lock | 44 -------------
5 files changed, 66 insertions(+), 47 deletions(-)
create mode 100644 docs/components/Plugin.md
rename services/plugin/src/{PluginSender.tsx => Plugin.tsx} (99%)
diff --git a/docs/components/Plugin.md b/docs/components/Plugin.md
new file mode 100644
index 000000000..a4ee4dff7
--- /dev/null
+++ b/docs/components/Plugin.md
@@ -0,0 +1,63 @@
+# Plugin Component
+
+A wrapper that creates an iframe for a specified plugin and establishes a two-way communication channel with said plugin, allowing you to pass props (including callbacks between an app and a plugin). Note that the plugin must be built using the app-platform with entryPoints.plugin specified in the d2.config.js file.
+
+## Basic Usage (Defining a plugin within an app)
+
+Within an app you can specify a plugin (either by providing its short name `pluginShortName`, or by specifying a URL directly (`pluginSource`). If you have provided `pluginSource`, this will take precedence (Note: lookup logic is TBD? Should we allow a URL only in development mode, for example?).
+
+```jsx
+import { Plugin } from '@dhis2/app-runtime'
+
+// within the app
+const MyApp = () => (
+ {
+ console.error(err)
+ }}
+ showAlertsInPlugin={true}
+ numberToPass={'42'}
+ callbackToPass={({ name }) => {
+ console.log(`Hi ${name}!`)
+ }}
+ />
+)
+```
+
+## Basic Usage (Using properties from the parent app)
+
+You must build your plugin with the app-platform. If you have done this, your entry component will be passed the props from the parent app. From the example above, the properties `numberToPass` and `callbackToPass` will be available in the build plugin (when it is rendered with a component).
+
+```jsx
+// your plugin entry point (the plugin itself)
+
+const MyPlugin = (propsFromParent) => {
+ const { numberToPass, callbackToPass: sayHi } = propsFromParent
+ return (
+ <>
+ {`The meaning of life is: ${numberToPass}`}
+
+ >
+ )
+}
+```
+
+## Plugin Props (reserved props)
+
+| Name | Type | Required | Description |
+| :--------------------: | :------------: | :---------------------------------------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **pluginShortName** | _string_ | _required_ if `pluginSource` is not provided | The shortName of the app/plugin you wish to load (matching the result from api/apps). Used to look up the plugin entry point. If this is not provided, `pluginSource` must be provided. `pluginSource` will take precedence if provided. |
+| **pluginSource** | _string_ (url) | _required_ if `pluginShortName` is not provided | The URL of the plugin. If this is not provided, `pluginShortName` must be provided. |
+| **onError** | _Function_ | _optional_ | Callback function to be called when an error in the plugin triggers an error boundary. You can use this to pass an error back up to the app and create a custom handling/UX if errors occur in the plugin. In general, it is recommended that you use the plugin's built-in error boundaries |
+| **showAlertsInPlugin** | _boolean_ | _optional_ | If `true`, any alerts within the plugin (defined with the `useAlert` hook) will be rendered within the iframe. By default, this is `false`. It is recommended, in general, that you do not override this and allow alerts to be hoisted up to the app level |
+
+## Plugin Props (custom props)
+
+You can specify pass any other props on the component and these will be passed down to the plugin (provided it was built with app-platform). When props are updated, they will be passed back down to the plugin. This mimics the behaviour of a normal React component, and hence you should provide stable references as needed to prevent rerendering.
+
+## Extended example
+
+See these links for an extended example of how component can be used within an [app](https://github.com/tomzemp/workingplugin/blob/plugin-wrapper-in-platform/src/App.js) and consumed within the [plugin](https://github.com/tomzemp/workingplugin/blob/plugin-wrapper-in-platform/src/Plugin.js).
diff --git a/runtime/src/index.ts b/runtime/src/index.ts
index 37607eb5d..aa07c14c3 100644
--- a/runtime/src/index.ts
+++ b/runtime/src/index.ts
@@ -22,6 +22,6 @@ export {
clearSensitiveCaches,
} from '@dhis2/app-service-offline'
-export { PluginSender } from '@dhis2/app-service-plugin'
+export { Plugin } from '@dhis2/app-service-plugin'
export { Provider } from './Provider'
diff --git a/services/plugin/src/PluginSender.tsx b/services/plugin/src/Plugin.tsx
similarity index 99%
rename from services/plugin/src/PluginSender.tsx
rename to services/plugin/src/Plugin.tsx
index a72663d2f..3fb2f149e 100644
--- a/services/plugin/src/PluginSender.tsx
+++ b/services/plugin/src/Plugin.tsx
@@ -24,7 +24,7 @@ const getPluginEntryPoint = ({
)?.pluginLaunchUrl
}
-export const PluginSender = ({
+export const Plugin = ({
pluginSource,
pluginShortName,
...propsToPass
diff --git a/services/plugin/src/index.ts b/services/plugin/src/index.ts
index f6913966f..2321646ed 100644
--- a/services/plugin/src/index.ts
+++ b/services/plugin/src/index.ts
@@ -1 +1 @@
-export { PluginSender } from './PluginSender'
+export { Plugin } from './Plugin'
diff --git a/yarn.lock b/yarn.lock
index a3b53d947..6d67514d6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3810,15 +3810,6 @@ bcryptjs@^2.3.0:
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
-belter@^1.0.41:
- version "1.0.190"
- resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
- integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
- dependencies:
- cross-domain-safe-weakmap "^1"
- cross-domain-utils "^2"
- zalgo-promise "^1"
-
bfj@^7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.0.2.tgz#1988ce76f3add9ac2913fd8ba47aad9e651bfbb2"
@@ -5025,20 +5016,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
- version "1.0.29"
- resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
- integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
- dependencies:
- cross-domain-utils "^2.0.0"
-
-cross-domain-utils@^2, cross-domain-utils@^2.0.0:
- version "2.0.38"
- resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
- integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
- dependencies:
- zalgo-promise "^1.0.11"
-
cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -11531,17 +11508,6 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
-post-robot@^10.0.46:
- version "10.0.46"
- resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
- integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
- dependencies:
- belter "^1.0.41"
- cross-domain-safe-weakmap "^1.0.1"
- cross-domain-utils "^2.0.0"
- universal-serialize "^1.0.4"
- zalgo-promise "^1.0.3"
-
postcss-attribute-case-insensitive@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
@@ -15125,11 +15091,6 @@ unique-string@^1.0.0:
dependencies:
crypto-random-string "^1.0.0"
-universal-serialize@^1.0.4:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
- integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==
-
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -16109,11 +16070,6 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
-zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
- version "1.0.48"
- resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
- integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==
-
zip-stream@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.3.tgz#26cc4bdb93641a8590dd07112e1f77af1758865b"
From 2480c1c6b82daaeee0ab82ef45962fbcabd0e778 Mon Sep 17 00:00:00 2001
From: Thomas Zemp
Date: Mon, 13 Mar 2023 15:31:03 +0100
Subject: [PATCH 10/10] fix: dependency resolution
---
examples/cra/yarn.lock | 92 ++++++++++++++++++-----------
examples/query-playground/yarn.lock | 84 ++++++++++++++++++--------
services/plugin/package.json | 6 +-
yarn.lock | 51 +++++++++++++---
4 files changed, 167 insertions(+), 66 deletions(-)
diff --git a/examples/cra/yarn.lock b/examples/cra/yarn.lock
index 08d7548ad..7fe01a14c 100644
--- a/examples/cra/yarn.lock
+++ b/examples/cra/yarn.lock
@@ -1054,41 +1054,33 @@
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
"@dhis2/app-runtime@file:../../runtime":
- version "3.7.0"
+ version "3.9.0"
dependencies:
- "@dhis2/app-service-alerts" "3.7.0"
- "@dhis2/app-service-config" "3.7.0"
- "@dhis2/app-service-data" "3.7.0"
- "@dhis2/app-service-offline" "3.7.0"
- "@dhis2/app-service-plugin" "3.7.0"
- "@dhis2/d2-i18n" "^1.1.0"
+ "@dhis2/app-service-alerts" "3.9.0"
+ "@dhis2/app-service-config" "3.9.0"
+ "@dhis2/app-service-data" "3.9.0"
+ "@dhis2/app-service-offline" "3.9.0"
-"@dhis2/app-service-alerts@3.7.0", "@dhis2/app-service-alerts@file:../../services/alerts":
- version "3.7.0"
+"@dhis2/app-service-alerts@3.9.0", "@dhis2/app-service-alerts@file:../../services/alerts":
+ version "3.9.0"
-"@dhis2/app-service-config@3.7.0", "@dhis2/app-service-config@file:../../services/config":
- version "3.7.0"
+"@dhis2/app-service-config@3.9.0", "@dhis2/app-service-config@file:../../services/config":
+ version "3.9.0"
-"@dhis2/app-service-data@3.7.0", "@dhis2/app-service-data@file:../../services/data":
- version "3.7.0"
+"@dhis2/app-service-data@3.9.0", "@dhis2/app-service-data@file:../../services/data":
+ version "3.9.0"
dependencies:
react-query "^3.13.11"
-"@dhis2/app-service-offline@3.7.0", "@dhis2/app-service-offline@file:../../services/offline":
- version "3.7.0"
+"@dhis2/app-service-offline@3.9.0", "@dhis2/app-service-offline@file:../../services/offline":
+ version "3.9.0"
dependencies:
lodash "^4.17.21"
-"@dhis2/app-service-plugin@3.7.0", "@dhis2/app-service-plugin@file:../../services/plugin":
+"@dhis2/app-service-plugin@file:../../services/plugin":
version "3.7.0"
-
-"@dhis2/d2-i18n@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@dhis2/d2-i18n/-/d2-i18n-1.1.0.tgz#ec777c5091f747e4c5aa4f9801c62ba4d1ef3d16"
- integrity sha512-x3u58goDQsMfBzy50koxNrJjofJTtjRZOfz6f6Py/wMMJfp/T6vZjWMQgcfWH0JrV6d04K1RTt6bI05wqsVQvg==
dependencies:
- i18next "^10.3"
- moment "^2.24.0"
+ post-robot "^10.0.46"
"@hapi/address@2.x.x":
version "2.1.4"
@@ -2272,6 +2264,15 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
+belter@^1.0.41:
+ version "1.0.190"
+ resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
+ integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
+ dependencies:
+ cross-domain-safe-weakmap "^1"
+ cross-domain-utils "^2"
+ zalgo-promise "^1"
+
big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
@@ -3156,6 +3157,20 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
+cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
+ version "1.0.29"
+ resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
+ integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
+ dependencies:
+ cross-domain-utils "^2.0.0"
+
+cross-domain-utils@^2, cross-domain-utils@^2.0.0:
+ version "2.0.38"
+ resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
+ integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
+ dependencies:
+ zalgo-promise "^1.0.11"
+
cross-spawn@7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
@@ -5162,11 +5177,6 @@ https-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
-i18next@^10.3:
- version "10.6.0"
- resolved "https://registry.yarnpkg.com/i18next/-/i18next-10.6.0.tgz#90ffd9f9bc617f34b9a12e037260f524445f7684"
- integrity sha512-ycRlN145kQf8EsyDAzMfjqv1ZT1Jwp7P2H/07bP8JLWm+7cSLD4XqlJOvq4mKVS2y2mMIy10lX9ZeYUdQ0qSRw==
-
iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -6896,11 +6906,6 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
dependencies:
minimist "^1.2.5"
-moment@^2.24.0:
- version "2.29.4"
- resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
- integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
-
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@@ -7741,6 +7746,17 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+post-robot@^10.0.46:
+ version "10.0.46"
+ resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
+ integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
+ dependencies:
+ belter "^1.0.41"
+ cross-domain-safe-weakmap "^1.0.1"
+ cross-domain-utils "^2.0.0"
+ universal-serialize "^1.0.4"
+ zalgo-promise "^1.0.3"
+
postcss-attribute-case-insensitive@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
@@ -10349,6 +10365,11 @@ unique-slug@^2.0.0:
dependencies:
imurmurhash "^0.1.4"
+universal-serialize@^1.0.4:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
+ integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==
+
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -11066,3 +11087,8 @@ yargs@^15.3.1:
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^18.1.1"
+
+zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
+ version "1.0.48"
+ resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
+ integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==
diff --git a/examples/query-playground/yarn.lock b/examples/query-playground/yarn.lock
index 232e71aad..c2a4509bb 100644
--- a/examples/query-playground/yarn.lock
+++ b/examples/query-playground/yarn.lock
@@ -1800,33 +1800,33 @@
"@dhis2/app-service-offline" "3.8.0"
"@dhis2/app-runtime@^2.2.2", "@dhis2/app-runtime@file:../../runtime":
- version "3.7.0"
+ version "3.9.0"
dependencies:
- "@dhis2/app-service-alerts" "3.7.0"
- "@dhis2/app-service-config" "3.7.0"
- "@dhis2/app-service-data" "3.7.0"
- "@dhis2/app-service-offline" "3.7.0"
- "@dhis2/app-service-plugin" "3.7.0"
- "@dhis2/d2-i18n" "^1.1.0"
+ "@dhis2/app-service-alerts" "3.9.0"
+ "@dhis2/app-service-config" "3.9.0"
+ "@dhis2/app-service-data" "3.9.0"
+ "@dhis2/app-service-offline" "3.9.0"
-"@dhis2/app-service-alerts@3.7.0", "@dhis2/app-service-alerts@3.8.0", "@dhis2/app-service-alerts@file:../../services/alerts":
- version "3.7.0"
+"@dhis2/app-service-alerts@3.8.0", "@dhis2/app-service-alerts@3.9.0", "@dhis2/app-service-alerts@file:../../services/alerts":
+ version "3.9.0"
-"@dhis2/app-service-config@3.7.0", "@dhis2/app-service-config@3.8.0", "@dhis2/app-service-config@file:../../services/config":
- version "3.7.0"
+"@dhis2/app-service-config@3.8.0", "@dhis2/app-service-config@3.9.0", "@dhis2/app-service-config@file:../../services/config":
+ version "3.9.0"
-"@dhis2/app-service-data@3.7.0", "@dhis2/app-service-data@3.8.0", "@dhis2/app-service-data@file:../../services/data":
- version "3.7.0"
+"@dhis2/app-service-data@3.8.0", "@dhis2/app-service-data@3.9.0", "@dhis2/app-service-data@file:../../services/data":
+ version "3.9.0"
dependencies:
react-query "^3.13.11"
-"@dhis2/app-service-offline@3.7.0", "@dhis2/app-service-offline@3.8.0", "@dhis2/app-service-offline@file:../../services/offline":
- version "3.7.0"
+"@dhis2/app-service-offline@3.8.0", "@dhis2/app-service-offline@3.9.0", "@dhis2/app-service-offline@file:../../services/offline":
+ version "3.9.0"
dependencies:
lodash "^4.17.21"
-"@dhis2/app-service-plugin@3.7.0", "@dhis2/app-service-plugin@file:../../services/plugin":
+"@dhis2/app-service-plugin@file:../../services/plugin":
version "3.7.0"
+ dependencies:
+ post-robot "^10.0.46"
"@dhis2/app-shell@5.2.0":
version "5.2.0"
@@ -1915,14 +1915,6 @@
i18next "^10.3"
moment "^2.24.0"
-"@dhis2/d2-i18n@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@dhis2/d2-i18n/-/d2-i18n-1.1.0.tgz#ec777c5091f747e4c5aa4f9801c62ba4d1ef3d16"
- integrity sha512-x3u58goDQsMfBzy50koxNrJjofJTtjRZOfz6f6Py/wMMJfp/T6vZjWMQgcfWH0JrV6d04K1RTt6bI05wqsVQvg==
- dependencies:
- i18next "^10.3"
- moment "^2.24.0"
-
"@dhis2/prop-types@^1.6.4":
version "1.6.4"
resolved "https://registry.yarnpkg.com/@dhis2/prop-types/-/prop-types-1.6.4.tgz#ec4d256c9440d4d00071524422a727c61ddaa6f6"
@@ -3382,6 +3374,15 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
+belter@^1.0.41:
+ version "1.0.190"
+ resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
+ integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
+ dependencies:
+ cross-domain-safe-weakmap "^1"
+ cross-domain-utils "^2"
+ zalgo-promise "^1"
+
big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
@@ -4445,6 +4446,20 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
+cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
+ version "1.0.29"
+ resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
+ integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
+ dependencies:
+ cross-domain-utils "^2.0.0"
+
+cross-domain-utils@^2, cross-domain-utils@^2.0.0:
+ version "2.0.38"
+ resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
+ integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
+ dependencies:
+ zalgo-promise "^1.0.11"
+
cross-spawn@7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
@@ -9634,6 +9649,17 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+post-robot@^10.0.46:
+ version "10.0.46"
+ resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
+ integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
+ dependencies:
+ belter "^1.0.41"
+ cross-domain-safe-weakmap "^1.0.1"
+ cross-domain-utils "^2.0.0"
+ universal-serialize "^1.0.4"
+ zalgo-promise "^1.0.3"
+
postcss-attribute-case-insensitive@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880"
@@ -12688,6 +12714,11 @@ unique-string@^1.0.0:
dependencies:
crypto-random-string "^1.0.0"
+universal-serialize@^1.0.4:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
+ integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==
+
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -13522,6 +13553,11 @@ yargs@^15.0.0, yargs@^15.3.1:
y18n "^4.0.0"
yargs-parser "^18.1.1"
+zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
+ version "1.0.48"
+ resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
+ integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==
+
zip-stream@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.3.tgz#26cc4bdb93641a8590dd07112e1f77af1758865b"
diff --git a/services/plugin/package.json b/services/plugin/package.json
index 7dc810784..f60aedb70 100644
--- a/services/plugin/package.json
+++ b/services/plugin/package.json
@@ -21,13 +21,15 @@
"files": [
"build/**"
],
+ "dependencies": {
+ "post-robot": "^10.0.46"
+ },
"peerDependencies": {
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"@dhis2/app-service-alerts": "3.7.0",
- "@dhis2/app-service-data": "3.7.0",
- "post-robot": "^10.0.46"
+ "@dhis2/app-service-data": "3.7.0"
},
"devDependencies": {
"@types/post-robot": "^10.0.3"
diff --git a/yarn.lock b/yarn.lock
index 6276cdef2..812c0e6c2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3961,22 +3961,15 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
-<<<<<<< HEAD
"@types/post-robot@^10.0.3":
version "10.0.3"
resolved "https://registry.yarnpkg.com/@types/post-robot/-/post-robot-10.0.3.tgz#d1429085f2faf4c87f841dab4e51472457edbf31"
integrity sha512-y8ysuxddaG8V/oA1Ay6Err7nSADRa9Bv1rl0ZQpJ0qgdIQ7ks3CHcOsYL4qE8w75+/XYDS94dBeXDs0xexm3tA==
-"@types/prettier@^2.0.0":
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.1.tgz#374e31645d58cb18a07b3ecd8e9dede4deb2cccd"
- integrity sha512-DxZZbyMAM9GWEzXL+BMZROWz9oo6A9EilwwOMET2UVu2uZTqMWS5S69KVtuVKaRjCUpcrOXRalet86/OpG4kqw==
-=======
"@types/prettier@^2.1.5":
version "2.7.2"
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0"
integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==
->>>>>>> alpha
"@types/prop-types@*":
version "15.7.3"
@@ -5279,6 +5272,15 @@ bcryptjs@^2.3.0:
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
+belter@^1.0.41:
+ version "1.0.190"
+ resolved "https://registry.yarnpkg.com/belter/-/belter-1.0.190.tgz#491857550ef240d9c66b56fc637991f5c3089966"
+ integrity sha512-jz05FHrO+bwitdI6JxV5ESyRdVhTcwMWQ7L4o+q/R4LNJFQrG58sp9EiwsSjhbihhiyYFcmmCMRRagxte6igtw==
+ dependencies:
+ cross-domain-safe-weakmap "^1"
+ cross-domain-utils "^2"
+ zalgo-promise "^1"
+
bfj@^7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.0.2.tgz#1988ce76f3add9ac2913fd8ba47aad9e651bfbb2"
@@ -6275,6 +6277,20 @@ crc@^3.4.4:
dependencies:
buffer "^5.1.0"
+cross-domain-safe-weakmap@^1, cross-domain-safe-weakmap@^1.0.1:
+ version "1.0.29"
+ resolved "https://registry.yarnpkg.com/cross-domain-safe-weakmap/-/cross-domain-safe-weakmap-1.0.29.tgz#0847975c27d9e1cc840f24c1745311958df98022"
+ integrity sha512-VLoUgf2SXnf3+na8NfeUFV59TRZkIJqCIATaMdbhccgtnTlSnHXkyTRwokngEGYdQXx8JbHT9GDYitgR2sdjuA==
+ dependencies:
+ cross-domain-utils "^2.0.0"
+
+cross-domain-utils@^2, cross-domain-utils@^2.0.0:
+ version "2.0.38"
+ resolved "https://registry.yarnpkg.com/cross-domain-utils/-/cross-domain-utils-2.0.38.tgz#2eaf321c4dfdb61596805ca4233fde4400cb6377"
+ integrity sha512-zZfi3+2EIR9l4chrEiXI2xFleyacsJf8YMLR1eJ0Veb5FTMXeJ3DpxDjZkto2FhL/g717WSELqbptNSo85UJDw==
+ dependencies:
+ zalgo-promise "^1.0.11"
+
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -11938,6 +11954,17 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+post-robot@^10.0.46:
+ version "10.0.46"
+ resolved "https://registry.yarnpkg.com/post-robot/-/post-robot-10.0.46.tgz#39cea5b51033729390fc7c90be3285cd285f0377"
+ integrity sha512-EgVJiuvI4iRWDZvzObWes0X/n8olWBEJWxlSw79zmhpgkigX8UsVL4VOBhVtoJKwf0Y9qP9g2zOONw1rv80QbA==
+ dependencies:
+ belter "^1.0.41"
+ cross-domain-safe-weakmap "^1.0.1"
+ cross-domain-utils "^2.0.0"
+ universal-serialize "^1.0.4"
+ zalgo-promise "^1.0.3"
+
postcss-attribute-case-insensitive@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz#03d761b24afc04c09e757e92ff53716ae8ea2741"
@@ -15263,6 +15290,11 @@ unique-string@^2.0.0:
dependencies:
crypto-random-string "^2.0.0"
+universal-serialize@^1.0.4:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/universal-serialize/-/universal-serialize-1.0.10.tgz#3279bb30f47290ea479f45135620f98fa9d3f3a6"
+ integrity sha512-FdouA4xSFa0fudk1+z5vLWtxZCoC0Q9lKYV3uUdFl7DttNfolmiw2ASr5ddY+/Yz6Isr68u3IqC9XMSwMP+Pow==
+
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -16131,6 +16163,11 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+zalgo-promise@^1, zalgo-promise@^1.0.11, zalgo-promise@^1.0.3:
+ version "1.0.48"
+ resolved "https://registry.yarnpkg.com/zalgo-promise/-/zalgo-promise-1.0.48.tgz#9e33eef502d5ed9f5a09fc5728c833c3e87afa2e"
+ integrity sha512-LLHANmdm53+MucY9aOFIggzYtUdkSBFxUsy4glTTQYNyK6B3uCPWTbfiGvSrEvLojw0mSzyFJ1/RRLv+QMNdzQ==
+
zip-stream@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.3.tgz#26cc4bdb93641a8590dd07112e1f77af1758865b"