diff --git a/.changeset/eighty-tools-fail.md b/.changeset/eighty-tools-fail.md deleted file mode 100644 index d2d04fdd21..0000000000 --- a/.changeset/eighty-tools-fail.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@comet/cms-api": minor ---- - -Add `PublicUploadsService` to public API - -The service can be used to programmatically create public uploads, such as when creating fixtures. diff --git a/.changeset/eleven-impalas-attack.md b/.changeset/eleven-impalas-attack.md new file mode 100644 index 0000000000..c5f6fd551a --- /dev/null +++ b/.changeset/eleven-impalas-attack.md @@ -0,0 +1,5 @@ +--- +"@comet/admin": minor +--- + +Add `helperText` prop to `Field` and `FieldContainer` to provide additional information diff --git a/.changeset/fifty-crabs-love.md b/.changeset/fifty-crabs-love.md deleted file mode 100644 index 4d715ccd03..0000000000 --- a/.changeset/fifty-crabs-love.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -"@comet/cms-site": minor ---- - -Add `hasRichTextBlockContent` helper - -The helper can be used to conditionally render a `RichTextBlock`. - -**Example:** - -```tsx -import { hasRichTextBlockContent } from "@comet/cms-site"; - -function TeaserBlock({ data: { image, text } }: PropsWithData) { - return ( - <> - - {hasRichTextBlockContent(text) && } - - ); -} -``` diff --git a/.changeset/rich-crews-smell.md b/.changeset/rich-crews-smell.md new file mode 100644 index 0000000000..092504a555 --- /dev/null +++ b/.changeset/rich-crews-smell.md @@ -0,0 +1,43 @@ +--- +"@comet/cms-admin": major +--- + +Make sites config generic + +The sites config was previously assumed to be `Record`. +However, as the sites config is solely used in application code, it could be of any shape. +Therefore, the `SitesConfigProvider` and `useSitesConfig` are made generic. +The following changes have to be made in the application: + +1. Define the type of your sites config + + Preferably this should be done in `config.ts`: + + ```diff + export function createConfig() { + // ... + + return { + ...cometConfig, + apiUrl: environmentVariables.API_URL, + adminUrl: environmentVariables.ADMIN_URL, + + sitesConfig: JSON.parse(environmentVariables.SITES_CONFIG) as SitesConfig, + }; + } + + + export type SitesConfig = Record; + ``` + +2. Use the type when using `useSitesConfig` + + ```diff + - const sitesConfig = useSitesConfig(); + + const sitesConfig = useSitesConfig(); + ``` + +3. Optional: Remove type annotation from `ContentScopeProvider#resolveSiteConfigForScope` (as it's now inferred) + + ```diff + - resolveSiteConfigForScope: (configs: Record, scope: ContentScope) => configs[scope.domain], + + resolveSiteConfigForScope: (configs, scope: ContentScope) => configs[scope.domain], + ``` diff --git a/.changeset/sixty-cobras-brake.md b/.changeset/sixty-cobras-brake.md index bb9ee035ea..cebcfff8b3 100644 --- a/.changeset/sixty-cobras-brake.md +++ b/.changeset/sixty-cobras-brake.md @@ -7,7 +7,8 @@ Replace ContentScopeModule with UserPermissionsModule Breaking changes: - ContentScope-Module has been removed -- canAccessScope has been moved to AccessControlService and renamed to isAllowedContentScope +- canAccessScope has been moved to AccessControlService and refactored into isAllowed +- contentScopes- and permissions-fields have been added to CurrentUser-Object - role- and rights-fields has been removed from CurrentUser-Object - AllowForRole-decorator has been removed - Rename decorator SubjectEntity to AffectedEntity diff --git a/.changeset/smart-weeks-behave.md b/.changeset/smart-weeks-behave.md deleted file mode 100644 index c341c49c13..0000000000 --- a/.changeset/smart-weeks-behave.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -"@comet/admin": minor ---- - -Add `Alert` component - -**Example:** - -```tsx -import { Alert, OkayButton, SaveButton } from "@comet/admin"; - -}> - Action Text - - } -> - Notification Text - -``` \ No newline at end of file diff --git a/.changeset/stale-wombats-reply.md b/.changeset/stale-wombats-reply.md new file mode 100644 index 0000000000..1291db3de7 --- /dev/null +++ b/.changeset/stale-wombats-reply.md @@ -0,0 +1,30 @@ +--- +"@comet/admin": major +--- + +Change the signatures of `shouldScrollToField`, `shouldShowFieldError` and `shouldShowFieldWarning` in `FinalFormContext` to match the corresponding methods in `Field` + +The API in `FinalFormContext` was changed from + +```tsx +// ❌ +export interface FinalFormContext { + shouldScrollToField: ({ fieldMeta }: { fieldMeta: FieldMetaState }) => boolean; + shouldShowFieldError: ({ fieldMeta }: { fieldMeta: FieldMetaState }) => boolean; + shouldShowFieldWarning: ({ fieldMeta }: { fieldMeta: FieldMetaState }) => boolean; +} +``` + +to + +```tsx +// ✅ +export interface FinalFormContext { + shouldScrollToField: (fieldMeta: FieldMetaState) => boolean; + shouldShowFieldError: (fieldMeta: FieldMetaState) => boolean; + shouldShowFieldWarning: (fieldMeta: FieldMetaState) => boolean; +} +``` + +Now the corresponding methods in `Field` and `FinalFormContext` have the same signature. + diff --git a/.changeset/tricky-adults-laugh.md b/.changeset/tricky-adults-laugh.md deleted file mode 100644 index 4c34ff0d0e..0000000000 --- a/.changeset/tricky-adults-laugh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@comet/cms-admin": minor ---- - -Make all DAM license fields optional if `LicenseType` is `ROYALTY_FREE` even if `requireLicense` is true in `DamConfig` diff --git a/.changeset/yellow-seahorses-lick.md b/.changeset/yellow-seahorses-lick.md deleted file mode 100644 index 71ca251373..0000000000 --- a/.changeset/yellow-seahorses-lick.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -"@comet/cms-admin": major -"@comet/cms-api": major -"@comet/eslint-config": minor -"@comet/cms-site": minor ---- - -Migrate site preview to Next.js Preview Mode - -Requires following changes to site: - -- Import `useRouter` from `next/router` (not exported from `@comet/cms-site` anymore) -- Import `Link` from `next/link` (not exported from `@comet/cms-site` anymore) -- Remove preview pages (pages in `src/pages/preview/` directory which call `createGetUniversalProps` with preview parameters) -- Remove `createGetUniversalProps` - - Just implement `getStaticProps`/`getServerSideProps` (Preview Mode will SSR automatically) - - Get `previewData` from `context` and use it to configure the GraphQL Client -- Add `SitePreviewProvider` to `App` (typically in `src/pages/_app.tsx`) -- Add `/api/preview` Next API route (see demo) - -Requires following changes to API: - -- Set `sitePreviewSecret` in `PageTreeModule`-options (make sure it's the same across multiple API-instances) diff --git a/.env b/.env index 0f565b2496..87c2bf6309 100644 --- a/.env +++ b/.env @@ -42,7 +42,6 @@ API_PORT=4000 API_URL=http://localhost:$API_PORT API_URL_INTERNAL=http://localhost:$API_PORT CORS_ALLOWED_ORIGINS="^http:\/\/localhost:\d+,^http://192.168.\d+.\d+:80[0-9]{2}" -SITE_PREVIEW_SECRET=uxa4ZBJ4exw8jgq-qrh # blob storage BLOB_STORAGE_DRIVER="file" @@ -65,6 +64,7 @@ SITE_PORT=3000 SITE_URL=http://localhost:$SITE_PORT SITE_PRELOGIN_ENABLED=false SITE_PRELOGIN_PASSWORD=password +PREVIEW_URL=$SITE_URL/preview # no gtm in dev mode NEXT_PUBLIC_GTM_ID= NEXT_PUBLIC_SITE_DOMAIN=main diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml index b0ea7f3183..145ba47897 100644 --- a/.github/auto_assign.yml +++ b/.github/auto_assign.yml @@ -4,3 +4,7 @@ addReviewers: true reviewers: - johnnyomair + +skipKeywords: + - Merge main into next + - Version Packages diff --git a/copy-schema-files.js b/copy-schema-files.js index d2a25e5c41..c6f9f8d1f7 100644 --- a/copy-schema-files.js +++ b/copy-schema-files.js @@ -11,7 +11,7 @@ const fs = require("fs"); fs.promises.copyFile("demo/api/block-meta.json", "demo/site/block-meta.json"), fs.promises.copyFile("demo/api/schema.gql", "demo/admin/schema.gql"), fs.promises.copyFile("demo/api/schema.gql", "demo/site/schema.gql"), - fs.promises.copyFile("demo/api/comet-config.json", "demo/site/comet-config.json"), - fs.promises.copyFile("demo/api/comet-config.json", "demo/admin/comet-config.json"), + fs.promises.copyFile("demo/api/src/comet-config.json", "demo/site/src/comet-config.json"), + fs.promises.copyFile("demo/api/src/comet-config.json", "demo/admin/src/comet-config.json"), ]); })(); diff --git a/demo/admin/.gitignore b/demo/admin/.gitignore index 170cb27250..fe900533ad 100644 --- a/demo/admin/.gitignore +++ b/demo/admin/.gitignore @@ -12,5 +12,5 @@ lang-compiled graphql.generated.ts block-meta.json src/blocks.generated.ts -comet-config.json +src/comet-config.json src/**/*.generated.ts diff --git a/demo/admin/package.json b/demo/admin/package.json index b38a0c34d5..6532122413 100644 --- a/demo/admin/package.json +++ b/demo/admin/package.json @@ -9,7 +9,6 @@ "generate-block-types:watch": "chokidar -s \"**/block-meta.json\" -c \"npm run generate-block-types\"", "gql:types": "graphql-codegen", "gql:watch": "graphql-codegen --watch", - "postinstall": "cd server && npm install", "intl:compile": "run-p intl:compile:comet intl:compile:comet-demo", "intl:compile:comet": "formatjs compile-folder --format simple --ast lang/comet-lang lang-compiled/comet-lang", "intl:compile:comet-demo": "formatjs compile-folder --format simple --ast lang/comet-demo-lang lang-compiled/comet-demo-lang", diff --git a/demo/admin/server/package-lock.json b/demo/admin/server/package-lock.json deleted file mode 100644 index e345ac25e1..0000000000 --- a/demo/admin/server/package-lock.json +++ /dev/null @@ -1,1153 +0,0 @@ -{ - "name": "server", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "server", - "version": "1.0.0", - "dependencies": { - "compression": "^1.7.4", - "express": "^4.18.2" - }, - "engines": { - "node": "18", - "npm": "9" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "engines": { - "node": ">= 0.8" - } - } - }, - "dependencies": { - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - } - } - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - } - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - } - } -} diff --git a/demo/admin/server/package.json b/demo/admin/server/package.json index 027a4e92be..62f1a66a09 100644 --- a/demo/admin/server/package.json +++ b/demo/admin/server/package.json @@ -8,9 +8,5 @@ "scripts": { "preserve": "envsubst < \"../build/index.html\" > \"/tmp/index.html\" && mv /tmp/index.html ../build/index.html", "serve": "node server.js" - }, - "engines": { - "node": "18", - "npm": "9" } } diff --git a/demo/admin/src/App.tsx b/demo/admin/src/App.tsx index 057e1c87b8..b19426879f 100644 --- a/demo/admin/src/App.tsx +++ b/demo/admin/src/App.tsx @@ -14,7 +14,6 @@ import { DamConfigProvider, LocaleProvider, MasterMenuRoutes, - SiteConfig, SitePreview, SitesConfigProvider, } from "@comet/cms-admin"; @@ -23,6 +22,7 @@ import { createApolloClient } from "@src/common/apollo/createApolloClient"; import ContentScopeProvider, { ContentScope } from "@src/common/ContentScopeProvider"; import { additionalPageTreeNodeFieldsFragment } from "@src/common/EditPageNode"; import { createConfig } from "@src/config"; +import { ImportFromUnsplash } from "@src/dam/ImportFromUnsplash"; import { pageTreeCategories } from "@src/pageTree/pageTreeCategories"; import theme from "@src/theme"; import * as React from "react"; @@ -62,10 +62,10 @@ class App extends React.Component { , scope: ContentScope) => configs[scope.domain], + resolveSiteConfigForScope: (configs, scope: ContentScope) => configs[scope.domain], }} > - + }}> scope.domain}> diff --git a/demo/admin/src/common/ContentScopeProvider.tsx b/demo/admin/src/common/ContentScopeProvider.tsx index 65e4a5bd89..98793dd476 100644 --- a/demo/admin/src/common/ContentScopeProvider.tsx +++ b/demo/admin/src/common/ContentScopeProvider.tsx @@ -12,6 +12,7 @@ import { useCurrentUser, useSitesConfig, } from "@comet/cms-admin"; +import { SitesConfig } from "@src/config"; import React from "react"; type Domain = "main" | "secondary" | string; @@ -50,10 +51,10 @@ export function useContentScopeConfig(p: ContentScopeConfigProps): void { } const ContentScopeProvider: React.FC> = ({ children }) => { - const sitesConfig = useSitesConfig(); + const sitesConfig = useSitesConfig(); const user = useCurrentUser(); - const allowedUserDomains = user.contentScopes.map((scope) => scope.domain); + const allowedUserDomains = user.allowedContentScopes.map((scope) => scope.domain); const allowedSiteConfigs = Object.fromEntries( Object.entries(sitesConfig.configs).filter(([siteKey, siteConfig]) => allowedUserDomains.includes(siteKey)), diff --git a/demo/admin/src/common/MasterMenu.tsx b/demo/admin/src/common/MasterMenu.tsx index d0578b60a2..5c0a392cf0 100644 --- a/demo/admin/src/common/MasterMenu.tsx +++ b/demo/admin/src/common/MasterMenu.tsx @@ -9,6 +9,7 @@ import { PublisherPage, UserPermissionsPage, } from "@comet/cms-admin"; +import { ImportFromUnsplash } from "@src/dam/ImportFromUnsplash"; import Dashboard from "@src/dashboard/Dashboard"; import { GQLPageTreeNodeCategory } from "@src/graphql.generated"; import { Link } from "@src/links/Link"; @@ -96,7 +97,12 @@ export const masterMenuData: MasterMenuData = [ icon: , route: { path: "/assets", - render: () => } />, + render: () => ( + } + additionalToolbarItems={} + /> + ), }, requiredPermission: "dam", }, @@ -143,6 +149,7 @@ export const masterMenuData: MasterMenuData = [ requiredPermission: "pageTree", }, ], + requiredPermission: "pageTree", }, { primary: , diff --git a/demo/admin/src/config.ts b/demo/admin/src/config.ts index cd49ec3d6a..63d861c641 100644 --- a/demo/admin/src/config.ts +++ b/demo/admin/src/config.ts @@ -1,4 +1,6 @@ -import cometConfig from "../comet-config.json"; +import { SiteConfig } from "@comet/cms-admin"; + +import cometConfig from "./comet-config.json"; import environment from "./environment"; export function createConfig() { @@ -18,8 +20,10 @@ export function createConfig() { ...cometConfig, apiUrl: environmentVariables.API_URL, adminUrl: environmentVariables.ADMIN_URL, - sitesConfig: JSON.parse(environmentVariables.SITES_CONFIG), + sitesConfig: JSON.parse(environmentVariables.SITES_CONFIG) as SitesConfig, }; } +export type SitesConfig = Record; + export type Config = ReturnType; diff --git a/demo/admin/src/dam/ImportFromUnsplash.tsx b/demo/admin/src/dam/ImportFromUnsplash.tsx new file mode 100644 index 0000000000..3bb707b455 --- /dev/null +++ b/demo/admin/src/dam/ImportFromUnsplash.tsx @@ -0,0 +1,79 @@ +import { CancelButton, messages, SaveButton } from "@comet/admin"; +import { useCurrentDamFolder, useDamAcceptedMimeTypes, useDamFileUpload } from "@comet/cms-admin"; +import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material"; +import { styled } from "@mui/material/styles"; +import * as React from "react"; +import { FormattedMessage } from "react-intl"; + +import { getRandomUnsplashImage, UnsplashImage } from "./getRandomUnsplashImage"; +import UnsplashIcon from "./UnsplashIcon"; + +export const ImportFromUnsplash: React.FC = () => { + const { allAcceptedMimeTypes } = useDamAcceptedMimeTypes(); + const { folderId } = useCurrentDamFolder(); + const [isOpen, setIsOpen] = React.useState(false); + const [unsplashImage, setUnsplashImage] = React.useState(); + + const { uploadFiles } = useDamFileUpload({ + acceptedMimetypes: allAcceptedMimeTypes, + }); + + const handleOpenDialog = async () => { + const image = await getRandomUnsplashImage(); + setUnsplashImage(image); + setIsOpen(true); + }; + + const handleCloseDialog = () => { + setIsOpen(false); + }; + + const handleSave = async () => { + if (unsplashImage === undefined) return; + await uploadFiles( + { acceptedFiles: [unsplashImage.file], fileRejections: [] }, + { + folderId, + importSource: { + importSourceId: unsplashImage.url, + importSourceType: "unsplash", + }, + }, + ); + handleCloseDialog(); + }; + + const handleShuffle = async () => { + const image = await getRandomUnsplashImage(); + setUnsplashImage(image); + }; + + return ( + <> + + +
+ Import from Unsplash + + + + + + + + + + +
+
+ + ); +}; + +const ImagePreview = styled("img")` + max-width: 100%; +`; diff --git a/demo/admin/src/dam/UnsplashIcon.tsx b/demo/admin/src/dam/UnsplashIcon.tsx new file mode 100644 index 0000000000..c0781c0bd9 --- /dev/null +++ b/demo/admin/src/dam/UnsplashIcon.tsx @@ -0,0 +1,10 @@ +import { SvgIcon, SvgIconProps } from "@mui/material"; +import * as React from "react"; + +export default function UnsplashIcon(props: SvgIconProps): JSX.Element { + return ( + + + + ); +} diff --git a/demo/admin/src/dam/getRandomUnsplashImage.ts b/demo/admin/src/dam/getRandomUnsplashImage.ts new file mode 100644 index 0000000000..d056e9c60e --- /dev/null +++ b/demo/admin/src/dam/getRandomUnsplashImage.ts @@ -0,0 +1,46 @@ +export interface UnsplashImage { + file: File; + url: string; +} + +async function fetchUnsplashImage(url: string) { + const response = await fetch(url); + + if (!response.ok) { + throw new Error("Failed to fetch image"); + } + + return { + blob: await response.blob(), + origin: response.url, + }; +} + +function extractFileNameFromUrl(url: string): string { + const fileNameWithQuery = url.split("?")[0]; + const fileName = fileNameWithQuery.split("/").pop(); + return fileName ? `${fileName}.jpeg` : "unnamed.jpeg"; +} + +export async function getRandomUnsplashImage(): Promise { + const imageUrl = "https://source.unsplash.com/all/"; + + try { + const image = await fetchUnsplashImage(imageUrl); + const mimeType = image.blob.type; + + if (mimeType !== "image/jpeg") { + return getRandomUnsplashImage(); + } + + const fileName = extractFileNameFromUrl(image.origin); + const acceptedFile = new File([image.blob], fileName, { type: mimeType }); + + return { + file: acceptedFile, + url: image.origin, + }; + } catch (error) { + throw new Error(`Failed to fetch image: ${error}`); + } +} diff --git a/demo/admin/src/dashboard/Dashboard.tsx b/demo/admin/src/dashboard/Dashboard.tsx index 340171810f..01dbed5051 100644 --- a/demo/admin/src/dashboard/Dashboard.tsx +++ b/demo/admin/src/dashboard/Dashboard.tsx @@ -1,5 +1,5 @@ import { MainContent, Stack } from "@comet/admin"; -import { DashboardHeader, LatestBuildsDashboardWidget } from "@comet/cms-admin"; +import { DashboardHeader, LatestBuildsDashboardWidget, useUserPermissionCheck } from "@comet/cms-admin"; import { Grid } from "@mui/material"; import { ContentScopeIndicator } from "@src/common/ContentScopeIndicator"; import * as React from "react"; @@ -11,7 +11,7 @@ import { LatestContentUpdates } from "./LatestContentUpdates"; const Dashboard: React.FC = () => { const intl = useIntl(); - + const isAllowed = useUserPermissionCheck(); return ( { - + {isAllowed("pageTree") && } {process.env.NODE_ENV !== "development" && } diff --git a/demo/admin/src/pages/PageContentBlock.tsx b/demo/admin/src/pages/PageContentBlock.tsx index 4a1373ebe8..85af38da69 100644 --- a/demo/admin/src/pages/PageContentBlock.tsx +++ b/demo/admin/src/pages/PageContentBlock.tsx @@ -12,6 +12,7 @@ import * as React from "react"; import { ColumnsBlock } from "./blocks/ColumnsBlock"; import { FullWidthImageBlock } from "./blocks/FullWidthImageBlock"; import { MediaBlock } from "./blocks/MediaBlock"; +import { TeaserBlock } from "./blocks/TeaserBlock"; import { TwoListsBlock } from "./blocks/TwoListsBlock"; import { VideoBlock } from "./blocks/VideoBlock"; @@ -32,6 +33,7 @@ export const PageContentBlock = createBlocksBlock({ anchor: AnchorBlock, twoLists: TwoListsBlock, media: MediaBlock, + teaser: TeaserBlock, }, additionalItemFields: { ...userGroupAdditionalItemFields, diff --git a/demo/admin/src/pages/blocks/TeaserBlock.tsx b/demo/admin/src/pages/blocks/TeaserBlock.tsx new file mode 100644 index 0000000000..cf57887c38 --- /dev/null +++ b/demo/admin/src/pages/blocks/TeaserBlock.tsx @@ -0,0 +1,43 @@ +import { BlockCategory, createCompositeBlock } from "@comet/blocks-admin"; +import { DamImageBlock } from "@comet/cms-admin"; +import { HeadlineBlock } from "@src/common/blocks/HeadlineBlock"; +import { LinkListBlock } from "@src/common/blocks/LinkListBlock"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +const TeaserBlock = createCompositeBlock( + { + name: "Teaser", + displayName: , + blocks: { + // Normal + headline: { + block: HeadlineBlock, + title: , + }, + // Nested + image: { + block: DamImageBlock, + title: , + nested: true, + }, + // Subroutes + links: { + block: LinkListBlock, + title: , + }, + // Nested inner subroutes + buttons: { + block: LinkListBlock, + title: , + nested: true, + }, + }, + }, + (block) => { + block.category = BlockCategory.Teaser; + return block; + }, +); + +export { TeaserBlock }; diff --git a/demo/api/block-meta.json b/demo/api/block-meta.json index 9c6d39d841..3b491a06dc 100644 --- a/demo/api/block-meta.json +++ b/demo/api/block-meta.json @@ -1073,7 +1073,8 @@ "columns": "Columns", "anchor": "Anchor", "twoLists": "TwoLists", - "media": "Media" + "media": "Media", + "teaser": "Teaser" }, "nullable": false }, @@ -1130,7 +1131,8 @@ "columns": "Columns", "anchor": "Anchor", "twoLists": "TwoLists", - "media": "Media" + "media": "Media", + "teaser": "Teaser" }, "nullable": false }, @@ -1737,6 +1739,61 @@ } ] }, + { + "name": "Teaser", + "fields": [ + { + "name": "headline", + "kind": "Block", + "block": "Headline", + "nullable": false + }, + { + "name": "image", + "kind": "Block", + "block": "DamImage", + "nullable": false + }, + { + "name": "links", + "kind": "Block", + "block": "LinkList", + "nullable": false + }, + { + "name": "buttons", + "kind": "Block", + "block": "LinkList", + "nullable": false + } + ], + "inputFields": [ + { + "name": "headline", + "kind": "Block", + "block": "Headline", + "nullable": false + }, + { + "name": "image", + "kind": "Block", + "block": "DamImage", + "nullable": false + }, + { + "name": "links", + "kind": "Block", + "block": "LinkList", + "nullable": false + }, + { + "name": "buttons", + "kind": "Block", + "block": "LinkList", + "nullable": false + } + ] + }, { "name": "TextImage", "fields": [ diff --git a/demo/api/schema.gql b/demo/api/schema.gql index 5a1421bb39..6709484995 100644 --- a/demo/api/schema.gql +++ b/demo/api/schema.gql @@ -67,8 +67,6 @@ type DamFileLicense { enum LicenseType { ROYALTY_FREE RIGHTS_MANAGED - SUBSCRIPTION - MICRO } """ @@ -148,6 +146,7 @@ type FilenameResponse { type CurrentUserPermission { permission: String! + contentScopes: [JSONObject!]! } type CurrentUser { @@ -155,7 +154,6 @@ type CurrentUser { name: String! email: String! language: String! - contentScopes: [JSONObject!]! permissions: [CurrentUserPermission!]! } @@ -175,6 +173,8 @@ type UserPermission { reason: String requestedBy: String approvedBy: String + overrideContentScopes: Boolean! + contentScopes: [JSONObject!]! } enum UserPermissionSource { @@ -321,6 +321,8 @@ type DamFile { license: DamFileLicense createdAt: DateTime! updatedAt: DateTime! + importSourceId: String + importSourceType: String scope: DamScope! fileUrl: String! duplicates: [DamFile!]! @@ -622,7 +624,6 @@ type Query { pageTreeNodeList(scope: PageTreeNodeScopeInput!, category: String): [PageTreeNode!]! paginatedPageTreeNodes(scope: PageTreeNodeScopeInput!, category: String, sort: [PageTreeNodeSort!], offset: Int! = 0, limit: Int! = 25): PaginatedPageTreeNodes! pageTreeNodeSlugAvailable(scope: PageTreeNodeScopeInput!, parentId: ID, slug: String!): SlugAvailability! - getSitePreviewJwt(path: String!, previewData: PreviewData!): String! redirects(scope: RedirectScopeInput!, query: String, type: RedirectGenerationType, active: Boolean, sortColumnName: String, sortDirection: SortDirection! = ASC): [Redirect!]! @deprecated(reason: "Use paginatedRedirects instead. Will be removed in the next version.") paginatedRedirects(scope: RedirectScopeInput!, search: String, filter: RedirectFilter, sort: [RedirectSort!], offset: Int! = 0, limit: Int! = 25): PaginatedRedirects! redirect(id: ID!): Redirect! @@ -710,10 +711,6 @@ enum SlugAvailability { Reserved } -input PreviewData { - includeInvisible: Boolean! -} - input RedirectFilter { generationType: StringFilter source: StringFilter @@ -914,7 +911,8 @@ type Mutation { userPermissionsCreatePermission(userId: String!, input: UserPermissionInput!): UserPermission! userPermissionsUpdatePermission(id: String!, input: UserPermissionInput!): UserPermission! userPermissionsDeletePermission(id: ID!): Boolean! - userPermissionsUpdateContentScopes(userId: String!, input: UserContentScopesInput!): [JSONObject!]! + userPermissionsUpdateOverrideContentScopes(input: UserPermissionOverrideContentScopesInput!): UserPermission! + userPermissionsUpdateContentScopes(userId: String!, input: UserContentScopesInput!): Boolean! createBuilds(input: CreateBuildsInput!): Boolean! saveLink(linkId: ID!, input: LinkInput!, attachedPageTreeNodeId: ID!, lastUpdatedAt: DateTime): Link! savePage(pageId: ID!, input: PageInput!, attachedPageTreeNodeId: ID!, lastUpdatedAt: DateTime): Page! @@ -975,6 +973,12 @@ input UserPermissionInput { approvedBy: String } +input UserPermissionOverrideContentScopesInput { + permissionId: ID! + overrideContentScopes: Boolean! + contentScopes: [JSONObject!]! = [] +} + input UserContentScopesInput { contentScopes: [JSONObject!]! = [] } diff --git a/demo/api/src/app.module.ts b/demo/api/src/app.module.ts index 33a911e6fc..9612cdd121 100644 --- a/demo/api/src/app.module.ts +++ b/demo/api/src/app.module.ts @@ -117,7 +117,6 @@ export class AppModule { Documents: [Page, Link, PredefinedPage], Scope: PageTreeNodeScope, reservedPaths: ["/events"], - sitePreviewSecret: config.sitePreviewSecret, }), RedirectsModule.register({ customTargets: { news: NewsLinkBlock }, Scope: RedirectScope }), BlobStorageModule.register({ diff --git a/demo/api/src/auth/access-control.service.ts b/demo/api/src/auth/access-control.service.ts index 8ffffe8e7a..67c8e47d69 100644 --- a/demo/api/src/auth/access-control.service.ts +++ b/demo/api/src/auth/access-control.service.ts @@ -7,7 +7,7 @@ export class AccessControlService extends AbstractAccessControlService { if (user.email.endsWith("@comet-dxp.com")) { return UserPermissions.allPermissions; } else { - return [{ permission: "news" }]; + return [{ permission: "products" }, { permission: "news", contentScopes: [{ domain: "secondary", language: "en" }] }]; } } getContentScopesForUser(user: User): ContentScopesForUser { diff --git a/demo/api/comet-config.json b/demo/api/src/comet-config.json similarity index 100% rename from demo/api/comet-config.json rename to demo/api/src/comet-config.json diff --git a/demo/api/src/config/config.ts b/demo/api/src/config/config.ts index 550414aded..fb2ee6b1c0 100644 --- a/demo/api/src/config/config.ts +++ b/demo/api/src/config/config.ts @@ -1,4 +1,4 @@ -import cometConfig from "@src/../comet-config.json"; +import cometConfig from "@src/comet-config.json"; import { plainToClass } from "class-transformer"; import { validateSync } from "class-validator"; @@ -48,7 +48,6 @@ export function createConfig(processEnv: NodeJS.ProcessEnv) { }, storageDirectoryPrefix: envVars.BLOB_STORAGE_DIRECTORY_PREFIX, }, - sitePreviewSecret: envVars.SITE_PREVIEW_SECRET, }; } diff --git a/demo/api/src/config/environment-variables.ts b/demo/api/src/config/environment-variables.ts index 043900dd54..bb2f51399e 100644 --- a/demo/api/src/config/environment-variables.ts +++ b/demo/api/src/config/environment-variables.ts @@ -92,7 +92,4 @@ export class EnvironmentVariables { @ValidateIf((v) => v.DAM_STORAGE_DRIVER === "s3") @IsString() S3_BUCKET: string; - - @IsString() - SITE_PREVIEW_SECRET: string; } diff --git a/demo/api/src/db/fixtures/fixtures.console.ts b/demo/api/src/db/fixtures/fixtures.console.ts index 23d4691f92..8eb643f157 100644 --- a/demo/api/src/db/fixtures/fixtures.console.ts +++ b/demo/api/src/db/fixtures/fixtures.console.ts @@ -1,11 +1,4 @@ -import { - BlobStorageBackendService, - FilesService, - FoldersService, - PageTreeNodeInterface, - PageTreeNodeVisibility, - PageTreeService, -} from "@comet/cms-api"; +import { BlobStorageBackendService, PageTreeNodeInterface, PageTreeNodeVisibility, PageTreeService } from "@comet/cms-api"; import { MikroORM, UseRequestContext } from "@mikro-orm/core"; import { InjectRepository } from "@mikro-orm/nestjs"; import { EntityRepository } from "@mikro-orm/postgresql"; @@ -25,7 +18,7 @@ import { Command, Console } from "nestjs-console"; import slugify from "slugify"; import { generateLinks } from "./generators/links.generator"; -import { ManyImagesTestPageGenerator } from "./generators/many-images-test-page.generator"; +import { ManyImagesTestPageFixtureService } from "./generators/many-images-test-page-fixture.service"; import { PublicUploadsFixtureService } from "./generators/public-uploads-fixture.service"; export interface PageTreeNodesFixtures { @@ -51,11 +44,10 @@ export class FixturesConsole { @Inject(CONFIG) private readonly config: Config, private readonly blobStorageBackendService: BlobStorageBackendService, private readonly pageTreeService: PageTreeService, - private readonly filesService: FilesService, - private readonly foldersService: FoldersService, private readonly orm: MikroORM, @InjectRepository(Page) private readonly pagesRepository: EntityRepository, @InjectRepository(Link) private readonly linksRepository: EntityRepository, + private readonly manyImagesTestPageFixtureService: ManyImagesTestPageFixtureService, private readonly publicUploadsFixtureService: PublicUploadsFixtureService, ) {} @@ -221,8 +213,7 @@ export class FixturesConsole { console.log("links generated"); console.log("generate many images test page"); - const manyImagesTestGenerator = new ManyImagesTestPageGenerator(this.pageTreeService, this.filesService, this.pagesRepository); - await manyImagesTestGenerator.execute(); + await this.manyImagesTestPageFixtureService.execute(); console.log("many images test page created"); console.log("generate lorem ispum fixtures"); diff --git a/demo/api/src/db/fixtures/fixtures.module.ts b/demo/api/src/db/fixtures/fixtures.module.ts index 4eb2038882..73dd8ef35f 100644 --- a/demo/api/src/db/fixtures/fixtures.module.ts +++ b/demo/api/src/db/fixtures/fixtures.module.ts @@ -5,10 +5,19 @@ import { LinksModule } from "@src/links/links.module"; import { PagesModule } from "@src/pages/pages.module"; import { ConsoleModule } from "nestjs-console"; +import { ManyImagesTestPageFixtureService } from "./generators/many-images-test-page-fixture.service"; import { PublicUploadsFixtureService } from "./generators/public-uploads-fixture.service"; +import { SvgImageFileFixtureService } from "./generators/svg-image-file-fixture.service"; +import { UnsplashImageFileFixtureService } from "./generators/unsplash-image-file-fixture.service"; @Module({ imports: [ConfigModule, ConsoleModule, PagesModule, LinksModule], - providers: [FixturesConsole, PublicUploadsFixtureService], + providers: [ + FixturesConsole, + ManyImagesTestPageFixtureService, + UnsplashImageFileFixtureService, + SvgImageFileFixtureService, + PublicUploadsFixtureService, + ], }) export class FixturesModule {} diff --git a/demo/api/src/db/fixtures/generators/image-file.fixture.ts b/demo/api/src/db/fixtures/generators/image-file.fixture.ts deleted file mode 100644 index 0d4202b5ed..0000000000 --- a/demo/api/src/db/fixtures/generators/image-file.fixture.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { download, FileInterface, FilesService } from "@comet/cms-api"; -import { DamScope } from "@src/dam/dto/dam-scope"; -import path from "path"; - -const images = ["01.jpg", "02.jpg", "03.jpg", "04.jpg", "05.jpg"]; - -export const generateImageFiles = async (filesService: FilesService, scope: DamScope): Promise => { - const files: FileInterface[] = []; - - for (const image of images) { - const file = await download(path.resolve(`./src/db/fixtures/generators/images/${image}`)); - files.push(await filesService.upload(file, { scope })); - } - - return files; -}; diff --git a/demo/api/src/db/fixtures/generators/many-images-test-page.generator.ts b/demo/api/src/db/fixtures/generators/many-images-test-page-fixture.service.ts similarity index 78% rename from demo/api/src/db/fixtures/generators/many-images-test-page.generator.ts rename to demo/api/src/db/fixtures/generators/many-images-test-page-fixture.service.ts index 6cdc3f3fb0..b1e079e251 100644 --- a/demo/api/src/db/fixtures/generators/many-images-test-page.generator.ts +++ b/demo/api/src/db/fixtures/generators/many-images-test-page-fixture.service.ts @@ -1,5 +1,7 @@ -import { FilesService, PageTreeNodeVisibility, PageTreeService } from "@comet/cms-api"; +import { PageTreeNodeVisibility, PageTreeService } from "@comet/cms-api"; +import { InjectRepository } from "@mikro-orm/nestjs"; import { EntityRepository } from "@mikro-orm/postgresql"; +import { Injectable } from "@nestjs/common"; import { DamScope } from "@src/dam/dto/dam-scope"; import { PageTreeNodeScope } from "@src/page-tree/dto/page-tree-node-scope"; import { PageTreeNodeCategory } from "@src/page-tree/page-tree-node-category"; @@ -11,23 +13,19 @@ import faker from "faker"; import { generateImageBlock } from "./blocks/image.generator"; import { generateSeoBlock } from "./blocks/seo.generator"; -import { SvgImageFileFixture } from "./svg-image-file.fixture"; -import { UnsplashImageFileFixture } from "./unsplash-image-file.fixture"; +import { SvgImageFileFixtureService } from "./svg-image-file-fixture.service"; +import { UnsplashImageFileFixtureService } from "./unsplash-image-file-fixture.service"; const IMAGES_NUMBER = 10; -export class ManyImagesTestPageGenerator { - private readonly unsplashImageFileFixture: UnsplashImageFileFixture; - private readonly svgImageFileFixture: SvgImageFileFixture; - +@Injectable() +export class ManyImagesTestPageFixtureService { constructor( private readonly pageTreeService: PageTreeService, - filesService: FilesService, - private readonly pagesRespository: EntityRepository, - ) { - this.unsplashImageFileFixture = new UnsplashImageFileFixture(filesService); - this.svgImageFileFixture = new SvgImageFileFixture(filesService); - } + @InjectRepository(Page) private readonly pagesRespository: EntityRepository, + private readonly unsplashImageFileFixtureService: UnsplashImageFileFixtureService, + private readonly svgImageFileFixtureService: SvgImageFileFixtureService, + ) {} async execute(): Promise { const uuidDocument = "c66ebddd-ecd1-430c-9ea2-8a482c62ad70"; @@ -60,10 +58,10 @@ export class ManyImagesTestPageGenerator { const imageBlocks: ReturnType[] = []; for (let index = 0; index < IMAGES_NUMBER; index++) { - const imageFile = await this.unsplashImageFileFixture.generateImage(damScope); + const imageFile = await this.unsplashImageFileFixtureService.generateImage(damScope); imageBlocks.push(generateImageBlock(imageFile)); } - const svgFile = await this.svgImageFileFixture.generateImage(damScope); + const svgFile = await this.svgImageFileFixtureService.generateImage(damScope); imageBlocks.push(generateImageBlock(svgFile)); const pageInput = new PageInput(); diff --git a/demo/api/src/db/fixtures/generators/svg-image-file-fixture.service.ts b/demo/api/src/db/fixtures/generators/svg-image-file-fixture.service.ts new file mode 100644 index 0000000000..abf117a624 --- /dev/null +++ b/demo/api/src/db/fixtures/generators/svg-image-file-fixture.service.ts @@ -0,0 +1,19 @@ +import { FileInterface, FilesService, FileUploadService } from "@comet/cms-api"; +import { Injectable } from "@nestjs/common"; +import { DamScope } from "@src/dam/dto/dam-scope"; +import path from "path"; + +@Injectable() +export class SvgImageFileFixtureService { + constructor(private readonly filesService: FilesService, private readonly fileUploadService: FileUploadService) {} + + async generateImage(scope: DamScope): Promise { + const file = await this.fileUploadService.createFileUploadInputFromUrl( + path.resolve(`./src/db/fixtures/generators/images/comet-logo-claim.svg`), + ); + // Convert to what the browser would send + file.mimetype = "image/svg+xml"; + file.originalname = "comet-logo-claim.svg"; + return this.filesService.upload(file, { scope }); + } +} diff --git a/demo/api/src/db/fixtures/generators/svg-image-file.fixture.ts b/demo/api/src/db/fixtures/generators/svg-image-file.fixture.ts deleted file mode 100644 index 2c3bdfaee2..0000000000 --- a/demo/api/src/db/fixtures/generators/svg-image-file.fixture.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { download, FileInterface, FilesService } from "@comet/cms-api"; -import { DamScope } from "@src/dam/dto/dam-scope"; -import path from "path"; - -export class SvgImageFileFixture { - constructor(private filesService: FilesService) {} - async generateImage(scope: DamScope): Promise { - const file = await download(path.resolve(`./src/db/fixtures/generators/images/comet-logo-claim.svg`)); - // Convert to what the browser would send - file.mimetype = "image/svg+xml"; - file.originalname = "comet-logo-claim.svg"; - return this.filesService.upload(file, { scope }); - } -} diff --git a/demo/api/src/db/fixtures/generators/unsplash-image-file.fixture.ts b/demo/api/src/db/fixtures/generators/unsplash-image-file-fixture.service.ts similarity index 65% rename from demo/api/src/db/fixtures/generators/unsplash-image-file.fixture.ts rename to demo/api/src/db/fixtures/generators/unsplash-image-file-fixture.service.ts index a2dd845983..cf319eb52d 100644 --- a/demo/api/src/db/fixtures/generators/unsplash-image-file.fixture.ts +++ b/demo/api/src/db/fixtures/generators/unsplash-image-file-fixture.service.ts @@ -1,9 +1,12 @@ -import { download, FileInterface, FilesService } from "@comet/cms-api"; +import { FileInterface, FilesService, FileUploadService } from "@comet/cms-api"; +import { Injectable } from "@nestjs/common"; import { DamScope } from "@src/dam/dto/dam-scope"; import faker from "faker"; -export class UnsplashImageFileFixture { - constructor(private filesService: FilesService) {} +@Injectable() +export class UnsplashImageFileFixtureService { + constructor(private readonly filesService: FilesService, private readonly fileUploadService: FileUploadService) {} + async generateImage(scope: DamScope): Promise { const width = faker.datatype.number({ min: 1000, @@ -16,7 +19,7 @@ export class UnsplashImageFileFixture { const imageUrl = `https://source.unsplash.com/all/${width}x${height}`; console.log(`Downloading ${imageUrl}.`); - const downloadedImage = await download(imageUrl); + const downloadedImage = await this.fileUploadService.createFileUploadInputFromUrl(imageUrl); console.log(`Downloading ${imageUrl} done.`); console.log(`Uploading ${downloadedImage.originalname}.`); const file = await this.filesService.upload(downloadedImage, { scope }); diff --git a/demo/api/src/main.ts b/demo/api/src/main.ts index f2c0816cc8..a24e8fe5b9 100644 --- a/demo/api/src/main.ts +++ b/demo/api/src/main.ts @@ -10,6 +10,7 @@ import { AppModule } from "@src/app.module"; import { useContainer } from "class-validator"; import compression from "compression"; import cookieParser from "cookie-parser"; +import { json } from "express"; import { createConfig } from "./config/config"; @@ -38,6 +39,7 @@ async function bootstrap(): Promise { }), ); + app.use(json({ limit: "1mb" })); // increase default limit of 100kb for saving large pages app.use(compression()); app.use(cookieParser()); diff --git a/demo/api/src/pages/blocks/PageContentBlock.ts b/demo/api/src/pages/blocks/PageContentBlock.ts index 006860244d..a27e10230f 100644 --- a/demo/api/src/pages/blocks/PageContentBlock.ts +++ b/demo/api/src/pages/blocks/PageContentBlock.ts @@ -9,6 +9,7 @@ import { ColumnsBlock } from "./columns.block"; import { FullWidthImageBlock } from "./full-width-image.block"; import { HeadlineBlock } from "./headline.block"; import { MediaBlock } from "./media.block"; +import { TeaserBlock } from "./teaser.block"; import { TextImageBlock } from "./TextImageBlock"; import { TwoListsBlock } from "./two-lists.block"; import { VideoBlock } from "./video.block"; @@ -28,6 +29,7 @@ const supportedBlocks = { anchor: AnchorBlock, twoLists: TwoListsBlock, media: MediaBlock, + teaser: TeaserBlock, }; class BlocksBlockItemData extends BaseBlocksBlockItemData(supportedBlocks) { diff --git a/demo/api/src/pages/blocks/teaser.block.ts b/demo/api/src/pages/blocks/teaser.block.ts new file mode 100644 index 0000000000..9bf85fd5e6 --- /dev/null +++ b/demo/api/src/pages/blocks/teaser.block.ts @@ -0,0 +1,51 @@ +import { + BlockData, + BlockDataInterface, + BlockInput, + ChildBlock, + ChildBlockInput, + createBlock, + ExtractBlockData, + ExtractBlockInput, + inputToData, +} from "@comet/blocks-api"; +import { DamImageBlock } from "@comet/cms-api"; +import { LinkListBlock } from "@src/common/blocks/link-list.block"; + +import { HeadlineBlock } from "./headline.block"; + +class TeaserBlockData extends BlockData { + @ChildBlock(HeadlineBlock) + headline: ExtractBlockData; + + @ChildBlock(DamImageBlock) + image: ExtractBlockData; + + @ChildBlock(LinkListBlock) + links: ExtractBlockData; + + @ChildBlock(LinkListBlock) + buttons: ExtractBlockData; +} + +class TeaserBlockInput extends BlockInput { + @ChildBlockInput(HeadlineBlock) + headline: ExtractBlockInput; + + @ChildBlockInput(DamImageBlock) + image: ExtractBlockInput; + + @ChildBlockInput(LinkListBlock) + links: ExtractBlockInput; + + @ChildBlockInput(LinkListBlock) + buttons: ExtractBlockInput; + + transformToBlockData(): BlockDataInterface { + return inputToData(TeaserBlockData, this); + } +} + +const TeaserBlock = createBlock(TeaserBlockData, TeaserBlockInput, "Teaser"); + +export { TeaserBlock }; diff --git a/demo/site/.gitignore b/demo/site/.gitignore index ebf0abcdf3..b92461e094 100644 --- a/demo/site/.gitignore +++ b/demo/site/.gitignore @@ -45,4 +45,4 @@ public/robots.txt tsconfig.tsbuildinfo -comet-config.json +src/comet-config.json diff --git a/demo/site/next.config.js b/demo/site/next.config.js index d4df1bd33f..bab71efbe6 100644 --- a/demo/site/next.config.js +++ b/demo/site/next.config.js @@ -2,7 +2,7 @@ // @ts-check -const cometConfig = require("./comet-config.json"); +const cometConfig = require("./src/comet-config.json"); /** * @type {import('next').NextConfig['i18n'] | undefined} diff --git a/demo/site/src/blocks/PageContentBlock.tsx b/demo/site/src/blocks/PageContentBlock.tsx index 265176282b..68ae6db59e 100644 --- a/demo/site/src/blocks/PageContentBlock.tsx +++ b/demo/site/src/blocks/PageContentBlock.tsx @@ -1,5 +1,6 @@ import { BlocksBlock, PropsWithData, SupportedBlocks, YouTubeVideoBlock } from "@comet/cms-site"; import { PageContentBlockData } from "@src/blocks.generated"; +import { TeaserBlock } from "@src/documents/pages/blocks/TeaserBlock"; import * as React from "react"; import { AnchorBlock } from "./AnchorBlock"; @@ -29,6 +30,7 @@ const supportedBlocks: SupportedBlocks = { anchor: (props) => , media: (props) => , twoLists: (props) => , + teaser: (props) => , }; export const PageContentBlock: React.FC> = ({ data }) => { diff --git a/demo/site/src/components/Breadcrumbs.tsx b/demo/site/src/components/Breadcrumbs.tsx index 22d67a7039..aa42698415 100644 --- a/demo/site/src/components/Breadcrumbs.tsx +++ b/demo/site/src/components/Breadcrumbs.tsx @@ -1,6 +1,6 @@ +import { Link } from "@comet/cms-site"; import { GridRoot } from "@src/components/common/GridRoot"; import { gql } from "graphql-request"; -import Link from "next/link"; import * as React from "react"; import { GQLBreadcrumbsFragment } from "./Breadcrumbs.generated"; diff --git a/demo/site/src/documents/pages/blocks/TeaserBlock.tsx b/demo/site/src/documents/pages/blocks/TeaserBlock.tsx new file mode 100644 index 0000000000..8bba683d75 --- /dev/null +++ b/demo/site/src/documents/pages/blocks/TeaserBlock.tsx @@ -0,0 +1,29 @@ +import { PropsWithData, withPreview } from "@comet/cms-site"; +import { TeaserBlockData } from "@src/blocks.generated"; +import { DamImageBlock } from "@src/blocks/DamImageBlock"; +import { HeadlineBlock } from "@src/blocks/HeadlineBlock"; +import { LinkListBlock } from "@src/blocks/LinkListBlock"; +import styled from "styled-components"; + +const TeaserBlock = withPreview( + ({ data: { headline, image, links, buttons } }: PropsWithData) => { + return ( + + + + + + + ); + }, + { label: "Teaser" }, +); + +const Root = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +`; + +export { TeaserBlock }; diff --git a/demo/site/src/header/PageLink.tsx b/demo/site/src/header/PageLink.tsx index c9ce0f7f10..1a9017e082 100644 --- a/demo/site/src/header/PageLink.tsx +++ b/demo/site/src/header/PageLink.tsx @@ -1,9 +1,8 @@ +import { Link, useRouter } from "@comet/cms-site"; import { LinkBlock } from "@src/blocks/LinkBlock"; import { GQLPredefinedPage } from "@src/graphql.generated"; import { predefinedPagePaths } from "@src/predefinedPages/predefinedPagePaths"; import { gql } from "graphql-request"; -import Link from "next/link"; -import { useRouter } from "next/router"; import * as React from "react"; import { GQLPageLinkFragment } from "./PageLink.generated"; diff --git a/demo/site/src/news/blocks/NewsLinkBlock.tsx b/demo/site/src/news/blocks/NewsLinkBlock.tsx index 476dc8ee26..89354ba035 100644 --- a/demo/site/src/news/blocks/NewsLinkBlock.tsx +++ b/demo/site/src/news/blocks/NewsLinkBlock.tsx @@ -1,6 +1,5 @@ -import { PropsWithData } from "@comet/cms-site"; +import { Link, PropsWithData } from "@comet/cms-site"; import { NewsLinkBlockData } from "@src/blocks.generated"; -import Link from "next/link"; import * as React from "react"; function NewsLinkBlock({ data: { id }, children }: React.PropsWithChildren>): JSX.Element | null { diff --git a/demo/site/src/pages/[[...path]].tsx b/demo/site/src/pages/[[...path]].tsx index 86d5d4dac2..fa5ce11116 100644 --- a/demo/site/src/pages/[[...path]].tsx +++ b/demo/site/src/pages/[[...path]].tsx @@ -1,20 +1,27 @@ -import { PreviewData } from "@comet/cms-site"; import { defaultLanguage, domain } from "@src/config"; import { GQLPage } from "@src/graphql.generated"; import NotFound404 from "@src/pages/404"; import PageTypePage, { loader as pageTypePageLoader } from "@src/pageTypes/Page"; import createGraphQLClient from "@src/util/createGraphQLClient"; import { gql } from "graphql-request"; -import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from "next"; -import { ParsedUrlQuery } from "querystring"; +import { + GetServerSidePropsContext, + GetServerSidePropsResult, + GetStaticPaths, + GetStaticProps, + GetStaticPropsContext, + GetStaticPropsResult, + InferGetStaticPropsType, +} from "next"; import * as React from "react"; import { GQLPagesQuery, GQLPagesQueryVariables, GQLPageTypeQuery, GQLPageTypeQueryVariables } from "./[[...path]].generated"; -type PageProps = GQLPage & { +interface PageProps { documentType: string; id: string; -}; +} +export type PageUniversalProps = PageProps & GQLPage; export default function Page(props: InferGetStaticPropsType): JSX.Element { if (!pageTypes[props.documentType]) { @@ -47,32 +54,55 @@ const pageTypes = { }, }; -export const getStaticProps: GetStaticProps = async ({ params, previewData, locale = defaultLanguage }) => { - const client = createGraphQLClient(previewData); - const path = params?.path ?? ""; - const scope = { domain, language: locale }; - //fetch pageType - const data = await client.request(pageTypeQuery, { - path: `/${Array.isArray(path) ? path.join("/") : path}`, - scope, - }); - if (!data.pageTreeNodeByPath?.documentType) { - // eslint-disable-next-line no-console - console.log("got no data from api", data, path); - return { notFound: true }; - } - const pageTreeNodeId = data.pageTreeNodeByPath.id; +export const getStaticProps: GetStaticProps = async (context) => { + const getUniversalProps = createGetUniversalProps(); + return getUniversalProps(context); +}; - //pageType dependent query - const { loader: loaderForPageType } = pageTypes[data.pageTreeNodeByPath.documentType]; - return { - props: { - ...(await loaderForPageType({ client, scope, pageTreeNodeId })), - documentType: data.pageTreeNodeByPath.documentType, - id: pageTreeNodeId, - }, +interface CreateGetUniversalPropsOptions { + includeInvisiblePages?: boolean; + includeInvisibleBlocks?: boolean; + previewDamUrls?: boolean; +} + +// a function to create a universal function which can be used as getStaticProps or getServerSideProps (preview) +export function createGetUniversalProps({ + includeInvisiblePages = false, + includeInvisibleBlocks = false, + previewDamUrls = false, +}: CreateGetUniversalPropsOptions = {}) { + return async function getUniversalProps({ + params, + locale = defaultLanguage, + }: Context): Promise< + Context extends GetStaticPropsContext ? GetStaticPropsResult : GetServerSidePropsResult + > { + const client = createGraphQLClient({ includeInvisiblePages, includeInvisibleBlocks, previewDamUrls }); + const path = params?.path ?? ""; + const scope = { domain, language: locale }; + //fetch pageType + const data = await client.request(pageTypeQuery, { + path: `/${Array.isArray(path) ? path.join("/") : path}`, + scope, + }); + if (!data.pageTreeNodeByPath?.documentType) { + // eslint-disable-next-line no-console + console.log("got no data from api", data, path); + return { notFound: true }; + } + const pageTreeNodeId = data.pageTreeNodeByPath.id; + + //pageType dependent query + const { loader: loaderForPageType } = pageTypes[data.pageTreeNodeByPath.documentType]; + return { + props: { + ...(await loaderForPageType({ client, scope, pageTreeNodeId })), + documentType: data.pageTreeNodeByPath.documentType, + id: pageTreeNodeId, + }, + }; }; -}; +} const pagesQuery = gql` query Pages($scope: PageTreeNodeScopeInput!) { diff --git a/demo/site/src/pages/_app.tsx b/demo/site/src/pages/_app.tsx index 3eb9208691..e919bf2b5c 100644 --- a/demo/site/src/pages/_app.tsx +++ b/demo/site/src/pages/_app.tsx @@ -1,4 +1,3 @@ -import { SitePreviewProvider } from "@comet/cms-site"; import theme, { Theme } from "@src/theme"; import { AppProps, NextWebVitalsMetric } from "next/app"; import Head from "next/head"; @@ -66,9 +65,7 @@ export default function App({ Component, pageProps }: AppProps): JSX.Element { - - - +
); diff --git a/demo/site/src/pages/api/preview.ts b/demo/site/src/pages/api/preview.ts deleted file mode 100644 index 8e0c7b4c57..0000000000 --- a/demo/site/src/pages/api/preview.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { handlePreviewApiRequest } from "@comet/cms-site"; - -export default async function handler(req, res) { - handlePreviewApiRequest(req, res); -} diff --git a/demo/site/src/pages/preview/[[...path]].tsx b/demo/site/src/pages/preview/[[...path]].tsx new file mode 100644 index 0000000000..0333bbf2f1 --- /dev/null +++ b/demo/site/src/pages/preview/[[...path]].tsx @@ -0,0 +1,22 @@ +import { parsePreviewParams, SitePreviewPage } from "@comet/cms-site"; +import Page, { createGetUniversalProps, PageUniversalProps } from "@src/pages/[[...path]]"; +import { GetServerSideProps, GetServerSidePropsContext, InferGetServerSidePropsType } from "next"; +import React from "react"; + +export default function AuthenticatedPreviewPage(props: InferGetServerSidePropsType): JSX.Element { + return ( + + + + ); +} + +export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => { + const { includeInvisibleBlocks } = parsePreviewParams(context.query); + const getUniversalProps = createGetUniversalProps({ + includeInvisiblePages: true, + includeInvisibleBlocks, + previewDamUrls: true, + }); + return getUniversalProps(context); +}; diff --git a/demo/site/src/util/createGraphQLClient.ts b/demo/site/src/util/createGraphQLClient.ts index e30f636390..87ffb051f2 100644 --- a/demo/site/src/util/createGraphQLClient.ts +++ b/demo/site/src/util/createGraphQLClient.ts @@ -1,12 +1,18 @@ -import { PreviewData } from "@comet/cms-site"; import { GraphQLClient } from "graphql-request"; -export default function createGraphQLClient(previewData?: PreviewData): GraphQLClient { - const { includeInvisibleBlocks, includeInvisiblePages, previewDamUrls } = { - includeInvisiblePages: !!previewData, - includeInvisibleBlocks: previewData && previewData.includeInvisible, - previewDamUrls: !!previewData, - }; +export type GraphQLClientOptions = { + includeInvisiblePages: boolean; + includeInvisibleBlocks: boolean; + previewDamUrls: boolean; +}; + +const defaultOptions: GraphQLClientOptions = { + includeInvisiblePages: false, + includeInvisibleBlocks: false, + previewDamUrls: false, +}; +export default function createGraphQLClient(options: Partial = {}): GraphQLClient { + const { includeInvisibleBlocks, includeInvisiblePages, previewDamUrls } = { ...defaultOptions, ...options }; const headers: Record = {}; diff --git a/install.sh b/install.sh index f9cd8dbf9d..874890c9bb 100755 --- a/install.sh +++ b/install.sh @@ -28,13 +28,13 @@ ln -sf ../../.env.local ./demo/api/.env.local ln -sf ../../.env ./demo/admin/.env ln -sf ../api/schema.gql ./demo/admin/schema.gql ln -sf ../api/block-meta.json ./demo/admin/block-meta.json -ln -sf ../api/comet-config.json ./demo/admin/comet-config.json +ln -sf ../../api/src/comet-config.json ./demo/admin/src/comet-config.json # site DEMO ln -sf ../../.env ./demo/site/.env ln -sf ../api/schema.gql ./demo/site/schema.gql ln -sf ../api/block-meta.json ./demo/site/block-meta.json -ln -sf ../api/comet-config.json ./demo/site/comet-config.json +ln -sf ../../api/src/comet-config.json ./demo/site/src/comet-config.json # Lang install sh ./demo/admin/intl-update.sh diff --git a/packages/admin/admin-babel-preset/CHANGELOG.md b/packages/admin/admin-babel-preset/CHANGELOG.md index f6b1feea06..5cbff5a486 100644 --- a/packages/admin/admin-babel-preset/CHANGELOG.md +++ b/packages/admin/admin-babel-preset/CHANGELOG.md @@ -1,5 +1,11 @@ # @comet/admin-babel-preset +## 5.6.0 + +## 5.5.0 + +## 5.4.0 + ## 5.3.0 ## 5.2.0 diff --git a/packages/admin/admin-babel-preset/package.json b/packages/admin/admin-babel-preset/package.json index d982671382..bae7fca9b9 100644 --- a/packages/admin/admin-babel-preset/package.json +++ b/packages/admin/admin-babel-preset/package.json @@ -1,6 +1,6 @@ { "name": "@comet/admin-babel-preset", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", diff --git a/packages/admin/admin-color-picker/CHANGELOG.md b/packages/admin/admin-color-picker/CHANGELOG.md index bcc44847a2..f5ec1ef5b0 100644 --- a/packages/admin/admin-color-picker/CHANGELOG.md +++ b/packages/admin/admin-color-picker/CHANGELOG.md @@ -1,5 +1,28 @@ # @comet/admin-color-picker +## 5.6.0 + +### Patch Changes + +- @comet/admin@5.6.0 +- @comet/admin-icons@5.6.0 + +## 5.5.0 + +### Patch Changes + +- @comet/admin@5.5.0 +- @comet/admin-icons@5.5.0 + +## 5.4.0 + +### Patch Changes + +- Updated dependencies [ba800163] +- Updated dependencies [60a18392] + - @comet/admin@5.4.0 + - @comet/admin-icons@5.4.0 + ## 5.3.0 ### Patch Changes diff --git a/packages/admin/admin-color-picker/package.json b/packages/admin/admin-color-picker/package.json index 4e0481cd24..04e2769bbb 100644 --- a/packages/admin/admin-color-picker/package.json +++ b/packages/admin/admin-color-picker/package.json @@ -1,6 +1,6 @@ { "name": "@comet/admin-color-picker", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -25,8 +25,8 @@ "start:types": "tsc --project ./tsconfig.json --emitDeclarationOnly --watch --preserveWatchOutput" }, "dependencies": { - "@comet/admin": "workspace:^5.3.0", - "@comet/admin-icons": "workspace:^5.3.0", + "@comet/admin": "workspace:^5.6.0", + "@comet/admin-icons": "workspace:^5.6.0", "clsx": "^1.1.1", "react-colorful": "^5.5.1", "tinycolor2": "^1.4.1", @@ -35,8 +35,8 @@ "devDependencies": { "@babel/cli": "^7.17.6", "@babel/core": "^7.20.12", - "@comet/admin-babel-preset": "workspace:^5.3.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/admin-babel-preset": "workspace:^5.6.0", + "@comet/eslint-config": "workspace:^5.6.0", "@mui/icons-material": "^5.0.0", "@mui/material": "^5.0.0", "@mui/styles": "^5.0.0", diff --git a/packages/admin/admin-date-time/CHANGELOG.md b/packages/admin/admin-date-time/CHANGELOG.md index 761488755c..412a7c08cc 100644 --- a/packages/admin/admin-date-time/CHANGELOG.md +++ b/packages/admin/admin-date-time/CHANGELOG.md @@ -1,5 +1,28 @@ # @comet/admin-date-time +## 5.6.0 + +### Patch Changes + +- @comet/admin@5.6.0 +- @comet/admin-icons@5.6.0 + +## 5.5.0 + +### Patch Changes + +- @comet/admin@5.5.0 +- @comet/admin-icons@5.5.0 + +## 5.4.0 + +### Patch Changes + +- Updated dependencies [ba800163] +- Updated dependencies [60a18392] + - @comet/admin@5.4.0 + - @comet/admin-icons@5.4.0 + ## 5.3.0 ### Patch Changes diff --git a/packages/admin/admin-date-time/package.json b/packages/admin/admin-date-time/package.json index 8ae9958d7e..fdce8ee9b0 100644 --- a/packages/admin/admin-date-time/package.json +++ b/packages/admin/admin-date-time/package.json @@ -1,6 +1,6 @@ { "name": "@comet/admin-date-time", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -25,8 +25,8 @@ "start:types": "tsc --project ./tsconfig.json --emitDeclarationOnly --watch --preserveWatchOutput" }, "dependencies": { - "@comet/admin": "workspace:^5.3.0", - "@comet/admin-icons": "workspace:^5.3.0", + "@comet/admin": "workspace:^5.6.0", + "@comet/admin-icons": "workspace:^5.6.0", "@mui/utils": "^5.4.1", "clsx": "^1.1.1", "date-fns": "^2.28.0", @@ -35,8 +35,8 @@ "devDependencies": { "@babel/cli": "^7.17.6", "@babel/core": "^7.20.12", - "@comet/admin-babel-preset": "workspace:^5.3.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/admin-babel-preset": "workspace:^5.6.0", + "@comet/eslint-config": "workspace:^5.6.0", "@mui/material": "^5.0.0", "@mui/styles": "^5.0.0", "@types/react": "^17.0", diff --git a/packages/admin/admin-icons/CHANGELOG.md b/packages/admin/admin-icons/CHANGELOG.md index 137648c956..5f759410bf 100644 --- a/packages/admin/admin-icons/CHANGELOG.md +++ b/packages/admin/admin-icons/CHANGELOG.md @@ -1,5 +1,11 @@ # @comet/admin-icons +## 5.6.0 + +## 5.5.0 + +## 5.4.0 + ## 5.3.0 ### Minor Changes diff --git a/packages/admin/admin-icons/package.json b/packages/admin/admin-icons/package.json index 2917bc74b8..25d1370ffb 100644 --- a/packages/admin/admin-icons/package.json +++ b/packages/admin/admin-icons/package.json @@ -1,6 +1,6 @@ { "name": "@comet/admin-icons", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -24,8 +24,8 @@ "devDependencies": { "@babel/cli": "^7.17.6", "@babel/core": "^7.20.12", - "@comet/admin-babel-preset": "workspace:^5.3.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/admin-babel-preset": "workspace:^5.6.0", + "@comet/eslint-config": "workspace:^5.6.0", "@mui/material": "^5.0.0", "@types/cli-progress": "^3.8.0", "@types/node": "^18.0.0", diff --git a/packages/admin/admin-react-select/CHANGELOG.md b/packages/admin/admin-react-select/CHANGELOG.md index 8b09c1f736..dcf14c92d2 100644 --- a/packages/admin/admin-react-select/CHANGELOG.md +++ b/packages/admin/admin-react-select/CHANGELOG.md @@ -1,5 +1,25 @@ # @comet/admin-react-select +## 5.6.0 + +### Patch Changes + +- @comet/admin@5.6.0 + +## 5.5.0 + +### Patch Changes + +- @comet/admin@5.5.0 + +## 5.4.0 + +### Patch Changes + +- Updated dependencies [ba800163] +- Updated dependencies [60a18392] + - @comet/admin@5.4.0 + ## 5.3.0 ### Patch Changes diff --git a/packages/admin/admin-react-select/package.json b/packages/admin/admin-react-select/package.json index 5fc14e30bd..da2d2c4bf2 100644 --- a/packages/admin/admin-react-select/package.json +++ b/packages/admin/admin-react-select/package.json @@ -1,6 +1,6 @@ { "name": "@comet/admin-react-select", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -25,14 +25,14 @@ "start:types": "tsc --project ./tsconfig.json --emitDeclarationOnly --watch --preserveWatchOutput" }, "dependencies": { - "@comet/admin": "workspace:^5.3.0", + "@comet/admin": "workspace:^5.6.0", "classnames": "^2.2.6" }, "devDependencies": { "@babel/cli": "^7.17.6", "@babel/core": "^7.20.12", - "@comet/admin-babel-preset": "workspace:^5.3.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/admin-babel-preset": "workspace:^5.6.0", + "@comet/eslint-config": "workspace:^5.6.0", "@mui/icons-material": "^5.0.0", "@mui/material": "^5.0.0", "@mui/styles": "^5.0.0", diff --git a/packages/admin/admin-rte/CHANGELOG.md b/packages/admin/admin-rte/CHANGELOG.md index 39e90b7059..13af554b82 100644 --- a/packages/admin/admin-rte/CHANGELOG.md +++ b/packages/admin/admin-rte/CHANGELOG.md @@ -1,5 +1,53 @@ # @comet/admin-rte +## 5.6.0 + +### Patch Changes + +- @comet/admin-icons@5.6.0 + +## 5.5.0 + +### Patch Changes + +- @comet/admin-icons@5.5.0 + +## 5.4.0 + +### Minor Changes + +- 981bf48c: Allow setting a tooltip to the button of custom-inline-styles using the `tooltipText` prop +- 51d6c2b9: Move soft-hyphen functionality to `@comet/admin-rte` + + This allows using the soft-hyphen functionality in plain RTEs, and not only in `RichTextBlock` + + ```tsx + const [useRteApi] = makeRteApi(); + + export default function MyRte() { + const { editorState, setEditorState } = useRteApi(); + return ( + + ); + } + ``` + +### Patch Changes + +- @comet/admin-icons@5.4.0 + ## 5.3.0 ### Patch Changes diff --git a/packages/admin/admin-rte/package.json b/packages/admin/admin-rte/package.json index df1c0570f2..22362f8910 100644 --- a/packages/admin/admin-rte/package.json +++ b/packages/admin/admin-rte/package.json @@ -1,6 +1,6 @@ { "name": "@comet/admin-rte", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -25,7 +25,7 @@ "start:types": "tsc --project ./tsconfig.json --emitDeclarationOnly --watch --preserveWatchOutput" }, "dependencies": { - "@comet/admin-icons": "workspace:^5.3.0", + "@comet/admin-icons": "workspace:^5.6.0", "detect-browser": "^5.2.1", "draftjs-conductor": "^3.0.0", "immutable": "~3.7.4" @@ -33,8 +33,8 @@ "devDependencies": { "@babel/cli": "^7.17.6", "@babel/core": "^7.20.12", - "@comet/admin-babel-preset": "workspace:^5.3.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/admin-babel-preset": "workspace:^5.6.0", + "@comet/eslint-config": "workspace:^5.6.0", "@mui/icons-material": "^5.0.0", "@mui/material": "^5.0.0", "@mui/styles": "^5.0.0", diff --git a/packages/admin/admin-rte/src/core/Controls/SpecialCharactersControls.tsx b/packages/admin/admin-rte/src/core/Controls/SpecialCharactersControls.tsx index 364d5d97cb..cb8c2669a2 100644 --- a/packages/admin/admin-rte/src/core/Controls/SpecialCharactersControls.tsx +++ b/packages/admin/admin-rte/src/core/Controls/SpecialCharactersControls.tsx @@ -2,6 +2,7 @@ import { ButtonGroup } from "@mui/material"; import * as React from "react"; import NonBreakingSpaceToolbarButton from "../extension/NonBreakingSpace/ToolbarButton"; +import { ToolbarButton as SoftHyphenToolbarButton } from "../extension/SoftHyphen/ToolbarButton"; import { IControlProps } from "../types"; function SpecialCharactersControls(props: IControlProps) { @@ -9,11 +10,16 @@ function SpecialCharactersControls(props: IControlProps) { options: { supports: supportedThings }, } = props; - if (!supportedThings.includes("non-breaking-space")) { + if (!supportedThings.includes("non-breaking-space") && !supportedThings.includes("soft-hyphen")) { return null; } - return {supportedThings.includes("non-breaking-space") && }; + return ( + + {supportedThings.includes("non-breaking-space") && } + {supportedThings.includes("soft-hyphen") && } + + ); } export default SpecialCharactersControls; diff --git a/packages/admin/admin-rte/src/core/Controls/useInlineStyleType.tsx b/packages/admin/admin-rte/src/core/Controls/useInlineStyleType.tsx index e3149f258f..97a797a8d8 100644 --- a/packages/admin/admin-rte/src/core/Controls/useInlineStyleType.tsx +++ b/packages/admin/admin-rte/src/core/Controls/useInlineStyleType.tsx @@ -118,10 +118,9 @@ export default function useInlineStyleType({ editorState, setEditorState, suppor onButtonClick: handleInlineStyleButtonClick.bind(null, c.name), })), ...(customInlineStyles - ? Object.entries(customInlineStyles).map(([name, { label, icon }]) => ({ + ? Object.entries(customInlineStyles).map(([name, { style, ...restOptions }]) => ({ + ...restOptions, name, - label, - icon, selected: inlineStyleActive(name), onButtonClick: handleInlineStyleButtonClick.bind(null, name), })) diff --git a/packages/admin/admin-rte/src/core/Rte.tsx b/packages/admin/admin-rte/src/core/Rte.tsx index 02caa1a8ea..bd4cf4e9be 100644 --- a/packages/admin/admin-rte/src/core/Rte.tsx +++ b/packages/admin/admin-rte/src/core/Rte.tsx @@ -48,7 +48,8 @@ export type SupportedThings = | "history" | "link" | "links-remove" - | "non-breaking-space"; + | "non-breaking-space" + | "soft-hyphen"; export interface IRteOptions { supports: SupportedThings[]; diff --git a/packages/admin/cms-admin/src/blocks/rte/extension/SoftHyphen/Decorator.ts b/packages/admin/admin-rte/src/core/extension/SoftHyphen/Decorator.ts similarity index 90% rename from packages/admin/cms-admin/src/blocks/rte/extension/SoftHyphen/Decorator.ts rename to packages/admin/admin-rte/src/core/extension/SoftHyphen/Decorator.ts index 53a306e8d2..cc1cac61a0 100644 --- a/packages/admin/cms-admin/src/blocks/rte/extension/SoftHyphen/Decorator.ts +++ b/packages/admin/admin-rte/src/core/extension/SoftHyphen/Decorator.ts @@ -4,7 +4,7 @@ import { EditorComponent } from "./EditorComponent"; const SHY_UNICHAR_REGEX = /\u00ad/g; -export const Decorator: DraftDecorator = { +const Decorator: DraftDecorator = { strategy: (contentBlock, callback) => { findWithRegex(SHY_UNICHAR_REGEX, contentBlock, callback); }, @@ -19,3 +19,5 @@ function findWithRegex(regex: RegExp, contentBlock: Draft.ContentBlock, callback callback(start, start + matchArr[0].length); } } + +export default Decorator; diff --git a/packages/admin/admin-rte/src/core/extension/SoftHyphen/EditorComponent.tsx b/packages/admin/admin-rte/src/core/extension/SoftHyphen/EditorComponent.tsx new file mode 100644 index 0000000000..133e1f0c7e --- /dev/null +++ b/packages/admin/admin-rte/src/core/extension/SoftHyphen/EditorComponent.tsx @@ -0,0 +1,29 @@ +import { RteSoftHyphen } from "@comet/admin-icons"; +import { styled } from "@mui/material/styles"; +import { ContentState } from "draft-js"; +import * as React from "react"; + +interface Props { + contentState: ContentState; + entityKey: string; + children?: React.ReactNode; +} + +//TODO: Allow text selection for SoftHyphen +export function EditorComponent({ children }: Props): React.ReactElement { + return ( + + + {children} + + ); +} + +const VisibleHyphen = styled(RteSoftHyphen)` + font-size: inherit; + color: currentcolor; + opacity: 0.5; + + // Arbitrary value to make the icon look centered + padding-top: 0.2em; +`; diff --git a/packages/admin/cms-admin/src/blocks/rte/extension/SoftHyphen/ToolbarButton.tsx b/packages/admin/admin-rte/src/core/extension/SoftHyphen/ToolbarButton.tsx similarity index 91% rename from packages/admin/cms-admin/src/blocks/rte/extension/SoftHyphen/ToolbarButton.tsx rename to packages/admin/admin-rte/src/core/extension/SoftHyphen/ToolbarButton.tsx index 41a2f26963..c9e6895693 100644 --- a/packages/admin/cms-admin/src/blocks/rte/extension/SoftHyphen/ToolbarButton.tsx +++ b/packages/admin/admin-rte/src/core/extension/SoftHyphen/ToolbarButton.tsx @@ -1,11 +1,12 @@ import { RteSoftHyphen } from "@comet/admin-icons"; -import { ControlButton } from "@comet/admin-rte"; -import { IControlProps } from "@comet/admin-rte/lib/core/types"; //@TODO export from RTE import Tooltip from "@mui/material/Tooltip"; import { EditorState, Modifier } from "draft-js"; import * as React from "react"; import { FormattedMessage } from "react-intl"; +import ControlButton from "../../Controls/ControlButton"; +import { IControlProps } from "../../types"; + const SHY_UNICODE_CHAR = 0x00ad; export function ToolbarButton({ editorState, setEditorState }: IControlProps): React.ReactElement { diff --git a/packages/admin/admin-rte/src/core/makeRteApi.ts b/packages/admin/admin-rte/src/core/makeRteApi.ts index d623e136b0..e0296ab3cc 100644 --- a/packages/admin/admin-rte/src/core/makeRteApi.ts +++ b/packages/admin/admin-rte/src/core/makeRteApi.ts @@ -5,6 +5,7 @@ import useDebounce from "../useDebounce"; import usePrevious from "../usePrevious"; import LinkDecorator from "./extension/Link/Decorator"; import NonBreakingSpaceDecorator from "./extension/NonBreakingSpace/Decorator"; +import SoftHyphenDecorator from "./extension/SoftHyphen/Decorator"; export interface IMakeRteApiProps { decorators?: DraftDecorator[]; @@ -39,7 +40,7 @@ function makeRteApi(o?: IMakeRteApiProps) { const { decorators = [LinkDecorator], parse = defaultParseContent, format = defaultFormatContent }: IMakeRteApiProps = o || {}; // Add default decorators - decorators.push(NonBreakingSpaceDecorator); + decorators.push(NonBreakingSpaceDecorator, SoftHyphenDecorator); const decorator = new CompositeDecorator(decorators); diff --git a/packages/admin/admin-rte/src/index.ts b/packages/admin/admin-rte/src/index.ts index a8d5c19ed8..5da93d73c8 100644 --- a/packages/admin/admin-rte/src/index.ts +++ b/packages/admin/admin-rte/src/index.ts @@ -6,6 +6,7 @@ export { default as LinkControls, RteLinkControlsClassKey } from "./core/Control export { RteToolbarClassKey, default as Toolbar } from "./core/Controls/Toolbar"; export { default as LinkDecorator } from "./core/extension/Link/Decorator"; export { default as NonBreakingSpaceDecorator } from "./core/extension/NonBreakingSpace/Decorator"; +export { default as SoftHyphenDecorator } from "./core/extension/SoftHyphen/Decorator"; export { default as filterEditorStateDefault } from "./core/filterEditor/default"; export { default as filterEditorStateRemoveUnsupportedBlockTypes } from "./core/filterEditor/removeUnsupportedBlockTypes"; export { default as filterEditorStateRemoveUnsupportedEntities } from "./core/filterEditor/removeUnsupportedEntities"; diff --git a/packages/admin/admin-stories/src/admin-rte/RteAllOptions.tsx b/packages/admin/admin-stories/src/admin-rte/RteAllOptions.tsx index cfea6ba53a..99704b9b3f 100644 --- a/packages/admin/admin-stories/src/admin-rte/RteAllOptions.tsx +++ b/packages/admin/admin-stories/src/admin-rte/RteAllOptions.tsx @@ -54,6 +54,7 @@ export const rteOptions: IRteOptions = { "link", "links-remove", "non-breaking-space", + "soft-hyphen", ], listLevelMax: 2, blocktypeMap: { diff --git a/packages/admin/admin-stories/src/admin/error-handling/ErrorBoundary.tsx b/packages/admin/admin-stories/src/admin/error-handling/ErrorBoundary.tsx index 5bfadf6852..3e3171de28 100644 --- a/packages/admin/admin-stories/src/admin/error-handling/ErrorBoundary.tsx +++ b/packages/admin/admin-stories/src/admin/error-handling/ErrorBoundary.tsx @@ -1,5 +1,5 @@ -import { ErrorBoundary } from "@comet/admin"; -import { Alert, Box, Card, CardContent, Link, Typography } from "@mui/material"; +import { Alert, ErrorBoundary } from "@comet/admin"; +import { Box, Card, CardContent, Link, Typography } from "@mui/material"; import { boolean } from "@storybook/addon-knobs"; import { storiesOf } from "@storybook/react"; import * as React from "react"; diff --git a/packages/admin/admin-stories/src/admin/error-handling/RouteWithErrorBoundary.tsx b/packages/admin/admin-stories/src/admin/error-handling/RouteWithErrorBoundary.tsx index a9eea14565..ebc13a642d 100644 --- a/packages/admin/admin-stories/src/admin/error-handling/RouteWithErrorBoundary.tsx +++ b/packages/admin/admin-stories/src/admin/error-handling/RouteWithErrorBoundary.tsx @@ -1,5 +1,5 @@ -import { MasterLayout, Menu, MenuItemRouterLink, RouteWithErrorBoundary } from "@comet/admin"; -import { Alert, Card, CardContent, Typography } from "@mui/material"; +import { Alert, MasterLayout, Menu, MenuItemRouterLink, RouteWithErrorBoundary } from "@comet/admin"; +import { Card, CardContent, Typography } from "@mui/material"; import { storiesOf } from "@storybook/react"; import * as React from "react"; import { Redirect, Route, Switch } from "react-router"; diff --git a/packages/admin/admin-stories/src/admin/form/ScrollToErrorField.tsx b/packages/admin/admin-stories/src/admin/form/ScrollToErrorField.tsx index 9005cb3da9..e097d77ea6 100644 --- a/packages/admin/admin-stories/src/admin/form/ScrollToErrorField.tsx +++ b/packages/admin/admin-stories/src/admin/form/ScrollToErrorField.tsx @@ -43,7 +43,7 @@ function Story() { }} initialValues={initialValues} validate={validate} - formContext={{ shouldScrollToField: ({ fieldMeta: { touched } }) => !touched, shouldShowFieldError: () => true }} + formContext={{ shouldScrollToField: ({ touched }) => !touched, shouldShowFieldError: () => true }} >
diff --git a/packages/admin/admin-stories/src/admin/form/ShowErrorStrategies.tsx b/packages/admin/admin-stories/src/admin/form/ShowErrorStrategies.tsx index 6e8b580631..94db018010 100644 --- a/packages/admin/admin-stories/src/admin/form/ShowErrorStrategies.tsx +++ b/packages/admin/admin-stories/src/admin/form/ShowErrorStrategies.tsx @@ -25,9 +25,9 @@ function validate({ foo, bar }: FormValues) { type ShowStrategy = "always" | "while-typing" | "on-blur" | "when-submitted"; const strategies: Record = { always: () => true, - "on-blur": ({ fieldMeta: { touched } }) => !!touched, - "while-typing": ({ fieldMeta: { touched, active } }) => !!(touched || active), - "when-submitted": ({ fieldMeta: { submitFailed } }) => !!submitFailed, + "on-blur": ({ touched }) => !!touched, + "while-typing": ({ touched, active }) => !!(touched || active), + "when-submitted": ({ submitFailed }) => !!submitFailed, }; function Story() { diff --git a/packages/admin/admin-stories/src/admin/tabs/DynamicTabs.tsx b/packages/admin/admin-stories/src/admin/tabs/DynamicTabs.tsx new file mode 100644 index 0000000000..3e8c70f87a --- /dev/null +++ b/packages/admin/admin-stories/src/admin/tabs/DynamicTabs.tsx @@ -0,0 +1,68 @@ +import { RouterTab, RouterTabs, Tab, Tabs } from "@comet/admin"; +import { Button, Typography } from "@mui/material"; +import { storiesOf } from "@storybook/react"; +import * as React from "react"; + +import { storyRouterDecorator } from "../../story-router.decorator"; + +export const DynamicRouterTabs = ({ showFourthTab }: { showFourthTab: boolean }) => { + const content = ["Two", "Three"]; + + return ( + + + One + + {content.map((value) => ( + + {value} + + ))} + {showFourthTab && ( + + Four + + )} + + ); +}; + +export const DynamicTabs = ({ showFourthTab }: { showFourthTab: boolean }) => { + const content = ["Two", "Three"]; + + return ( + + One + {content.map((value) => ( + + {value} + + ))} + {showFourthTab && Four} + + ); +}; + +function Story() { + const [showFourthTab, setShowFourthTab] = React.useState(false); + + return ( + <> + + + + Tabs: + + + + + RouterTabs: + + + + ); +} + +storiesOf("@comet/admin/tabs", module) + .addDecorator(storyRouterDecorator()) + .add("Dynamic Tabs and RouterTabs", () => ); diff --git a/packages/admin/admin-stories/src/docs/form/components/stories/FieldContainer.stories.tsx b/packages/admin/admin-stories/src/docs/form/components/stories/FieldContainer.stories.tsx index ac19a3b682..18edc993bd 100644 --- a/packages/admin/admin-stories/src/docs/form/components/stories/FieldContainer.stories.tsx +++ b/packages/admin/admin-stories/src/docs/form/components/stories/FieldContainer.stories.tsx @@ -28,6 +28,9 @@ storiesOf("stories/Form/Components", module).add("FieldContainer", () => { + + + ); }); diff --git a/packages/admin/admin-theme/CHANGELOG.md b/packages/admin/admin-theme/CHANGELOG.md index 49da3c224b..a5b0ccf52e 100644 --- a/packages/admin/admin-theme/CHANGELOG.md +++ b/packages/admin/admin-theme/CHANGELOG.md @@ -1,5 +1,27 @@ # @comet/admin-theme +## 5.6.0 + +### Minor Changes + +- fb6c8063: Change `DataGrid`'s `noRowsLabel` from "No rows" to "No results found." + +### Patch Changes + +- @comet/admin-icons@5.6.0 + +## 5.5.0 + +### Patch Changes + +- @comet/admin-icons@5.5.0 + +## 5.4.0 + +### Patch Changes + +- @comet/admin-icons@5.4.0 + ## 5.3.0 ### Patch Changes diff --git a/packages/admin/admin-theme/package.json b/packages/admin/admin-theme/package.json index d5f3cd1700..e74ad1d43d 100644 --- a/packages/admin/admin-theme/package.json +++ b/packages/admin/admin-theme/package.json @@ -1,6 +1,6 @@ { "name": "@comet/admin-theme", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -25,14 +25,14 @@ "start:types": "tsc --project ./tsconfig.json --emitDeclarationOnly --watch --preserveWatchOutput" }, "dependencies": { - "@comet/admin-icons": "workspace:^5.3.0", + "@comet/admin-icons": "workspace:^5.6.0", "@mui/utils": "^5.4.1" }, "devDependencies": { "@babel/cli": "^7.17.6", "@babel/core": "^7.20.12", - "@comet/admin-babel-preset": "workspace:^5.3.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/admin-babel-preset": "workspace:^5.6.0", + "@comet/eslint-config": "workspace:^5.6.0", "@mui/material": "^5.0.0", "@mui/styles": "^5.0.0", "@mui/system": "^5.0.0", diff --git a/packages/admin/admin-theme/src/componentsTheme/MuiDataGrid.tsx b/packages/admin/admin-theme/src/componentsTheme/MuiDataGrid.tsx index 8dd59ce410..d766c009c3 100644 --- a/packages/admin/admin-theme/src/componentsTheme/MuiDataGrid.tsx +++ b/packages/admin/admin-theme/src/componentsTheme/MuiDataGrid.tsx @@ -12,7 +12,7 @@ import { TextField, TextFieldProps, } from "@mui/material"; -import { getDataGridUtilityClass } from "@mui/x-data-grid"; +import { getDataGridUtilityClass, GRID_DEFAULT_LOCALE_TEXT } from "@mui/x-data-grid"; import type {} from "@mui/x-data-grid/themeAugmentation"; import React from "react"; @@ -34,6 +34,9 @@ export const getMuiDataGrid: GetMuiComponentTheme<"MuiDataGrid"> = (component, { ColumnMenuIcon: (props: SvgIconProps) => , ...component?.defaultProps?.components, }, + localeText: { + noRowsLabel: GRID_DEFAULT_LOCALE_TEXT.noResultsOverlayLabel, + }, ...component?.defaultProps, }, styleOverrides: mergeOverrideStyles<"MuiDataGrid">(component?.styleOverrides, { diff --git a/packages/admin/admin/CHANGELOG.md b/packages/admin/admin/CHANGELOG.md index 01b7286b9b..e56821277e 100644 --- a/packages/admin/admin/CHANGELOG.md +++ b/packages/admin/admin/CHANGELOG.md @@ -1,5 +1,67 @@ # @comet/admin +## 5.6.0 + +### Patch Changes + +- @comet/admin-icons@5.6.0 + +## 5.5.0 + +### Patch Changes + +- @comet/admin-icons@5.5.0 + +## 5.4.0 + +### Minor Changes + +- 60a18392: Add `Alert` component + + **Example:** + + ```tsx + import { Alert, OkayButton, SaveButton } from "@comet/admin"; + + }> + Action Text + + } + > + Notification Text + ; + ``` + +### Patch Changes + +- ba800163: Allow passing a mix of elements and arrays to `Tabs` and `RouterTabs` as children + + For example: + + ```tsx + + + One + + {content.map((value) => ( + + {value} + + ))} + {showFourthTab && ( + + Four + + )} + + ``` + + - @comet/admin-icons@5.4.0 + ## 5.3.0 ### Minor Changes diff --git a/packages/admin/admin/package.json b/packages/admin/admin/package.json index e9d4fa9a86..82b5e4cbe1 100644 --- a/packages/admin/admin/package.json +++ b/packages/admin/admin/package.json @@ -1,6 +1,6 @@ { "name": "@comet/admin", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -27,7 +27,7 @@ "test:watch": "jest --watch" }, "dependencies": { - "@comet/admin-icons": "workspace:^5.3.0", + "@comet/admin-icons": "workspace:^5.6.0", "@mui/private-theming": "^5.0.0", "clsx": "^1.1.1", "exceljs": "^3.4.0", @@ -45,8 +45,8 @@ "@apollo/client": "^3.7.0", "@babel/cli": "^7.17.6", "@babel/core": "^7.20.12", - "@comet/admin-babel-preset": "workspace:^5.3.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/admin-babel-preset": "workspace:^5.6.0", + "@comet/eslint-config": "workspace:^5.6.0", "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@mui/icons-material": "^5.0.0", diff --git a/packages/admin/admin/src/alert/Alert.tsx b/packages/admin/admin/src/alert/Alert.tsx index ec9fff05de..aa0b0ee227 100644 --- a/packages/admin/admin/src/alert/Alert.tsx +++ b/packages/admin/admin/src/alert/Alert.tsx @@ -1,4 +1,5 @@ import { Close } from "@comet/admin-icons"; +// eslint-disable-next-line no-restricted-imports import { Alert as MuiAlert, AlertTitle, buttonClasses, IconButton, Theme, Typography } from "@mui/material"; import { createStyles, WithStyles, withStyles } from "@mui/styles"; import clsx from "clsx"; diff --git a/packages/admin/admin/src/error/errorboundary/ErrorBoundary.tsx b/packages/admin/admin/src/error/errorboundary/ErrorBoundary.tsx index 28b1082781..6b6515449d 100644 --- a/packages/admin/admin/src/error/errorboundary/ErrorBoundary.tsx +++ b/packages/admin/admin/src/error/errorboundary/ErrorBoundary.tsx @@ -1,4 +1,5 @@ import { ChevronDown, ChevronRight, Error } from "@comet/admin-icons"; +// eslint-disable-next-line no-restricted-imports import { Alert as MuiAlert, AlertProps, ComponentsOverrides, Typography } from "@mui/material"; import { css, styled, Theme, useThemeProps } from "@mui/material/styles"; import * as React from "react"; diff --git a/packages/admin/admin/src/form/Field.tsx b/packages/admin/admin/src/form/Field.tsx index 5f61014f4f..b3cbf3678b 100644 --- a/packages/admin/admin/src/form/Field.tsx +++ b/packages/admin/admin/src/form/Field.tsx @@ -16,6 +16,7 @@ const composeValidators = export interface FieldProps { name: string; label?: React.ReactNode; + helperText?: React.ReactNode; component?: React.ComponentType | string; children?: (props: FieldRenderProps) => React.ReactNode; required?: boolean; @@ -34,6 +35,7 @@ export function Field) => finalFormContext.shouldShowFieldError({ fieldMeta })); - const shouldShowWarning = - passedShouldShowWarning ?? ((fieldMeta: FieldMetaState) => finalFormContext.shouldShowFieldWarning({ fieldMeta })); - const shouldScrollToField = - passedShouldScrollTo ?? ((fieldMeta: FieldMetaState) => finalFormContext.shouldScrollToField({ fieldMeta })); + const shouldShowError = passedShouldShowError ?? finalFormContext.shouldShowFieldError; + const shouldShowWarning = passedShouldShowWarning ?? finalFormContext.shouldShowFieldWarning; + const shouldScrollToField = passedShouldScrollTo ?? finalFormContext.shouldScrollToField; function renderField({ input, meta, fieldContainerProps, ...rest }: FieldRenderProps & { warning?: string }) { function render() { @@ -76,6 +75,7 @@ export function Field & { variant?: "vertical" | "horizontal"; fullWidth?: boolean; @@ -21,6 +22,7 @@ export type FieldContainerProps = ThemedComponentBaseProps<{ warning?: string; scrollTo?: boolean; fieldMargin?: "always" | "never" | "onlyIfNotLast"; + helperText?: React.ReactNode; }; export type FieldContainerClassKey = @@ -38,7 +40,8 @@ export type FieldContainerClassKey = | "hasError" | "error" | "hasWarning" - | "warning"; + | "warning" + | "helperText"; type OwnerState = Pick & { hasError: boolean; @@ -186,6 +189,18 @@ const Warning = styled(FormHelperText, { `, ); +const HelperText = styled(FormHelperText, { + name: "CometAdminFormFieldContainer", + slot: "helperText", + overridesResolver(_, styles) { + return [styles.helperText]; + }, +})( + ({ theme }) => css` + color: ${theme.palette.grey[300]}; + `, +); + export const FieldContainer = (inProps: React.PropsWithChildren) => { const { variant = "vertical", @@ -197,6 +212,7 @@ export const FieldContainer = (inProps: React.PropsWithChildren )} {hasWarning && !hasError && {warning}} + {helperText && !hasError && !hasWarning && {helperText}} diff --git a/packages/admin/admin/src/form/FinalFormContextProvider.tsx b/packages/admin/admin/src/form/FinalFormContextProvider.tsx index 0d85838d7e..dcb679986a 100644 --- a/packages/admin/admin/src/form/FinalFormContextProvider.tsx +++ b/packages/admin/admin/src/form/FinalFormContextProvider.tsx @@ -2,15 +2,15 @@ import * as React from "react"; import { FieldMetaState } from "react-final-form"; export interface FinalFormContext { - shouldScrollToField: ({ fieldMeta }: { fieldMeta: FieldMetaState }) => boolean; - shouldShowFieldError: ({ fieldMeta }: { fieldMeta: FieldMetaState }) => boolean; - shouldShowFieldWarning: ({ fieldMeta }: { fieldMeta: FieldMetaState }) => boolean; + shouldScrollToField: (fieldMeta: FieldMetaState) => boolean; + shouldShowFieldError: (fieldMeta: FieldMetaState) => boolean; + shouldShowFieldWarning: (fieldMeta: FieldMetaState) => boolean; } const defaultFinalFormContext: FinalFormContext = { shouldScrollToField: () => false, - shouldShowFieldError: ({ fieldMeta }) => !!fieldMeta?.touched, - shouldShowFieldWarning: ({ fieldMeta }) => !!fieldMeta?.touched, + shouldShowFieldError: (fieldMeta) => !!fieldMeta?.touched, + shouldShowFieldWarning: (fieldMeta) => !!fieldMeta?.touched, }; const FinalFormContext = React.createContext(defaultFinalFormContext); diff --git a/packages/admin/admin/src/index.ts b/packages/admin/admin/src/index.ts index 798d1de430..24e8785b8a 100644 --- a/packages/admin/admin/src/index.ts +++ b/packages/admin/admin/src/index.ts @@ -104,7 +104,7 @@ export { MasterLayoutContext } from "./mui/MasterLayoutContext"; export { MenuCollapsibleItem, MenuCollapsibleItemClassKey, MenuCollapsibleItemProps, MenuLevel } from "./mui/menu/CollapsibleItem"; export { IMenuContext, IWithMenu, MenuContext, withMenu } from "./mui/menu/Context"; export { MenuItem, MenuItemClassKey, MenuItemProps } from "./mui/menu/Item"; -export { MenuItemAnchorLink } from "./mui/menu/ItemAnchorLink"; +export { MenuItemAnchorLink, MenuItemAnchorLinkProps } from "./mui/menu/ItemAnchorLink"; export { MenuItemRouterLink, MenuItemRouterLinkProps } from "./mui/menu/ItemRouterLink"; export { Menu, MenuProps } from "./mui/menu/Menu"; export { MenuClassKey, styles } from "./mui/menu/Menu.styles"; diff --git a/packages/admin/admin/src/mui/menu/ItemAnchorLink.tsx b/packages/admin/admin/src/mui/menu/ItemAnchorLink.tsx index 7ef7a12bdc..5b086194b8 100644 --- a/packages/admin/admin/src/mui/menu/ItemAnchorLink.tsx +++ b/packages/admin/admin/src/mui/menu/ItemAnchorLink.tsx @@ -3,6 +3,6 @@ import * as React from "react"; import { MenuItem, MenuItemProps } from "./Item"; -export const MenuItemAnchorLink: React.FC> = (props) => ( - -); +export type MenuItemAnchorLinkProps = MenuItemProps & ListItemProps & React.HTMLProps; + +export const MenuItemAnchorLink: React.FC = (props) => ; diff --git a/packages/admin/admin/src/tabs/RouterTabs.tsx b/packages/admin/admin/src/tabs/RouterTabs.tsx index 5e53d94d48..66ce707ebf 100644 --- a/packages/admin/admin/src/tabs/RouterTabs.tsx +++ b/packages/admin/admin/src/tabs/RouterTabs.tsx @@ -58,13 +58,16 @@ interface TabProps extends Omit { export const RouterTab: React.FunctionComponent = () => null; +type RouterTabsChild = React.ReactElement | boolean | null | undefined; +type RouterTabsChildren = RouterTabsChild | Array>; + export interface Props extends ThemedComponentBaseProps<{ root: "div"; tabs: typeof Tabs; content: "div"; }> { - children: Array | boolean | null | undefined> | React.ReactElement; + children: RouterTabsChildren; tabComponent?: React.ComponentType; tabsProps?: Partial; } diff --git a/packages/admin/admin/src/tabs/Tabs.tsx b/packages/admin/admin/src/tabs/Tabs.tsx index e3adc9a4ec..3ce331bd8f 100644 --- a/packages/admin/admin/src/tabs/Tabs.tsx +++ b/packages/admin/admin/src/tabs/Tabs.tsx @@ -55,6 +55,9 @@ interface ITabsState { setValue: (value: number) => void; } +type TabsChild = React.ReactElement | boolean | null | undefined; +type TabsChildren = TabsChild | Array>; + export interface TabsProps extends MuiTabsProps, ThemedComponentBaseProps<{ @@ -62,7 +65,7 @@ export interface TabsProps tabs: typeof MuiTabs; content: "div"; }> { - children: Array | boolean | null | undefined> | React.ReactElement; + children: TabsChildren; tabComponent?: React.ComponentType; defaultIndex?: number; tabsState?: ITabsState; diff --git a/packages/admin/blocks-admin/CHANGELOG.md b/packages/admin/blocks-admin/CHANGELOG.md index 5472260198..7c8dacfcc3 100644 --- a/packages/admin/blocks-admin/CHANGELOG.md +++ b/packages/admin/blocks-admin/CHANGELOG.md @@ -1,5 +1,29 @@ # @comet/blocks-admin +## 5.6.0 + +### Patch Changes + +- 76f85abe: Fix linking from block preview to block admin for non-trivial composite/list block combinations + - @comet/admin@5.6.0 + - @comet/admin-icons@5.6.0 + +## 5.5.0 + +### Patch Changes + +- @comet/admin@5.5.0 +- @comet/admin-icons@5.5.0 + +## 5.4.0 + +### Patch Changes + +- Updated dependencies [ba800163] +- Updated dependencies [60a18392] + - @comet/admin@5.4.0 + - @comet/admin-icons@5.4.0 + ## 5.3.0 ### Minor Changes diff --git a/packages/admin/blocks-admin/package.json b/packages/admin/blocks-admin/package.json index 7794dedfea..6a143662a4 100644 --- a/packages/admin/blocks-admin/package.json +++ b/packages/admin/blocks-admin/package.json @@ -1,6 +1,6 @@ { "name": "@comet/blocks-admin", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -29,8 +29,8 @@ "test:watch": "jest --watch" }, "dependencies": { - "@comet/admin": "workspace:^5.3.0", - "@comet/admin-icons": "workspace:^5.3.0", + "@comet/admin": "workspace:^5.6.0", + "@comet/admin-icons": "workspace:^5.6.0", "@mui/lab": "^5.0.0-alpha.76", "clipboard-copy": "^4.0.0", "clsx": "^1.1.1", @@ -42,9 +42,9 @@ "devDependencies": { "@babel/cli": "^7.17.6", "@babel/core": "^7.20.12", - "@comet/admin-babel-preset": "workspace:^5.3.0", - "@comet/cli": "workspace:^5.3.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/admin-babel-preset": "workspace:^5.6.0", + "@comet/cli": "workspace:^5.6.0", + "@comet/eslint-config": "workspace:^5.6.0", "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@mui/lab": "^5.0.0-alpha.76", diff --git a/packages/admin/blocks-admin/src/blocks/factories/createBlocksBlock.tsx b/packages/admin/blocks-admin/src/blocks/factories/createBlocksBlock.tsx index 5915e941ed..62b02b5a06 100644 --- a/packages/admin/blocks-admin/src/blocks/factories/createBlocksBlock.tsx +++ b/packages/admin/blocks-admin/src/blocks/factories/createBlocksBlock.tsx @@ -212,7 +212,7 @@ export function createBlocksBlock (previewCtx.showVisibleOnly ? child.visible : true)) // depending on context show all blocks or only visible blocks .map((child) => { - const blockAdminRoute = `${previewCtx.parentUrl}/${child.key}/blocks`; + const blockAdminRoute = `${previewCtx.parentUrlSubRoute ?? previewCtx.parentUrl}/${child.key}/blocks`; const block = blockForType(child.type); if (!block) { throw new Error(`No Block found for type ${child.type}`); // for TS diff --git a/packages/admin/blocks-admin/src/blocks/factories/createColumnsBlock.tsx b/packages/admin/blocks-admin/src/blocks/factories/createColumnsBlock.tsx index fa374878f1..a229fc137b 100644 --- a/packages/admin/blocks-admin/src/blocks/factories/createColumnsBlock.tsx +++ b/packages/admin/blocks-admin/src/blocks/factories/createColumnsBlock.tsx @@ -160,7 +160,7 @@ export function createColumnsBlock({ columns: state.columns .filter((c) => (previewContext.showVisibleOnly ? c.visible : true)) // depending on context show all blocks or only visible blocks .map((column) => { - const blockAdminRoute = `${previewContext.parentUrl}/${column.key}/edit`; + const blockAdminRoute = `${previewContext.parentUrlSubRoute ?? previewContext.parentUrl}/${column.key}/edit`; return { key: column.key, diff --git a/packages/admin/blocks-admin/src/blocks/factories/createCompositeBlock.tsx b/packages/admin/blocks-admin/src/blocks/factories/createCompositeBlock.tsx index d32197e3be..0d78e821be 100644 --- a/packages/admin/blocks-admin/src/blocks/factories/createCompositeBlock.tsx +++ b/packages/admin/blocks-admin/src/blocks/factories/createCompositeBlock.tsx @@ -132,7 +132,7 @@ export const createCompositeBlock = (previewCtx.showVisibleOnly ? child.visible : true)) // depending on context show all blocks or only visible blocks .map((child) => { - const blockAdminRoute = `${previewCtx.parentUrl}/${child.key}/edit`; + const blockAdminRoute = `${previewCtx.parentUrlSubRoute ?? previewCtx.parentUrl}/${child.key}/edit`; return { key: child.key, diff --git a/packages/admin/blocks-admin/src/blocks/factories/createOneOfBlock.tsx b/packages/admin/blocks-admin/src/blocks/factories/createOneOfBlock.tsx index ca2d22e2ea..1860f8516b 100644 --- a/packages/admin/blocks-admin/src/blocks/factories/createOneOfBlock.tsx +++ b/packages/admin/blocks-admin/src/blocks/factories/createOneOfBlock.tsx @@ -87,7 +87,13 @@ export const createOneOfBlock = ({ } const activeBlockState = s.attachedBlocks.find((c) => c.type === s.activeType); if (!activeBlockState) { - throw new Error(`Reference to active block of type ${s.activeType} not found`); + let message = `Can't find reference to active block of type "${s.activeType}".`; + + if (process.env.NODE_ENV === "development") { + message += ` This is probably due to a missing "x-include-invisible-content" HTTP header. Please make sure to include the header when fetching the block.`; + } + + throw new Error(message); } const block = activeBlockState ? blockForType(activeBlockState.type) : undefined; diff --git a/packages/admin/blocks-admin/src/blocks/helpers/composeBlocks/composeBlocks.tsx b/packages/admin/blocks-admin/src/blocks/helpers/composeBlocks/composeBlocks.tsx index a093a1344f..9a5cde6a3a 100644 --- a/packages/admin/blocks-admin/src/blocks/helpers/composeBlocks/composeBlocks.tsx +++ b/packages/admin/blocks-admin/src/blocks/helpers/composeBlocks/composeBlocks.tsx @@ -127,16 +127,20 @@ export function composeBlocks(compositeBlocks: { flatten: true }, ), - createPreviewState: (s, previewCtx) => - applyToCompositeBlocks( - compositeBlocks, - ([block, options], attr) => { - const extractedState = extractData([block, options], attr, s); - return block.createPreviewState(extractedState, previewCtx); - }, - { flatten: true }, - ), - + createPreviewState: (s, previewCtx) => { + return { + adminRoute: previewCtx.parentUrl, + adminMeta: { route: previewCtx.parentUrl }, + ...applyToCompositeBlocks( + compositeBlocks, + ([block, options], attr) => { + const extractedState = extractData([block, options], attr, s); + return block.createPreviewState(extractedState, previewCtx); + }, + { flatten: true }, + ), + }; + }, isValid: async (state) => { const isValidPromises: Promise[] = Object.values( applyToCompositeBlocks(compositeBlocks, ([block, options], attr) => { diff --git a/packages/admin/blocks-admin/src/blocks/types.tsx b/packages/admin/blocks-admin/src/blocks/types.tsx index 8f5cdb6bb5..f8b3f40374 100644 --- a/packages/admin/blocks-admin/src/blocks/types.tsx +++ b/packages/admin/blocks-admin/src/blocks/types.tsx @@ -7,6 +7,7 @@ export type DispatchSetStateAction = (setStateAction: SetStateAction) => v export interface IPreviewContext { showVisibleOnly?: boolean; parentUrl: string; + parentUrlSubRoute?: string; } interface AdminMetaInterface { @@ -59,7 +60,7 @@ export type PreviewContent = PreviewContentImage | PreviewContentText; export type BlockDependency = { targetGraphqlObjectType: string; id: string; data?: unknown }; -export type ReplaceDependencyObject = { originalId: string; replaceWithId: string; type: string }; +export type ReplaceDependencyObject = { originalId: string; replaceWithId: string | undefined; type: string }; export interface BlockMethods< // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/admin/blocks-admin/src/form/BlocksFinalForm.tsx b/packages/admin/blocks-admin/src/form/BlocksFinalForm.tsx index dfa1d5feff..903baba4d2 100644 --- a/packages/admin/blocks-admin/src/form/BlocksFinalForm.tsx +++ b/packages/admin/blocks-admin/src/form/BlocksFinalForm.tsx @@ -27,9 +27,9 @@ const noop = () => { }; const finalFormContextValues: Omit = { - shouldShowFieldError: ({ fieldMeta }) => fieldMeta.error || fieldMeta.submitError, // Doesnt matter if touched or not - shouldShowFieldWarning: ({ fieldMeta }) => fieldMeta.data?.warning, - shouldScrollToField: ({ fieldMeta }) => fieldMeta.error && !fieldMeta.touched, // If a field is not touched yet and has an error we scroll to it + shouldShowFieldError: (fieldMeta) => fieldMeta.error || fieldMeta.submitError, // Doesnt matter if touched or not + shouldShowFieldWarning: (fieldMeta) => fieldMeta.data?.warning, + shouldScrollToField: (fieldMeta) => fieldMeta.error && !fieldMeta.touched, // If a field is not touched yet and has an error we scroll to it }; export function BlocksFinalForm({ onSubmit, children, ...rest }: FormProps): JSX.Element { diff --git a/packages/admin/cms-admin/CHANGELOG.md b/packages/admin/cms-admin/CHANGELOG.md index ae34152f99..06128b19d8 100644 --- a/packages/admin/cms-admin/CHANGELOG.md +++ b/packages/admin/cms-admin/CHANGELOG.md @@ -1,5 +1,138 @@ # @comet/cms-admin +## 5.6.0 + +### Minor Changes + +- fe26a6e6: Show an error message when trying to edit a non-existing document and when trying to edit an archived document +- 3ee4ce09: Return newly uploaded items from `useDamFileUpload#uploadFiles` + +### Patch Changes + +- Updated dependencies [fb6c8063] +- Updated dependencies [76f85abe] + - @comet/admin-theme@5.6.0 + - @comet/blocks-admin@5.6.0 + - @comet/admin@5.6.0 + - @comet/admin-date-time@5.6.0 + - @comet/admin-icons@5.6.0 + - @comet/admin-rte@5.6.0 + +## 5.5.0 + +### Patch Changes + +- 1b37b1f6: Show `additionalToolbarItems` in `ChooseFileDialog` + + The `additionalToolbarItems` were only shown inside the `DamPage`, but not in the `ChooseFileDialog`. + To fix this, use the `additionalToolbarItems` option in `DamConfigProvider`. + The `additionalToolbarItems` prop of `DamPage` has been deprecated in favor of this option. + + **Previously:** + + ```tsx + } + /> + ``` + + **Now:** + + ```tsx + , + }} + > + {/*...*/} + + ``` + +- 85aa962c: Set unhandled dependencies to `undefined` when copying documents to another scope + + This prevents leaks between scopes. In practice, this mostly concerns links to documents that don't exist in the target scope. + + **Example:** + + - Page A links to Page B + - Page A is copied from Scope A to Scope B + - Link to Page B is removed from Page A by replacing the `id` with `undefined` (since Page B doesn't exist in Scope B) + + **Note:** The link is only retained if both pages are copied in the same operation. + +- c4639be5: Clip crop values when cropping an image in the DAM or `PixelImageBlock` + + Previously, negative values could occur, causing the image proxy to fail on delivery. + + - @comet/admin@5.5.0 + - @comet/admin-date-time@5.5.0 + - @comet/admin-icons@5.5.0 + - @comet/admin-rte@5.5.0 + - @comet/admin-theme@5.5.0 + - @comet/blocks-admin@5.5.0 + +## 5.4.0 + +### Minor Changes + +- e146d8bb: Support the import of files from external DAMs + + To connect an external DAM, implement a component with the necessary logic (asset picker, upload functionality, ...). Pass this component to the `DamPage` via the `additionalToolbarItems` prop. + + ```tsx + } + /> + ``` + + You can find an [example](demo/admin/src/dam/ImportFromUnsplash.tsx) in the demo project. + +- 51d6c2b9: Move soft-hyphen functionality to `@comet/admin-rte` + + This allows using the soft-hyphen functionality in plain RTEs, and not only in `RichTextBlock` + + ```tsx + const [useRteApi] = makeRteApi(); + + export default function MyRte() { + const { editorState, setEditorState } = useRteApi(); + return ( + + ); + } + ``` + +- dcaf750d: Make all DAM license fields optional if `LicenseType` is `ROYALTY_FREE` even if `requireLicense` is true in `DamConfig` + +### Patch Changes + +- 087cb01d: Enable copying documents from one `PageTree` category to another (e.g. from "Main navigation" to "Top menu" in Demo) +- Updated dependencies [ba800163] +- Updated dependencies [981bf48c] +- Updated dependencies [60a18392] +- Updated dependencies [51d6c2b9] + - @comet/admin@5.4.0 + - @comet/admin-rte@5.4.0 + - @comet/admin-date-time@5.4.0 + - @comet/admin-icons@5.4.0 + - @comet/admin-theme@5.4.0 + - @comet/blocks-admin@5.4.0 + ## 5.3.0 ### Patch Changes diff --git a/packages/admin/cms-admin/package.json b/packages/admin/cms-admin/package.json index 825cc0c0cf..9a04bc99c7 100644 --- a/packages/admin/cms-admin/package.json +++ b/packages/admin/cms-admin/package.json @@ -1,6 +1,6 @@ { "name": "@comet/cms-admin", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -19,7 +19,7 @@ "build": "$npm_execpath run clean && run-p generate-graphql-types generate-block-types && run-p build:babel build:types", "build:babel": "npx babel ./src -x \".ts,.tsx\" -d lib", "build:types": "tsc --project ./tsconfig.build.json --emitDeclarationOnly", - "clean": "rimraf lib", + "clean": "rimraf lib 'src/**/*.generated.ts'", "generate-block-types": "comet generate-block-types --inputs", "generate-block-types:watch": "chokidar -s \"**/block-meta.json\" -c \"$npm_execpath generate-block-types\"", "generate-graphql-types": "graphql-codegen", @@ -34,12 +34,12 @@ "test:watch": "jest --watch" }, "dependencies": { - "@comet/admin": "workspace:^5.3.0", - "@comet/admin-date-time": "workspace:^5.3.0", - "@comet/admin-icons": "workspace:^5.3.0", - "@comet/admin-rte": "workspace:^5.3.0", - "@comet/admin-theme": "workspace:^5.3.0", - "@comet/blocks-admin": "workspace:^5.3.0", + "@comet/admin": "workspace:^5.6.0", + "@comet/admin-date-time": "workspace:^5.6.0", + "@comet/admin-icons": "workspace:^5.6.0", + "@comet/admin-rte": "workspace:^5.6.0", + "@comet/admin-theme": "workspace:^5.6.0", + "@comet/blocks-admin": "workspace:^5.6.0", "@graphql-tools/graphql-file-loader": "^7.5.17", "@graphql-tools/load": "^7.8.14", "@graphql-typed-document-node/core": "^3.1.1", @@ -80,9 +80,9 @@ "@apollo/client": "^3.7.0", "@babel/cli": "^7.17.6", "@babel/core": "^7.20.12", - "@comet/admin-babel-preset": "workspace:^5.3.0", - "@comet/cli": "workspace:^5.3.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/admin-babel-preset": "workspace:^5.6.0", + "@comet/cli": "workspace:^5.6.0", + "@comet/eslint-config": "workspace:^5.6.0", "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@graphql-codegen/cli": "^2.0.0", diff --git a/packages/admin/cms-admin/src/blocks/createRichTextBlock.tsx b/packages/admin/cms-admin/src/blocks/createRichTextBlock.tsx index 6082b41af8..c40f35a172 100644 --- a/packages/admin/cms-admin/src/blocks/createRichTextBlock.tsx +++ b/packages/admin/cms-admin/src/blocks/createRichTextBlock.tsx @@ -17,15 +17,13 @@ import { FormattedMessage } from "react-intl"; import { RichTextBlockData, RichTextBlockInput } from "../blocks.generated"; import { createCmsLinkToolbarButton } from "./rte/extension/CmsLink/createCmsLinkToolbarButton"; import { Decorator as CmsLinkDecorator } from "./rte/extension/CmsLink/Decorator"; -import { Decorator as SoftHyphenDecorator } from "./rte/extension/SoftHyphen/Decorator"; -import { ToolbarButton as SoftHyphenToolbarButton } from "./rte/extension/SoftHyphen/ToolbarButton"; export interface RichTextBlockState { editorState: EditorState; } const [, { createEmptyState, createStateFromRawContent, convertStateToRawContent }] = makeRteApi({ - decorators: [CmsLinkDecorator, SoftHyphenDecorator], + decorators: [CmsLinkDecorator], // @TODO: implement a compound decorator in rte // like https://jsfiddle.net/paulyoung85/2unzgt68/ // https://github.com/facebook/draft-js/issues/542#issuecomment-275996606 @@ -120,6 +118,7 @@ export const createRichTextBlock = ( "link", "links-remove", "non-breaking-space", + "soft-hyphen", ], draftJsProps: { spellCheck: true, @@ -127,7 +126,6 @@ export const createRichTextBlock = ( standardBlockType: "unstyled", overwriteLinkButton: CmsLinkToolbarButton, - customToolbarButtons: [SoftHyphenToolbarButton], }; const LinkBlock = options.link; const rteOptions = { ...defaultRteOptions, ...(options.rte ?? {}) }; diff --git a/packages/admin/cms-admin/src/blocks/rte/extension/SoftHyphen/EditorComponent.tsx b/packages/admin/cms-admin/src/blocks/rte/extension/SoftHyphen/EditorComponent.tsx deleted file mode 100644 index 33215d8a04..0000000000 --- a/packages/admin/cms-admin/src/blocks/rte/extension/SoftHyphen/EditorComponent.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { styled } from "@mui/material/styles"; -import { ContentState } from "draft-js"; -import * as React from "react"; - -interface IProps { - contentState: ContentState; - entityKey: string; - children?: React.ReactNode; -} - -const VisibleHyphen = styled("span")` - color: #999; - &:before { - content: "[-]"; - } -`; - -// children must be rendered, although ­ is invisible -// https://github.com/facebook/draft-js/issues/1558 - -export function EditorComponent(props: IProps): React.ReactElement { - return {props.children}; -} diff --git a/packages/admin/cms-admin/src/builds/StartBuildsDialog.tsx b/packages/admin/cms-admin/src/builds/StartBuildsDialog.tsx index 69880e977d..32ec01b37d 100644 --- a/packages/admin/cms-admin/src/builds/StartBuildsDialog.tsx +++ b/packages/admin/cms-admin/src/builds/StartBuildsDialog.tsx @@ -1,6 +1,6 @@ import { gql, useMutation, useQuery } from "@apollo/client"; -import { CancelButton, LocalErrorScopeApolloContext } from "@comet/admin"; -import { Alert, Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material"; +import { Alert, CancelButton, LocalErrorScopeApolloContext } from "@comet/admin"; +import { Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material"; import { DataGrid, GridSelectionModel } from "@mui/x-data-grid"; import * as React from "react"; import { FormattedMessage, useIntl } from "react-intl"; diff --git a/packages/admin/cms-admin/src/common/MasterMenu.tsx b/packages/admin/cms-admin/src/common/MasterMenu.tsx index 972ea385bf..6dee7605b5 100644 --- a/packages/admin/cms-admin/src/common/MasterMenu.tsx +++ b/packages/admin/cms-admin/src/common/MasterMenu.tsx @@ -1,28 +1,46 @@ -import { Menu, MenuCollapsibleItem, MenuContext, MenuItemRouterLink, MenuItemRouterLinkProps, useWindowSize } from "@comet/admin"; +import { + Menu, + MenuCollapsibleItem, + MenuContext, + MenuItemAnchorLink, + MenuItemAnchorLinkProps, + MenuItemRouterLink, + MenuItemRouterLinkProps, + useWindowSize, +} from "@comet/admin"; import * as React from "react"; import { RouteProps, useRouteMatch } from "react-router-dom"; -import { CurrentUserContext } from "../userPermissions/hooks/currentUser"; +import { useUserPermissionCheck } from "../userPermissions/hooks/currentUser"; -export type MasterMenuItem = Omit & { +type MasterMenuItemRoute = Omit & { requiredPermission?: string; route?: RouteProps; to?: string; submenu?: MasterMenuItem[]; }; +type MasterMenuItemAnchor = MenuItemAnchorLinkProps & { + requiredPermission?: string; +}; + +export type MasterMenuItem = MasterMenuItemRoute | MasterMenuItemAnchor; + export type MasterMenuData = MasterMenuItem[]; +export function isMasterMenuItemAnchor(item: MasterMenuItem): item is MasterMenuItemAnchor { + return !!item.href; +} + export function useMenuFromMasterMenuData(items: MasterMenuData): MenuItem[] { - const context = React.useContext(CurrentUserContext); - const checkPermission = (item: MasterMenuItem): boolean => { - if (!item.requiredPermission) return true; - if (context === undefined) - throw new Error("MasterMenu: requiredPermission is set but CurrentUserContext not found. Make sure CurrentUserProvider exists."); - return context.isAllowed(context.currentUser, item.requiredPermission); - }; + const isAllowed = useUserPermissionCheck(); + const checkPermission = (item: MasterMenuItem): boolean => !item.requiredPermission || isAllowed(item.requiredPermission); const mapFn = (item: MasterMenuItem): MenuItem => { + if (isMasterMenuItemAnchor(item)) { + return { menuItem: item }; + } + const { route, submenu, to, ...menuItem } = item; return { menuItem: { @@ -36,12 +54,22 @@ export function useMenuFromMasterMenuData(items: MasterMenuData): MenuItem[] { return items.filter(checkPermission).map(mapFn); } -type MenuItem = { +type MenuItemRoute = { menuItem: MenuItemRouterLinkProps; hasSubmenu: boolean; submenu: MenuItem[]; }; +type MenuItemAnchor = { + menuItem: MenuItemAnchorLinkProps; +}; + +type MenuItem = MenuItemRoute | MenuItemAnchor; + +function isMenuItemAnchor(item: MenuItem): item is MenuItemAnchor { + return !!item.menuItem.href; +} + export interface MasterMenuProps { permanentMenuMinWidth?: number; menu: MasterMenuData; @@ -66,11 +94,17 @@ export const MasterMenu: React.FC = ({ menu, permanentMenuMinWi return ( {menuItems.map((menuItem, index) => - menuItem.hasSubmenu ? ( + isMenuItemAnchor(menuItem) ? ( + + ) : menuItem.hasSubmenu ? ( - {menuItem.submenu.map((submenu, index) => ( - - ))} + {menuItem.submenu.map((submenuItem, index) => + isMenuItemAnchor(submenuItem) ? ( + + ) : ( + + ), + )} ) : ( diff --git a/packages/admin/cms-admin/src/common/MasterMenuRoutes.tsx b/packages/admin/cms-admin/src/common/MasterMenuRoutes.tsx index 84ae7f6281..792cc61c4e 100644 --- a/packages/admin/cms-admin/src/common/MasterMenuRoutes.tsx +++ b/packages/admin/cms-admin/src/common/MasterMenuRoutes.tsx @@ -2,19 +2,18 @@ import { RouteWithErrorBoundary } from "@comet/admin"; import * as React from "react"; import { Redirect, RouteProps, Switch, useRouteMatch } from "react-router-dom"; -import { CurrentUserContext } from "../userPermissions/hooks/currentUser"; -import { MasterMenuData, MasterMenuItem } from "./MasterMenu"; +import { useUserPermissionCheck } from "../userPermissions/hooks/currentUser"; +import { isMasterMenuItemAnchor, MasterMenuData, MasterMenuItem } from "./MasterMenu"; export function useRoutePropsFromMasterMenuData(items: MasterMenuData): RouteProps[] { - const context = React.useContext(CurrentUserContext); - const checkPermission = (item: MasterMenuItem): boolean => { - if (!item.requiredPermission) return true; - if (context === undefined) - throw new Error("MasterMenuRoutes: requiredPermission is set but CurrentUserContext not found. Make sure CurrentUserProvider exists."); - return context.isAllowed(context.currentUser, item.requiredPermission); - }; + const isAllowed = useUserPermissionCheck(); + const checkPermission = (item: MasterMenuItem): boolean => !item.requiredPermission || isAllowed(item.requiredPermission); const flat = (routes: RouteProps[], item: MasterMenuItem): RouteProps[] => { + if (isMasterMenuItemAnchor(item)) { + return routes; + } + if (item.route && checkPermission(item)) { routes.push(item.route); } diff --git a/packages/admin/cms-admin/src/common/image/ImageCrop.tsx b/packages/admin/cms-admin/src/common/image/ImageCrop.tsx index b55a25e1e7..9c05033f74 100644 --- a/packages/admin/cms-admin/src/common/image/ImageCrop.tsx +++ b/packages/admin/cms-admin/src/common/image/ImageCrop.tsx @@ -11,6 +11,10 @@ const focalPoints: GQLFocalPoint[] = ["CENTER", "NORTHEAST", "NORTHWEST", "SOUTH type ImageCropProps = Pick; +const clipValue = (value?: number) => { + return value === undefined ? undefined : Math.max(0, Math.min(100, value)); +}; + export const ImageCrop = (props: ImageCropProps): React.ReactElement => { const form = useForm(); const { @@ -39,10 +43,10 @@ export const ImageCrop = (props: ImageCropProps): React.ReactElement => { } onChange({ - x: percentCrop.x, - y: percentCrop.y, - width: percentCrop.width, - height: percentCrop.height, + x: clipValue(percentCrop.x), + y: clipValue(percentCrop.y), + width: clipValue(percentCrop.width), + height: clipValue(percentCrop.height), }); }} renderSelectionAddon={() => { diff --git a/packages/admin/cms-admin/src/dam/CurrentDamFolderProvider.tsx b/packages/admin/cms-admin/src/dam/CurrentDamFolderProvider.tsx new file mode 100644 index 0000000000..3c5e38c9da --- /dev/null +++ b/packages/admin/cms-admin/src/dam/CurrentDamFolderProvider.tsx @@ -0,0 +1,25 @@ +import * as React from "react"; + +type CurrentDamFolderApi = { + folderId: string | undefined; +}; + +const CurrentDamFolderContext = React.createContext({ folderId: undefined }); + +export function useCurrentDamFolder(): CurrentDamFolderApi { + const context = React.useContext(CurrentDamFolderContext); + if (!context) { + throw new Error("useCurrentDamFolder() must be used within a CurrentDamFolderProvider"); + } + return context; +} + +export function CurrentDamFolderProvider({ + folderId, + children, +}: { + folderId: CurrentDamFolderApi["folderId"]; + children: React.ReactNode; +}): React.ReactElement { + return {children}; +} diff --git a/packages/admin/cms-admin/src/dam/DamPage.tsx b/packages/admin/cms-admin/src/dam/DamPage.tsx index 6662a15974..7641a19243 100644 --- a/packages/admin/cms-admin/src/dam/DamPage.tsx +++ b/packages/admin/cms-admin/src/dam/DamPage.tsx @@ -10,10 +10,15 @@ import { ContentScopeIndicator } from "../contentScope/ContentScopeIndicator"; import { ContentScopeInterface, useContentScope } from "../contentScope/Provider"; import { useContentScopeConfig } from "../contentScope/useContentScopeConfig"; import { DamScopeProvider } from "./config/DamScopeProvider"; +import { useDamConfig } from "./config/useDamConfig"; import { DamTable } from "./DamTable"; type Props = { renderContentScopeIndicator?: (scope: ContentScopeInterface) => React.ReactNode; + /** + * @deprecated Use `additionalToolbarItems` option in `DamConfigProvider` instead + */ + additionalToolbarItems?: React.ReactNode; }; const defaultRenderContentScopeIndicator = () => ( @@ -47,16 +52,20 @@ const DamTableWrapper = styled("div")` grid-template-rows: max-content; `; -function DamPage({ renderContentScopeIndicator = defaultRenderContentScopeIndicator }: Props): React.ReactElement { +function DamPage({ renderContentScopeIndicator = defaultRenderContentScopeIndicator, additionalToolbarItems }: Props): React.ReactElement { const { scope, match } = useContentScope(); const routeMatch = useRouteMatch(); const damRouteLocation = routeMatch.url.replace(match.url, ""); useContentScopeConfig({ redirectPathAfterChange: damRouteLocation }); + const damConfig = useDamConfig(); return ( - + ); diff --git a/packages/admin/cms-admin/src/dam/DamTable.tsx b/packages/admin/cms-admin/src/dam/DamTable.tsx index c2ed224648..4712bf881f 100644 --- a/packages/admin/cms-admin/src/dam/DamTable.tsx +++ b/packages/admin/cms-admin/src/dam/DamTable.tsx @@ -21,6 +21,7 @@ import { Button } from "@mui/material"; import * as React from "react"; import { FormattedMessage, useIntl } from "react-intl"; +import { CurrentDamFolderProvider } from "./CurrentDamFolderProvider"; import { ManualDuplicatedFilenamesHandlerContextProvider } from "./DataGrid/duplicatedFilenames/ManualDuplicatedFilenamesHandler"; import { FileUploadContextProvider } from "./DataGrid/fileUpload/FileUploadContext"; import { UploadFilesButton } from "./DataGrid/fileUpload/UploadFilesButton"; @@ -39,6 +40,7 @@ import EditFile from "./FileForm/EditFile"; interface FolderProps extends DamConfig { filterApi: IFilterApi; + additionalToolbarItems?: React.ReactNode; id?: string; } @@ -70,52 +72,55 @@ const Folder = ({ id, filterApi, ...props }: FolderProps) => { }; return ( - - - - {props.contentScopeIndicator} - - - - - - - } sx={{ mx: 2 }}> - - - } - anchorOrigin={{ - vertical: "bottom", - horizontal: "left", - }} - transformOrigin={{ - vertical: "top", - horizontal: "left", - }} - folderId={id} - filter={uploadFilters} - /> + + + + + {props.contentScopeIndicator} + + + + + + + {props.additionalToolbarItems} + } sx={{ mx: 2 }}> + + + } + anchorOrigin={{ + vertical: "bottom", + horizontal: "left", + }} + transformOrigin={{ + vertical: "top", + horizontal: "left", + }} + folderId={id} + filter={uploadFilters} + /> - - - - - - - - {(selectedId: string) => { - return ; - }} - - - {(selectedId) => { - setSelectedFolderId(selectedId); - return ; - }} - - + + + + + + + + {(selectedId: string) => { + return ; + }} + + + {(selectedId) => { + setSelectedFolderId(selectedId); + return ; + }} + + + ); }; @@ -127,6 +132,7 @@ export interface DamConfig { contentScopeIndicator?: React.ReactNode; hideMultiselect?: boolean; hideDamActions?: boolean; + additionalToolbarItems?: React.ReactNode; } type DamTableProps = DamConfig; @@ -161,7 +167,7 @@ export const DamTable = ({ ...props }: DamTableProps): React.ReactElement => { - + diff --git a/packages/admin/cms-admin/src/dam/DataGrid/FolderDataGrid.tsx b/packages/admin/cms-admin/src/dam/DataGrid/FolderDataGrid.tsx index 6bf060dc10..256c594359 100644 --- a/packages/admin/cms-admin/src/dam/DataGrid/FolderDataGrid.tsx +++ b/packages/admin/cms-admin/src/dam/DataGrid/FolderDataGrid.tsx @@ -23,12 +23,11 @@ import { useDamScope } from "../config/useDamScope"; import { DamConfig, DamFilter } from "../DamTable"; import AddFolder from "../FolderForm/AddFolder"; import EditFolder from "../FolderForm/EditFolder"; -import { clearDamItemCache } from "../helpers/clearDamItemCache"; import { isFile } from "../helpers/isFile"; import { isFolder } from "../helpers/isFolder"; import { MoveDamItemDialog } from "../MoveDamItemDialog/MoveDamItemDialog"; import DamContextMenu from "./DamContextMenu"; -import { useFileUpload } from "./fileUpload/useFileUpload"; +import { useDamFileUpload } from "./fileUpload/useDamFileUpload"; import { damFolderQuery, damItemListPosition, damItemsListQuery } from "./FolderDataGrid.gql"; import { GQLDamFolderQuery, @@ -136,12 +135,8 @@ const FolderDataGrid = ({ const { allAcceptedMimeTypes } = useDamAcceptedMimeTypes(); - const fileUploadApi = useFileUpload({ + const fileUploadApi = useDamFileUpload({ acceptedMimetypes: props.allowedMimetypes ?? allAcceptedMimeTypes, - onAfterUpload: () => { - apolloClient.reFetchObservableQueries(); - clearDamItemCache(apolloClient.cache); - }, }); React.useEffect(() => { @@ -281,7 +276,7 @@ const FolderDataGrid = ({ hideHoverStyles(); hideUploadFooter(); - await fileUploadApi.uploadFiles({ acceptedFiles, fileRejections }, currentFolderId); + await fileUploadApi.uploadFiles({ acceptedFiles, fileRejections }, { folderId: currentFolderId }); // react-dropzone doesn't support folder drops natively // the only way to find out if an empty folder was dropped is if there are no rejected files and no accepted files diff --git a/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/FileUploadContext.tsx b/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/FileUploadContext.tsx index 80c3e30890..3725b2d3f9 100644 --- a/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/FileUploadContext.tsx +++ b/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/FileUploadContext.tsx @@ -1,9 +1,12 @@ import React from "react"; +import { FileWithFolderPath } from "./useDamFileUpload"; + export interface NewlyUploadedItem { id: string; parentId?: string; type: "file" | "folder"; + file?: FileWithFolderPath; } interface FileUploadContextApi { diff --git a/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/FileUploadErrorDialog.tsx b/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/FileUploadErrorDialog.tsx index b5179ddecd..fc9485c63a 100644 --- a/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/FileUploadErrorDialog.tsx +++ b/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/FileUploadErrorDialog.tsx @@ -18,7 +18,7 @@ import { styled } from "@mui/material/styles"; import React from "react"; import { FormattedMessage } from "react-intl"; -import { FileUploadValidationError } from "./useFileUpload"; +import { FileUploadValidationError } from "./useDamFileUpload"; const Path = styled(Typography)` color: ${({ theme }) => theme.palette.grey[300]}; diff --git a/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/UploadFilesButton.tsx b/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/UploadFilesButton.tsx index 071e650ec9..9a98b67286 100644 --- a/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/UploadFilesButton.tsx +++ b/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/UploadFilesButton.tsx @@ -1,4 +1,3 @@ -import { useApolloClient } from "@apollo/client"; import { Upload } from "@comet/admin-icons"; import { Button } from "@mui/material"; import * as React from "react"; @@ -6,8 +5,7 @@ import { FileRejection, useDropzone } from "react-dropzone"; import { FormattedMessage } from "react-intl"; import { useDamAcceptedMimeTypes } from "../../config/useDamAcceptedMimeTypes"; -import { clearDamItemCache } from "../../helpers/clearDamItemCache"; -import { useFileUpload } from "./useFileUpload"; +import { useDamFileUpload } from "./useDamFileUpload"; interface UploadSplitButtonProps { folderId?: string; @@ -17,7 +15,6 @@ interface UploadSplitButtonProps { } export const UploadFilesButton = ({ folderId, filter }: UploadSplitButtonProps): React.ReactElement => { - const client = useApolloClient(); const { allAcceptedMimeTypes } = useDamAcceptedMimeTypes(); const fileInputRef = React.useRef(null); @@ -26,18 +23,14 @@ export const UploadFilesButton = ({ folderId, filter }: UploadSplitButtonProps): uploadFiles, dialogs: fileUploadDialogs, dropzoneConfig, - } = useFileUpload({ + } = useDamFileUpload({ acceptedMimetypes: filter?.allowedMimetypes ?? allAcceptedMimeTypes, - onAfterUpload: () => { - client.reFetchObservableQueries(); - clearDamItemCache(client.cache); - }, }); const { getInputProps } = useDropzone({ ...dropzoneConfig, onDrop: async (acceptedFiles: File[], fileRejections: FileRejection[]) => { - await uploadFiles({ acceptedFiles, fileRejections }, folderId); + await uploadFiles({ acceptedFiles, fileRejections }, { folderId: folderId }); }, }); diff --git a/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useFileUpload.gql.ts b/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useDamFileUpload.gql.ts similarity index 94% rename from packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useFileUpload.gql.ts rename to packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useDamFileUpload.gql.ts index e32acf5e85..ae5513077a 100644 --- a/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useFileUpload.gql.ts +++ b/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useDamFileUpload.gql.ts @@ -5,7 +5,7 @@ export { GQLDamFolderByNameAndParentIdQueryVariables, GQLDamFolderForFolderUploadMutation, GQLDamFolderForFolderUploadMutationVariables, -} from "./useFileUpload.gql.generated"; +} from "./useDamFileUpload.gql.generated"; export const damFolderByNameAndParentId = gql` query DamFolderByNameAndParentId($name: String!, $parentId: ID, $scope: DamScopeInput!) { diff --git a/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useFileUpload.tsx b/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useDamFileUpload.tsx similarity index 92% rename from packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useFileUpload.tsx rename to packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useDamFileUpload.tsx index 775a409806..e5ee3a7ace 100644 --- a/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useFileUpload.tsx +++ b/packages/admin/cms-admin/src/dam/DataGrid/fileUpload/useDamFileUpload.tsx @@ -9,6 +9,7 @@ import { NetworkError, UnknownError } from "../../../common/errors/errorMessages import { upload } from "../../../form/file/upload"; import { useDamAcceptedMimeTypes } from "../../config/useDamAcceptedMimeTypes"; import { useDamScope } from "../../config/useDamScope"; +import { clearDamItemCache } from "../../helpers/clearDamItemCache"; import { FilenameData, useManualDuplicatedFilenamesHandler } from "../duplicatedFilenames/ManualDuplicatedFilenamesHandler"; import { NewlyUploadedItem, useFileUploadContext } from "./FileUploadContext"; import { FileUploadErrorDialog } from "./FileUploadErrorDialog"; @@ -21,25 +22,24 @@ import { UnsupportedTypeError, } from "./fileUploadErrorMessages"; import { ProgressDialog } from "./ProgressDialog"; -import { createDamFolderForFolderUpload, damFolderByNameAndParentId } from "./useFileUpload.gql"; +import { createDamFolderForFolderUpload, damFolderByNameAndParentId } from "./useDamFileUpload.gql"; import { GQLDamFolderByNameAndParentIdQuery, GQLDamFolderByNameAndParentIdQueryVariables, GQLDamFolderForFolderUploadMutation, GQLDamFolderForFolderUploadMutationVariables, -} from "./useFileUpload.gql.generated"; +} from "./useDamFileUpload.gql.generated"; interface FileWithPath extends File { path?: string; } -interface FileWithFolderPath extends FileWithPath { +export interface FileWithFolderPath extends FileWithPath { folderPath?: string; } -interface UploadFileOptions { +interface UploadDamFileOptions { acceptedMimetypes?: string[]; - onAfterUpload?: (errorOccurred: boolean) => void; } interface Files { @@ -47,8 +47,18 @@ interface Files { fileRejections: FileRejection[]; } +type ImportSource = { importSourceType: never; importSourceId: never } | { importSourceType: string; importSourceId: string }; + +interface UploadFilesOptions { + folderId?: string; + importSource?: ImportSource; +} + export interface FileUploadApi { - uploadFiles: ({ acceptedFiles, fileRejections }: Files, folderId?: string) => void; + uploadFiles: ( + { acceptedFiles, fileRejections }: Files, + { folderId, importSource }: UploadFilesOptions, + ) => Promise<{ hasError: boolean; rejectedFiles: RejectedFile[]; uploadedItems: NewlyUploadedItem[] }>; validationErrors?: FileUploadValidationError[]; maxFileSizeInBytes: number; dialogs: React.ReactNode; @@ -65,6 +75,10 @@ export interface FileUploadValidationError { message: React.ReactNode; } +interface RejectedFile { + file: File; +} + const addFolderPathToFiles = async (acceptedFiles: FileWithPath[]): Promise => { const newFiles = []; @@ -98,7 +112,7 @@ const addFolderPathToFiles = async (acceptedFiles: FileWithPath[]): Promise { +export const useDamFileUpload = (options: UploadDamFileOptions): FileUploadApi => { const context = useCmsBlockContext(); // TODO create separate CmsContext? const client = useApolloClient(); const manualDuplicatedFilenamesHandler = useManualDuplicatedFilenamesHandler(); @@ -351,17 +365,22 @@ export const useFileUpload = (options: UploadFileOptions): FileUploadApi => { [manualDuplicatedFilenamesHandler], ); - const uploadFiles = async ({ acceptedFiles, fileRejections }: Files, folderId?: string) => { + const uploadFiles = async ( + { acceptedFiles, fileRejections }: Files, + { folderId, importSource }: UploadFilesOptions, + ): Promise<{ hasError: boolean; rejectedFiles: RejectedFile[]; uploadedItems: NewlyUploadedItem[] }> => { setProgressDialogOpen(true); setValidationErrors(undefined); const uploadedFolders: Array = []; const uploadedFiles: Array = []; + const rejectedFiles: Array = []; let errorOccurred = false; if (fileRejections.length > 0) { errorOccurred = true; generateValidationErrorsForRejectedFiles(fileRejections); + rejectedFiles.push(...fileRejections.map(({ errors, ...rejection }) => rejection)); } if (acceptedFiles.length > 0) { @@ -397,6 +416,8 @@ export const useFileUpload = (options: UploadFileOptions): FileUploadApi => { file, folderId: targetFolderId, scope, + importSourceId: importSource?.importSourceId, + importSourceType: importSource?.importSourceType, }, cancelUpload.current.token, { @@ -406,7 +427,7 @@ export const useFileUpload = (options: UploadFileOptions): FileUploadApi => { }, ); - uploadedFiles.push({ id: response.data.id, parentId: targetFolderId, type: "file" }); + uploadedFiles.push({ id: response.data.id, parentId: targetFolderId, type: "file", file }); } catch (err) { errorOccurred = true; const typedErr = err as AxiosError<{ error: string; message: string; statusCode: number }>; @@ -433,8 +454,13 @@ export const useFileUpload = (options: UploadFileOptions): FileUploadApi => { } else { addValidationError(file, ); } + + rejectedFiles.push({ file }); } } + + await client.reFetchObservableQueries(); + clearDamItemCache(client.cache); } setProgressDialogOpen(false); @@ -443,9 +469,10 @@ export const useFileUpload = (options: UploadFileOptions): FileUploadApi => { } setTotalSizes({}); setUploadedSizes({}); - options.onAfterUpload?.(errorOccurred); addNewlyUploadedItems([...uploadedFolders, ...uploadedFiles]); + + return { hasError: errorOccurred, rejectedFiles: rejectedFiles, uploadedItems: [...uploadedFolders, ...uploadedFiles] }; }; return { diff --git a/packages/admin/cms-admin/src/dam/DataGrid/label/DamItemLabelColumn.tsx b/packages/admin/cms-admin/src/dam/DataGrid/label/DamItemLabelColumn.tsx index ae514fff77..1d76b5c347 100644 --- a/packages/admin/cms-admin/src/dam/DataGrid/label/DamItemLabelColumn.tsx +++ b/packages/admin/cms-admin/src/dam/DataGrid/label/DamItemLabelColumn.tsx @@ -9,7 +9,7 @@ import { useDamConfig } from "../../config/useDamConfig"; import { DamFilter } from "../../DamTable"; import { isFile } from "../../helpers/isFile"; import { isFolder } from "../../helpers/isFolder"; -import { FileUploadApi } from "../fileUpload/useFileUpload"; +import { FileUploadApi } from "../fileUpload/useDamFileUpload"; import { GQLDamFileTableFragment, GQLDamFolderTableFragment } from "../FolderDataGrid"; import { DamItemMatches } from "../useDamSearchHighlighting"; import DamItemLabel from "./DamItemLabel"; @@ -95,7 +95,7 @@ export const DamItemLabelColumn: React.VoidFunctionComponent(null); @@ -104,18 +101,14 @@ export const DamMoreActions = ({ button, transformOrigin, anchorOrigin, folderId uploadFiles, dialogs: fileUploadDialogs, dropzoneConfig, - } = useFileUpload({ + } = useDamFileUpload({ acceptedMimetypes: filter?.allowedMimetypes ?? allAcceptedMimeTypes, - onAfterUpload: () => { - client.reFetchObservableQueries(); - clearDamItemCache(client.cache); - }, }); const { getInputProps } = useDropzone({ ...dropzoneConfig, onDrop: async (acceptedFiles: File[], fileRejections: FileRejection[]) => { - await uploadFiles({ acceptedFiles, fileRejections }, folderId); + await uploadFiles({ acceptedFiles, fileRejections }, { folderId: folderId }); }, }); diff --git a/packages/admin/cms-admin/src/dam/FileForm/FileSettingsFields.tsx b/packages/admin/cms-admin/src/dam/FileForm/FileSettingsFields.tsx index ebdd5c71f7..88b9fb4764 100644 --- a/packages/admin/cms-admin/src/dam/FileForm/FileSettingsFields.tsx +++ b/packages/admin/cms-admin/src/dam/FileForm/FileSettingsFields.tsx @@ -27,14 +27,12 @@ const damIsFilenameOccupiedQuery = gql` export type LicenseType = GQLLicenseType | "NO_LICENSE"; -const licenseTypeArray: readonly LicenseType[] = ["NO_LICENSE", "ROYALTY_FREE", "RIGHTS_MANAGED", "SUBSCRIPTION", "MICRO"]; +const licenseTypeArray: readonly LicenseType[] = ["NO_LICENSE", "ROYALTY_FREE", "RIGHTS_MANAGED"]; const licenseTypeLabels: { [key in LicenseType]: React.ReactNode } = { NO_LICENSE: "-", ROYALTY_FREE: , RIGHTS_MANAGED: , - SUBSCRIPTION: , - MICRO: , }; export const FileSettingsFields = ({ isImage, folderId }: SettingsFormProps): React.ReactElement => { diff --git a/packages/admin/cms-admin/src/dam/config/DamConfigContext.tsx b/packages/admin/cms-admin/src/dam/config/DamConfigContext.tsx index 5fed745157..05c86e2c4b 100644 --- a/packages/admin/cms-admin/src/dam/config/DamConfigContext.tsx +++ b/packages/admin/cms-admin/src/dam/config/DamConfigContext.tsx @@ -5,6 +5,7 @@ export interface DamConfig { scopeParts?: string[]; enableLicenseFeature?: boolean; requireLicense?: boolean; + additionalToolbarItems?: React.ReactNode; } export const DamConfigContext = React.createContext(undefined); diff --git a/packages/admin/cms-admin/src/form/file/chooseFile/ChooseFileDialog.tsx b/packages/admin/cms-admin/src/form/file/chooseFile/ChooseFileDialog.tsx index bdd3469ae2..38963f496f 100644 --- a/packages/admin/cms-admin/src/form/file/chooseFile/ChooseFileDialog.tsx +++ b/packages/admin/cms-admin/src/form/file/chooseFile/ChooseFileDialog.tsx @@ -114,6 +114,7 @@ export const ChooseFileDialog = ({ open, onClose, onChooseFile, allowedMimetypes hideContextMenu={true} hideMultiselect={true} hideArchiveFilter={true} + additionalToolbarItems={damConfig.additionalToolbarItems} /> diff --git a/packages/admin/cms-admin/src/form/file/upload.ts b/packages/admin/cms-admin/src/form/file/upload.ts index c9e9e17da1..e164990132 100644 --- a/packages/admin/cms-admin/src/form/file/upload.ts +++ b/packages/admin/cms-admin/src/form/file/upload.ts @@ -4,6 +4,8 @@ interface UploadFileData { file: File; scope: Record; folderId?: string; + importSourceId?: string; + importSourceType?: string; } export function upload( @@ -15,6 +17,10 @@ export function upload( const formData = new FormData(); formData.append("file", data.file); formData.append("scope", JSON.stringify(data.scope)); + if (data.importSourceId && data.importSourceType) { + formData.append("importSourceId", data.importSourceId); + formData.append("importSourceType", data.importSourceType); + } if (data.folderId !== undefined) { formData.append("folderId", data.folderId); } diff --git a/packages/admin/cms-admin/src/index.ts b/packages/admin/cms-admin/src/index.ts index 966b39eab7..4fe3a22358 100644 --- a/packages/admin/cms-admin/src/index.ts +++ b/packages/admin/cms-admin/src/index.ts @@ -48,7 +48,9 @@ export { DamConfigProvider } from "./dam/config/DamConfigProvider"; export { damDefaultAcceptedMimeTypes } from "./dam/config/damDefaultAcceptedMimeTypes"; export { useDamAcceptedMimeTypes } from "./dam/config/useDamAcceptedMimeTypes"; export { useDamConfig } from "./dam/config/useDamConfig"; +export { useCurrentDamFolder } from "./dam/CurrentDamFolderProvider"; export { DamPage } from "./dam/DamPage"; +export { useDamFileUpload } from "./dam/DataGrid/fileUpload/useDamFileUpload"; export { DashboardHeader, DashboardHeaderProps } from "./dashboard/DashboardHeader"; export { DashboardWidgetRoot, DashboardWidgetRootProps } from "./dashboard/widgets/DashboardWidgetRoot"; export { LatestBuildsDashboardWidget } from "./dashboard/widgets/LatestBuildsDashboardWidget"; @@ -90,7 +92,7 @@ export type { SiteConfig } from "./sitesConfig/SitesConfigContext"; export { SitesConfigProvider } from "./sitesConfig/SitesConfigProvider"; export { useSiteConfig } from "./sitesConfig/useSiteConfig"; export { useSitesConfig } from "./sitesConfig/useSitesConfig"; -export { CurrentUserInterface, CurrentUserProvider, useCurrentUser } from "./userPermissions/hooks/currentUser"; +export { CurrentUserInterface, CurrentUserProvider, useCurrentUser, useUserPermissionCheck } from "./userPermissions/hooks/currentUser"; export { UserPermissionsPage } from "./userPermissions/UserPermissionsPage"; // eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports import emotionStyled from "@emotion/styled"; diff --git a/packages/admin/cms-admin/src/pages/SaveConflictDialog.tsx b/packages/admin/cms-admin/src/pages/SaveConflictDialog.tsx index 5f42f4014b..12dcde7ba5 100644 --- a/packages/admin/cms-admin/src/pages/SaveConflictDialog.tsx +++ b/packages/admin/cms-admin/src/pages/SaveConflictDialog.tsx @@ -1,7 +1,6 @@ -import { messages } from "@comet/admin"; -import { Clear, Delete, OpenNewTab, Warning } from "@comet/admin-icons"; -import { fontWeights } from "@comet/admin-theme"; -import { Alert, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, List, ListItem, Stack, Typography } from "@mui/material"; +import { Alert, messages } from "@comet/admin"; +import { Clear, Delete, OpenNewTab } from "@comet/admin-icons"; +import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, List, ListItem, Stack, Typography } from "@mui/material"; import { styled } from "@mui/material/styles"; import makeStyles from "@mui/styles/makeStyles"; import * as React from "react"; @@ -28,12 +27,6 @@ export const useStyles = makeStyles((theme) => ({ backgroundColor: theme.palette.error.dark, }, }, - errorAlert: { - backgroundColor: "white", - marginBottom: "20px", - color: "black", - fontWeight: fontWeights.fontWeightBold, - }, })); function SaveConflictDialog({ open, onClosePressed, onDiscardChangesPressed }: SaveConflictDialogProps): React.ReactElement { @@ -47,7 +40,7 @@ function SaveConflictDialog({ open, onClosePressed, onDiscardChangesPressed }: S - } classes={{ root: styles.errorAlert }}> + diff --git a/packages/admin/cms-admin/src/pages/pageTree/PageTreeContext.ts b/packages/admin/cms-admin/src/pages/pageTree/PageTreeContext.ts index 428f136d63..74b3bd107d 100644 --- a/packages/admin/cms-admin/src/pages/pageTree/PageTreeContext.ts +++ b/packages/admin/cms-admin/src/pages/pageTree/PageTreeContext.ts @@ -9,6 +9,7 @@ export type AllCategories = Array<{ category: string; label: React.ReactNode }>; export interface PageTreeContext { allCategories: AllCategories; + currentCategory: string; documentTypes: Record; tree: TreeMap; query: DocumentNode; diff --git a/packages/admin/cms-admin/src/pages/pageTree/useCopyPastePages.tsx b/packages/admin/cms-admin/src/pages/pageTree/useCopyPastePages.tsx index d452193c78..b401f8c6a1 100644 --- a/packages/admin/cms-admin/src/pages/pageTree/useCopyPastePages.tsx +++ b/packages/admin/cms-admin/src/pages/pageTree/useCopyPastePages.tsx @@ -4,7 +4,7 @@ import * as React from "react"; import { FormattedMessage } from "react-intl"; import { useCmsBlockContext } from "../../blocks/useCmsBlockContext"; -import { useContentScope } from "../../contentScope/Provider"; +import { ContentScopeInterface, useContentScope } from "../../contentScope/Provider"; import { useDamScope } from "../../dam/config/useDamScope"; import { GQLDocument, GQLPageQuery, GQLPageQueryVariables } from "../../documents/types"; import { useProgressDialog } from "./useCopyPastePages/ProgressDialog"; @@ -16,6 +16,7 @@ export type PageClipboard = GQLPageTreePageFragment & { document?: GQLDocument | export interface PagesClipboard { pages: PageClipboard[]; + scope: ContentScopeInterface; } /** @@ -64,7 +65,7 @@ interface UseCopyPastePagesApi { * This hooks provides some helper functions to copy / paste Pages and PageTreeNodes */ function useCopyPastePages(): UseCopyPastePagesApi { - const { documentTypes } = usePageTreeContext(); + const { documentTypes, currentCategory } = usePageTreeContext(); const client = useApolloClient(); const { scope } = useContentScope(); const damScope = useDamScope(); @@ -107,10 +108,11 @@ function useCopyPastePages(): UseCopyPastePagesApi { const clipboardPages: PagesClipboard = { pages: [...pagesWithDocuments], + scope, }; return clipboardPages; }, - [client, documentTypes], + [client, documentTypes, scope], ); const writeToClipboard = React.useCallback(async (pages: PagesClipboard) => { return writeClipboardText(JSON.stringify(pages)); @@ -170,7 +172,7 @@ function useCopyPastePages(): UseCopyPastePagesApi { const sendPagesCb = React.useCallback( async (parentId: string | null, pages: PagesClipboard, options: SendPagesOptions) => { try { - await sendPages(parentId, pages, options, { client, scope, documentTypes, blockContext, damScope }, updateProgress); + await sendPages(parentId, pages, options, { client, scope, documentTypes, blockContext, damScope, currentCategory }, updateProgress); } catch (e) { errorDialog?.showError({ title: , @@ -183,7 +185,7 @@ function useCopyPastePages(): UseCopyPastePagesApi { updateProgress(undefined); //hides progress dialog } }, - [client, scope, documentTypes, blockContext, damScope, updateProgress, errorDialog], + [client, scope, documentTypes, blockContext, damScope, currentCategory, updateProgress, errorDialog], ); return { diff --git a/packages/admin/cms-admin/src/pages/pageTree/useCopyPastePages/sendPages.tsx b/packages/admin/cms-admin/src/pages/pageTree/useCopyPastePages/sendPages.tsx index 9552c81599..cdef63f91f 100644 --- a/packages/admin/cms-admin/src/pages/pageTree/useCopyPastePages/sendPages.tsx +++ b/packages/admin/cms-admin/src/pages/pageTree/useCopyPastePages/sendPages.tsx @@ -1,6 +1,6 @@ import { ApolloClient, gql } from "@apollo/client"; import { LocalErrorScopeApolloContext } from "@comet/admin"; -import { ReplaceDependencyObject } from "@comet/blocks-admin"; +import { BlockDependency, ReplaceDependencyObject } from "@comet/blocks-admin"; import isEqual from "lodash.isequal"; import * as React from "react"; import { FormattedMessage } from "react-intl"; @@ -72,6 +72,7 @@ interface SendPagesDependencies { documentTypes: PageTreeContext["documentTypes"]; blockContext: CmsBlockContext; damScope: Record; + currentCategory: string; } /** @@ -84,20 +85,21 @@ interface SendPagesDependencies { * 2b. Create new PageTreeNode with new name "{name} {uniqueNumber}" and new parent * 3. create documents (and copy files if required) and attach them to the page tree nodes * 3a. Copy all files used on page to target scope - * 3b. Create new document and attach it to new page tree node + * 3b. Replace unhandled dependencies with undefined (when copying to another scope) + * 3c. Create new document and attach it to new page tree node * 4. Refetch Pages query * **/ export async function sendPages( parentId: string | null, - { pages }: PagesClipboard, + { pages, scope: sourceContentScope }: PagesClipboard, options: SendPagesOptions, - { client, scope, documentTypes, blockContext, damScope }: SendPagesDependencies, + { client, scope: targetContentScope, documentTypes, blockContext, damScope: targetDamScope, currentCategory }: SendPagesDependencies, updateProgress: (progress: number, message: React.ReactNode) => void, ): Promise { const dependencyReplacements = createPageTreeNodeIdReplacements(pages); let inboxFolderIdForCopiedFiles: string | undefined = undefined; - const hasDamScope = Object.entries(damScope).length > 0; + const hasDamScope = Object.entries(targetDamScope).length > 0; // 1. find all source scopes of file dependencies, to create an dam inbox folder if needed updateProgress(0, ); @@ -114,7 +116,10 @@ export async function sendPages( //TODO use damFile.size; to build a progress bar for uploading/downloading files if (dependencyReplacements.some((replacement) => replacement.type == "DamFile" && replacement.originalId === damFile.id)) { //file already handled (same file used multiple times on page) - } else if (damFile.fileUrl.startsWith(blockContext.damConfig.apiUrl) && (!hasDamScope || isEqual(damFile.scope, damScope))) { + } else if ( + damFile.fileUrl.startsWith(blockContext.damConfig.apiUrl) && + (!hasDamScope || isEqual(damFile.scope, targetDamScope)) + ) { //same scope, same server, no need to copy } else { // TODO eventually handle multiple files in one request for better performance @@ -128,7 +133,7 @@ export async function sendPages( `, variables: { id: damFile.id, - scope: damScope, + scope: targetDamScope, imageCropArea: damFile.image?.cropArea, }, }); @@ -158,7 +163,7 @@ export async function sendPages( if (sourceScopes.length > 0) { const { id } = await createInboxFolder({ client, - targetScope: damScope, + targetScope: targetDamScope, sourceScopes, }); inboxFolderIdForCopiedFiles = id; @@ -186,7 +191,7 @@ export async function sendPages( variables: { parentId: newParentId, slug, - scope, + scope: targetContentScope, }, fetchPolicy: "network-only", context: LocalErrorScopeApolloContext, @@ -216,8 +221,8 @@ export async function sendPages( parentId: newParentId, pos: options.targetPos ? options.targetPos + posOffset : undefined, }, - contentScope: scope, - category: node.category, + contentScope: targetContentScope, + category: currentCategory, }, context: LocalErrorScopeApolloContext, }); @@ -272,7 +277,7 @@ export async function sendPages( //already copied } else if (damFile.fileUrl.startsWith(blockContext.damConfig.apiUrl)) { //our own api, no need to download&upload - if (!hasDamScope || isEqual(damFile.scope, damScope)) { + if (!hasDamScope || isEqual(damFile.scope, targetDamScope)) { //same scope, same server, no need to copy } else { //batch copy below @@ -287,7 +292,7 @@ export async function sendPages( const file = new File([fileBlob], damFile.name, { type: damFile.mimetype }); const formData = new FormData(); formData.append("file", file); - if (hasDamScope) formData.append("scope", JSON.stringify(damScope)); + if (hasDamScope) formData.append("scope", JSON.stringify(targetDamScope)); formData.append("folderId", inboxFolderIdForCopiedFiles); if (damFile.title) formData.append("title", damFile.title); if (damFile.altText) formData.append("altText", damFile.altText); @@ -313,7 +318,7 @@ export async function sendPages( `, variables: { url: damFile.fileUrl, - scope: damScope, + scope: targetDamScope, input: { name: damFile.name, folderId: inboxFolderIdForCopiedFiles, @@ -350,7 +355,16 @@ export async function sendPages( } } - // 3b. Create new document and attach it to new page tree node + // 3b. Replace unhandled dependencies with undefined (when copying to another scope) + if (sourcePage.document && !isEqual(sourceContentScope, targetContentScope)) { + const unhandledDependencies = unhandledDependenciesFromDocument(documentType, sourcePage.document, { + existingReplacements: dependencyReplacements, + }); + const replacementsForUnhandledDependencies = createUndefinedReplacementsForDependencies(unhandledDependencies); + dependencyReplacements.push(...replacementsForUnhandledDependencies); + } + + // 3c. Create new document and attach it to new page tree node const newDocumentId = uuid(); if ( sourcePage?.document != null && @@ -396,6 +410,39 @@ function createPageTreeNodeIdReplacements(nodes: PageClipboard[]): ReplaceDepend return replacements; } +function unhandledDependenciesFromDocument( + documentType: DocumentInterface, + document: GQLDocument, + { existingReplacements }: { existingReplacements: ReplaceDependencyObject[] }, +) { + const unhandledDependencies = documentType + .dependencies(document) + .filter( + (dependency) => + !existingReplacements.some( + (replacement) => replacement.originalId === dependency.id && replacement.type === dependency.targetGraphqlObjectType, + ), + ); + + return unhandledDependencies; +} + +function createUndefinedReplacementsForDependencies(dependencies: BlockDependency[]) { + const existingReplacements = new Set(); + const replacements: ReplaceDependencyObject[] = []; + + for (const dependency of dependencies) { + const key = `${dependency.targetGraphqlObjectType}#${dependency.id}`; + + if (!existingReplacements.has(key)) { + replacements.push({ type: dependency.targetGraphqlObjectType, originalId: dependency.id, replaceWithId: undefined }); + existingReplacements.add(key); + } + } + + return replacements; +} + function fileDependenciesFromDocument(documentType: DocumentInterface, document: GQLDocument) { return ( documentType diff --git a/packages/admin/cms-admin/src/pages/pageTreeSelect/PageTreeSelectDialog.tsx b/packages/admin/cms-admin/src/pages/pageTreeSelect/PageTreeSelectDialog.tsx index cf62ad92ea..944bb65127 100644 --- a/packages/admin/cms-admin/src/pages/pageTreeSelect/PageTreeSelectDialog.tsx +++ b/packages/admin/cms-admin/src/pages/pageTreeSelect/PageTreeSelectDialog.tsx @@ -223,7 +223,13 @@ export default function PageTreeSelectDialog({ value, onChange, open, onClose, d - + page.id == selectedId); if (!page) { - return null; + return ( + + } + severity="error" + > + + + + ); } if (page.visibility === "Archived") { - return <>403, not allowed; + return ( + + } + severity="error" + > + + + + ); } const documentType = documentTypes[page.documentType]; diff --git a/packages/admin/cms-admin/src/pages/useSaveConflict.tsx b/packages/admin/cms-admin/src/pages/useSaveConflict.tsx index 84013675f2..3f923cc1c5 100644 --- a/packages/admin/cms-admin/src/pages/useSaveConflict.tsx +++ b/packages/admin/cms-admin/src/pages/useSaveConflict.tsx @@ -1,4 +1,6 @@ import { useSnackbarApi } from "@comet/admin"; +// TODO Our Alert currently can't be used inside a Snackbar +// eslint-disable-next-line no-restricted-imports import { Alert, Snackbar } from "@mui/material"; import * as React from "react"; import { FormattedMessage } from "react-intl"; diff --git a/packages/admin/cms-admin/src/preview/README.md b/packages/admin/cms-admin/src/preview/README.md index a6aab80192..1453c133f1 100644 --- a/packages/admin/cms-admin/src/preview/README.md +++ b/packages/admin/cms-admin/src/preview/README.md @@ -47,7 +47,6 @@ EditPage: previewApi, created with useBlockPreview: State containing showOnlyVis - highlights blocks with matching hoveredSiteRoute (with useIFrameBridge) - sends SelectComponent on block admin render (using SelectPreviewComponent[blocks-admin] with useIFrameBridge) - sends HoverComponent on block preview hover (using HoverPreviewComponent[blocks-admin] with useIFrameBridge) - ``` ### Site: States, Contexts and Components @@ -74,7 +73,7 @@ IFrameBridgePreviewPage (src/pages/preview/admin/page.tsx) ## Site Preview -Uses Next.js Preview Mode to live render pages (SSR), optionally with invisible blocks shown. +Similar to real site but live rendered (SSR) and optionally with invisible blocks shown. ### iframe messages: admin -> site @@ -82,14 +81,15 @@ Uses Next.js Preview Mode to live render pages (SSR), optionally with invisible ### URL: admin -> site -Admin opens I-Frame with {previewSiteUrl}/api/preview to enter Next.js Preview Mode and passes the following parameters: - -- path: which pathname to be shown -- includeInvisibleBlocks -- timestamp & hash: is validated to activate Preview Mode - + - page url (real site url prefixed with /preview) + - __preview parameter: { includeInvisibleBlocks: boolean } (JSON encoded) ### iframe messages: site -> admin + Expand All + + @@ -96,7 +99,6 @@ Similar to real site but live rendered (SSR) and optionally with invisible block + - OpenLink: user clicked an external link and admin should ask the user if it should be opened in a new tab - SitePreviewLocation: user navigated in the page and the url changed, admin should update the current url @@ -99,20 +99,37 @@ Admin opens I-Frame with {previewSiteUrl}/api/preview to enter Next.js Preview M SitePreview: state from Url (get params): path, device, showOnlyVisible - has controls for managing path, device, showOnlyVisible - handles messages coming from iframe (OpenLink, SitePreviewLocation) + - appends authProvider to iframeUrl - handles incoming messages (with useSitePreviewIFrameBridge) IFrameViewer[common] (prop drilling: device) - does scale the iframe according to device (+the device around the iframe) + + + + + + + + Expand All + + @@ -106,16 +108,11 @@ SitePreview: state from Url (get params): path, device, showOnlyVisible + - renders the actual iframe ``` ### Site: States, Contexts and Components ``` -SitePreviewProvider (only active in Preview Mode) - - messages SitePreviewLocation on location change (sends message directly using sendSitePreviewIFrameMessage helper) - - creates PreviewContext containing - - previewType: "SitePreview", - - showPreviewSkeletons: false, - Page (src/pages/[...path]].tsx) - - ExternalLinkBlock messages OpenLink (sends message directly using sendSitePreviewIFrameMessage helper) +AuthenticatedPreviewPage (src/pages/preview/[...path]].tsx) + SitePreviewPage + - checks login and registers serviceworker + SitePreviewProvider + - messages SitePreviewLocation on location change (sends message directly using sendSitePreviewIFrameMessage helper) + - creates PreviewContext containing + - previewType: "SitePreview", + - showPreviewSkeletons: false, + - pathToPreviewPath: implementation that adds baseUrl (/preview) and __preview params + - previewPathToPath: implementation that removes them + Page (src/pages/[...path]].tsx) + - ExternalLinkBlock messages OpenLink (sends message directly using sendSitePreviewIFrameMessage helper) ``` diff --git a/packages/admin/cms-admin/src/preview/site/SitePreview.tsx b/packages/admin/cms-admin/src/preview/site/SitePreview.tsx index 36d11c05fd..bc51219e0f 100644 --- a/packages/admin/cms-admin/src/preview/site/SitePreview.tsx +++ b/packages/admin/cms-admin/src/preview/site/SitePreview.tsx @@ -1,4 +1,3 @@ -import { gql, useQuery } from "@apollo/client"; import { CometColor } from "@comet/admin-icons"; import { Public, VpnLock } from "@mui/icons-material"; import { Grid, Tooltip, Typography } from "@mui/material"; @@ -13,12 +12,16 @@ import { Device } from "../common/Device"; import { DeviceToggle } from "../common/DeviceToggle"; import { IFrameViewer } from "../common/IFrameViewer"; import { VisibilityToggle } from "../common/VisibilityToggle"; +import { buildPreviewUrl } from "./buildPreviewUrl"; import { SitePrevewIFrameLocationMessage, SitePreviewIFrameMessageType } from "./iframebridge/SitePreviewIFrameMessage"; import { useSitePreviewIFrameBridge } from "./iframebridge/useSitePreviewIFrameBridge"; import { OpenLinkDialog } from "./OpenLinkDialog"; -import { GQLGetSitePreviewJwtQuery } from "./SitePreview.generated"; import { ActionsContainer, LogoWrapper, Root, SiteInformation, SiteLink, SiteLinkWrapper } from "./SitePreview.sc"; +interface SitePreviewParams { + includeInvisibleBlocks: boolean; +} + //TODO v4 remove RouteComponentProps interface Props extends RouteComponentProps { resolvePath?: (path: string, scope: ContentScopeInterface) => string; @@ -56,11 +59,21 @@ function SitePreview({ resolvePath, logo = const [showOnlyVisible, setShowOnlyVisible] = useSearchState("showOnlyVisible", (v) => !v || v === "true"); const [linkToOpen, setLinkToOpen] = React.useState(undefined); + const sitePreviewParams: SitePreviewParams = { includeInvisibleBlocks: !showOnlyVisible }; + const formattedSitePreviewParams = JSON.stringify(sitePreviewParams); const { scope } = useContentScope(); const siteConfig = useSiteConfig({ scope }); - const [initialPath, setInitialPath] = React.useState(previewPath); // prevents the iframe from reloading on every change + const [initialPageUrl, setInitialPageUrl] = React.useState(buildPreviewUrl(siteConfig.previewUrl, previewPath, formattedSitePreviewParams)); + + // update the initialPreviewUrl when previewParams changes + // the iframe is then force-rerendered with the new previewUrl + React.useEffect(() => { + // react-hooks/exhaustive-deps is disabled because the src-prop of iframe is uncontrolled + // the src-value is just the default value, the iframe keeps its own src-state (by clicking links inside the iframe) + setInitialPageUrl(buildPreviewUrl(siteConfig.previewUrl, previewPath, formattedSitePreviewParams)); + }, [formattedSitePreviewParams]); // eslint-disable-line react-hooks/exhaustive-deps const intl = useIntl(); @@ -68,11 +81,17 @@ function SitePreview({ resolvePath, logo = // we sync the location back to our admin-url, so we have it and can reload the page without loosing const handlePreviewLocationChange = React.useCallback( (message: SitePrevewIFrameLocationMessage) => { - if (previewPath !== message.data.pathname) { - setPreviewPath(message.data.pathname); + const pathPrefix = new URL(siteConfig.previewUrl).pathname; + if (message.data.pathname.search(pathPrefix) === 0) { + // this is the original-pathname of the site, we extract it and keep it in "our" url as get-param + let normalizedPathname = message.data.pathname.substr(pathPrefix.length); + if (normalizedPathname == "") normalizedPathname = "/"; + if (previewPath !== normalizedPathname) { + setPreviewPath(normalizedPathname); + } } }, - [previewPath, setPreviewPath], + [previewPath, setPreviewPath, siteConfig.previewUrl], ); const handleDeviceChange = (newDevice: Device) => { @@ -82,8 +101,6 @@ function SitePreview({ resolvePath, logo = const handleShowOnlyVisibleChange = () => { const newShowOnlyVisible = !showOnlyVisible; setShowOnlyVisible(String(newShowOnlyVisible)); - setInitialPath(previewPath); - refetch(); }; const siteLink = `${siteConfig.url}${resolvePath ? resolvePath(previewPath, scope) : previewPath}`; @@ -99,26 +116,6 @@ function SitePreview({ resolvePath, logo = } }); - const { data, error, refetch } = useQuery( - gql` - query GetSitePreviewJwt($path: String!, $previewData: PreviewData!) { - getSitePreviewJwt(path: $path, previewData: $previewData) - } - `, - { - fetchPolicy: "network-only", - variables: { - path: initialPath, - previewData: { - includeInvisible: showOnlyVisible ? false : true, - }, - }, - }, - ); - if (error) throw new Error(error.message); - if (!data) return <>; - const initialPageUrl = `${siteConfig.url}/api/preview?jwt=${data.getSitePreviewJwt}`; - return ( diff --git a/packages/admin/cms-admin/src/preview/site/buildPreviewUrl.ts b/packages/admin/cms-admin/src/preview/site/buildPreviewUrl.ts new file mode 100644 index 0000000000..15800ecd3f --- /dev/null +++ b/packages/admin/cms-admin/src/preview/site/buildPreviewUrl.ts @@ -0,0 +1,7 @@ +export function buildPreviewUrl(previewUrl: string, previewPath: string, formattedSiteState: string) { + const url = new URL(`${previewUrl}${previewPath}`); + + url.searchParams.append("__preview", `${formattedSiteState}`); + + return url.toString(); +} diff --git a/packages/admin/cms-admin/src/sitesConfig/SitesConfigContext.tsx b/packages/admin/cms-admin/src/sitesConfig/SitesConfigContext.tsx index 920b5828e1..7df7f601c8 100644 --- a/packages/admin/cms-admin/src/sitesConfig/SitesConfigContext.tsx +++ b/packages/admin/cms-admin/src/sitesConfig/SitesConfigContext.tsx @@ -8,9 +8,9 @@ export interface SiteConfig { preloginEnabled: boolean; } -export interface SiteConfigApi { - configs: Record; - resolveSiteConfigForScope: (configs: Record, scope: ContentScopeInterface) => SiteConfig; +export interface SiteConfigApi { + configs: Configs; + resolveSiteConfigForScope: (configs: Configs, scope: ContentScopeInterface) => SiteConfig; } export const SiteConfigContext = React.createContext(undefined); diff --git a/packages/admin/cms-admin/src/sitesConfig/SitesConfigProvider.tsx b/packages/admin/cms-admin/src/sitesConfig/SitesConfigProvider.tsx index ec45da7dd4..39a413e83d 100644 --- a/packages/admin/cms-admin/src/sitesConfig/SitesConfigProvider.tsx +++ b/packages/admin/cms-admin/src/sitesConfig/SitesConfigProvider.tsx @@ -2,11 +2,11 @@ import * as React from "react"; import { SiteConfigApi, SiteConfigContext } from "./SitesConfigContext"; -interface Props { +interface Props { children: React.ReactNode; - value: SiteConfigApi; + value: SiteConfigApi; } -export const SitesConfigProvider = ({ children, value }: Props): React.ReactElement => { - return {children}; -}; +export function SitesConfigProvider({ children, value }: Props) { + return }>{children}; +} diff --git a/packages/admin/cms-admin/src/sitesConfig/useSitesConfig.ts b/packages/admin/cms-admin/src/sitesConfig/useSitesConfig.ts index 4b95283686..e4205cd348 100644 --- a/packages/admin/cms-admin/src/sitesConfig/useSitesConfig.ts +++ b/packages/admin/cms-admin/src/sitesConfig/useSitesConfig.ts @@ -2,7 +2,7 @@ import * as React from "react"; import { SiteConfigApi, SiteConfigContext } from "./SitesConfigContext"; -export function useSitesConfig(): SiteConfigApi { +export function useSitesConfig(): SiteConfigApi { const context = React.useContext(SiteConfigContext); if (!context) { @@ -11,5 +11,5 @@ export function useSitesConfig(): SiteConfigApi { ); } - return context; + return context as SiteConfigApi; } diff --git a/packages/admin/cms-admin/src/userPermissions/hooks/currentUser.tsx b/packages/admin/cms-admin/src/userPermissions/hooks/currentUser.tsx index 5899480eef..eb0df6fdf2 100644 --- a/packages/admin/cms-admin/src/userPermissions/hooks/currentUser.tsx +++ b/packages/admin/cms-admin/src/userPermissions/hooks/currentUser.tsx @@ -1,12 +1,16 @@ import { gql, useQuery } from "@apollo/client"; import { Loading } from "@comet/admin"; +import isEqual from "lodash.isequal"; import React from "react"; -import { ContentScopeInterface } from "../../contentScope/Provider"; +import { ContentScopeInterface, useContentScope } from "../../contentScope/Provider"; import { GQLCurrentUserPermission } from "../../graphql.generated"; import { GQLCurrentUserQuery } from "./currentUser.generated"; -type CurrentUserContext = { currentUser: CurrentUserInterface; isAllowed: (user: CurrentUserInterface, permission: string) => boolean }; +type CurrentUserContext = { + currentUser: CurrentUserInterface; + isAllowed: (user: CurrentUserInterface, permission: string, contentScope?: ContentScopeInterface) => boolean; +}; export const CurrentUserContext = React.createContext(undefined); export interface CurrentUserInterface { @@ -14,7 +18,7 @@ export interface CurrentUserInterface { email?: string; language?: string; permissions: GQLCurrentUserPermission[]; - contentScopes: ContentScopeInterface[]; + allowedContentScopes: ContentScopeInterface[]; } export const CurrentUserProvider: React.FC<{ @@ -26,9 +30,9 @@ export const CurrentUserProvider: React.FC<{ id name email - contentScopes permissions { permission + contentScopes } } } @@ -39,12 +43,17 @@ export const CurrentUserProvider: React.FC<{ if (!data) return ; const context: CurrentUserContext = { - currentUser: data.currentUser, + currentUser: { + ...data.currentUser, + allowedContentScopes: data.currentUser.permissions.flatMap((p) => p.contentScopes), + }, isAllowed: isAllowed ?? - ((user: CurrentUserInterface, permission: string) => { + ((user: CurrentUserInterface, permission: string, contentScope?: ContentScopeInterface) => { if (user.email === undefined) return false; - return user.permissions.some((p) => p.permission === permission); + return user.permissions.some( + (p) => p.permission === permission && (!contentScope || p.contentScopes.some((cs) => isEqual(cs, contentScope))), + ); }), }; @@ -56,3 +65,10 @@ export function useCurrentUser(): CurrentUserInterface { if (!ret || !ret.currentUser) throw new Error("CurrentUser not found. Make sure CurrentUserContext exists."); return ret.currentUser; } + +export function useUserPermissionCheck(): (permission: string) => boolean { + const context = React.useContext(CurrentUserContext); + if (!context) throw new Error("CurrentUser not found. Make sure CurrentUserContext exists."); + const contentScope = useContentScope(); + return (permission: string) => context.isAllowed(context.currentUser, permission, contentScope.scope); +} diff --git a/packages/admin/cms-admin/src/userPermissions/user/permissions/ContentScopeGrid.tsx b/packages/admin/cms-admin/src/userPermissions/user/permissions/ContentScopeGrid.tsx index 2b3779dd43..f31b98d15c 100644 --- a/packages/admin/cms-admin/src/userPermissions/user/permissions/ContentScopeGrid.tsx +++ b/packages/admin/cms-admin/src/userPermissions/user/permissions/ContentScopeGrid.tsx @@ -94,8 +94,7 @@ export const ContentScopeGrid: React.FC<{ value={JSON.stringify(contentScope)} label={Object.entries(contentScope).map(([scope, value]) => ( <> - :{" "} - + {camelCaseToHumanReadable(scope)}: {camelCaseToHumanReadable(value)}
))} diff --git a/packages/admin/cms-admin/src/userPermissions/user/permissions/OverrideContentScopesDialog.tsx b/packages/admin/cms-admin/src/userPermissions/user/permissions/OverrideContentScopesDialog.tsx new file mode 100644 index 0000000000..a45771e603 --- /dev/null +++ b/packages/admin/cms-admin/src/userPermissions/user/permissions/OverrideContentScopesDialog.tsx @@ -0,0 +1,135 @@ +import { gql, useApolloClient, useQuery } from "@apollo/client"; +import { CancelButton, Field, FinalForm, FinalFormCheckbox, FinalFormSwitch, SaveButton } from "@comet/admin"; +import { CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material"; +import React from "react"; +import { FormattedMessage } from "react-intl"; + +import { camelCaseToHumanReadable } from "../../utils/camelCaseToHumanReadable"; +import { + GQLOverrideContentScopesMutation, + GQLOverrideContentScopesMutationVariables, + GQLPermissionContentScopesQuery, + GQLPermissionContentScopesQueryVariables, + namedOperations, +} from "./OverrideContentScopesDialog.generated"; + +interface FormSubmitData { + overrideContentScopes: boolean; + contentScopes: string[]; +} +interface FormProps { + permissionId: string; + userId: string; + handleDialogClose: () => void; +} +type ContentScope = { + [key: string]: string; +}; + +export const OverrideContentScopesDialog: React.FC = ({ permissionId, userId, handleDialogClose }) => { + const client = useApolloClient(); + + const submit = async (data: FormSubmitData) => { + await client.mutate({ + mutation: gql` + mutation OverrideContentScopes($input: UserPermissionOverrideContentScopesInput!) { + userPermissionsUpdateOverrideContentScopes(input: $input) { + id + } + } + `, + variables: { + input: { + permissionId, + overrideContentScopes: data.overrideContentScopes, + contentScopes: data.contentScopes.map((contentScope) => JSON.parse(contentScope)), + }, + }, + refetchQueries: [namedOperations.Query.PermissionContentScopes, "Permissions"], + }); + handleDialogClose(); + }; + + const { data, error } = useQuery( + gql` + query PermissionContentScopes($permissionId: ID!, $userId: String) { + availableContentScopes: userPermissionsAvailableContentScopes + permission: userPermissionsPermission(id: $permissionId, userId: $userId) { + source + overrideContentScopes + contentScopes + } + } + `, + { + variables: { permissionId, userId }, + }, + ); + + if (error) { + throw new Error(error.message); + } + + if (!data) { + return ; + } + + const initialValues: FormSubmitData = { + overrideContentScopes: data.permission.overrideContentScopes, + contentScopes: data.permission.contentScopes.map((v) => JSON.stringify(v)), + }; + const disabled = data && data.permission.source === "BY_RULE"; + + return ( + + + mode="edit" + onSubmit={submit} + initialValues={initialValues} + render={({ values }) => ( + <> + + + + + + } + component={FinalFormSwitch} + type="checkbox" + disabled={disabled} + /> + {values.overrideContentScopes && + data.availableContentScopes.map((contentScope: ContentScope) => ( + ( + <> + {camelCaseToHumanReadable(scope)}: {camelCaseToHumanReadable(value)} +
+ + ))} + /> + ))} +
+ + handleDialogClose()}> + + + {!disabled && } + + + )} + /> +
+ ); +}; diff --git a/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionDialog.tsx b/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionDialog.tsx index 7586e5971d..1417e5f9a6 100644 --- a/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionDialog.tsx +++ b/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionDialog.tsx @@ -129,9 +129,7 @@ export const PermissionDialog: React.FC = ({ userId, permissionId, ha name="permission" component={FinalFormSelect} options={availablePermissionsData.availablePermissions} - getOptionLabel={(permission: string) => ( - - )} + getOptionLabel={(permission: string) => camelCaseToHumanReadable(permission)} disabled={disabled} label={} /> diff --git a/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionGrid.tsx b/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionGrid.tsx index 5c35f93d49..5cc8bafdfa 100644 --- a/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionGrid.tsx +++ b/packages/admin/cms-admin/src/userPermissions/user/permissions/PermissionGrid.tsx @@ -9,6 +9,7 @@ import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { camelCaseToHumanReadable } from "../../utils/camelCaseToHumanReadable"; +import { OverrideContentScopesDialog } from "./OverrideContentScopesDialog"; import { PermissionDialog } from "./PermissionDialog"; import { GQLPermissionForGridFragment, GQLPermissionsQuery, GQLPermissionsQueryVariables, namedOperations } from "./PermissionGrid.generated"; @@ -17,6 +18,7 @@ export const PermissionGrid: React.FC<{ }> = ({ userId }) => { const intl = useIntl(); const [permissionId, setPermissionId] = React.useState(null); + const [overrideContentScopesId, setOverrideContentScopesId] = React.useState(null); const { data, loading, error } = useQuery( gql` @@ -34,6 +36,7 @@ export const PermissionGrid: React.FC<{ reason requestedBy approvedBy + overrideContentScopes } `, { @@ -49,13 +52,7 @@ export const PermissionGrid: React.FC<{ flex: 1, pinnable: false, headerName: intl.formatMessage({ id: "comet.userPermissions.permission", defaultMessage: "Permission" }), - renderCell: ({ row }) => ( - <> - - - - - ), + renderCell: ({ row }) => {camelCaseToHumanReadable(row.permission)}, }, { field: "source", @@ -100,6 +97,20 @@ export const PermissionGrid: React.FC<{
), }, + { + field: "overrideContentScopes", + headerName: "", + width: 175, + sortable: false, + pinnable: false, + filterable: false, + renderCell: ({ row }) => + (row.source === "MANUAL" || row.overrideContentScopes) && ( + + ), + }, { field: "edit", width: 60, @@ -176,6 +187,13 @@ export const PermissionGrid: React.FC<{ ), }} /> + {overrideContentScopesId && ( + setOverrideContentScopesId(null)} + /> + )} {permissionId && setPermissionId(null)} />} ); diff --git a/packages/api/blocks-api/CHANGELOG.md b/packages/api/blocks-api/CHANGELOG.md index cfbcf6ceb8..6a5ebc84ba 100644 --- a/packages/api/blocks-api/CHANGELOG.md +++ b/packages/api/blocks-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @comet/blocks-api +## 5.6.0 + +### Minor Changes + +- fd10b801: Add support for a custom block name and migrations to `createRichTextBlock` + +## 5.5.0 + +## 5.4.0 + ## 5.3.0 ### Minor Changes diff --git a/packages/api/blocks-api/package.json b/packages/api/blocks-api/package.json index ad8ef789a2..41d154a313 100644 --- a/packages/api/blocks-api/package.json +++ b/packages/api/blocks-api/package.json @@ -1,6 +1,6 @@ { "name": "@comet/blocks-api", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -29,7 +29,7 @@ "rimraf": "^3.0.0" }, "devDependencies": { - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/eslint-config": "workspace:^5.6.0", "@nestjs/common": "^9.0.0", "@types/draft-js": "^0.11.10", "@types/jest": "^29.5.0", diff --git a/packages/api/blocks-api/src/blocks/createRichTextBlock.ts b/packages/api/blocks-api/src/blocks/createRichTextBlock.ts index 2d60c742eb..3dd8a55cc7 100644 --- a/packages/api/blocks-api/src/blocks/createRichTextBlock.ts +++ b/packages/api/blocks-api/src/blocks/createRichTextBlock.ts @@ -13,10 +13,10 @@ import { BlockInputFactory, BlockInputInterface, ExtractBlockInput, - MigrateOptions, registerBlock, } from "./block"; import { AnnotationBlockMeta, BlockField } from "./decorators/field"; +import { NameOrOptions } from "./factories/types"; import { strictBlockDataFactoryDecorator } from "./helpers/strictBlockDataFactoryDecorator"; import { strictBlockInputFactoryDecorator } from "./helpers/strictBlockInputFactoryDecorator"; @@ -67,17 +67,14 @@ export interface RichTextBlockInputInterface; } -export function createRichTextBlock({ - link: LinkBlock, - indexSearchText = true, -}: CreateRichTextBlockOptions): Block>> { - const BLOCK_NAME = "RichText"; - const MIGRATE: MigrateOptions = { - migrations: [], - version: 0, - }; // Placeholder for future migrations - - @BlockDataMigrationVersion(MIGRATE.version) +export function createRichTextBlock( + { link: LinkBlock, indexSearchText = true }: CreateRichTextBlockOptions, + nameOrOptions: NameOrOptions = "RichText", +): Block>> { + const blockName = typeof nameOrOptions === "string" ? nameOrOptions : nameOrOptions.name; + const migrate = typeof nameOrOptions !== "string" && nameOrOptions.migrate ? nameOrOptions.migrate : { migrations: [], version: 0 }; + + @BlockDataMigrationVersion(migrate.version) class RichTextBlockData extends BlockData { @BlockField({ type: "json" }) draftContent: RawDraftContentState; @@ -174,8 +171,8 @@ export function createRichTextBlock({ // Decorate BlockDataFactory let decorateBlockDataFactory = blockDataFactory; - if (MIGRATE.migrations) { - const blockDataFactoryDecorator1 = createAppliedMigrationsBlockDataFactoryDecorator(MIGRATE.migrations, BLOCK_NAME); + if (migrate.migrations) { + const blockDataFactoryDecorator1 = createAppliedMigrationsBlockDataFactoryDecorator(migrate.migrations, blockName); decorateBlockDataFactory = blockDataFactoryDecorator1(decorateBlockDataFactory); } decorateBlockDataFactory = strictBlockDataFactoryDecorator(decorateBlockDataFactory); @@ -184,7 +181,7 @@ export function createRichTextBlock({ const decorateBlockInputFactory = strictBlockInputFactoryDecorator(blockInputFactory); const RichTextBlock: Block>> = { - name: BLOCK_NAME, + name: blockName, blockDataFactory: decorateBlockDataFactory, blockInputFactory: decorateBlockInputFactory, blockMeta: new AnnotationBlockMeta(RichTextBlockData), diff --git a/packages/api/cms-api/CHANGELOG.md b/packages/api/cms-api/CHANGELOG.md index a13ac9eb12..9882319b2d 100644 --- a/packages/api/cms-api/CHANGELOG.md +++ b/packages/api/cms-api/CHANGELOG.md @@ -1,5 +1,86 @@ # @comet/cms-api +## 5.6.0 + +### Patch Changes + +- Updated dependencies [fd10b801] + - @comet/blocks-api@5.6.0 + +## 5.5.0 + +### Minor Changes + +- bb2c76d8: Deprecate `FileUploadInterface` interface + + Use `FileUploadInput` instead. + +- bb2c76d8: Deprecate `download` helper + + The helper is primarily used to create a `FileUploadInput` (previously `FileUploadInterface`) input for `FilesService#upload` while creating fixtures. + However, the name of the helper is too generic to be part of the package's public API. + Instead, use the newly added `FileUploadService#createFileUploadInputFromUrl`. + + **Example:** + + ```ts + @Injectable() + class ImageFixtureService { + constructor(private readonly filesService: FilesService, private readonly fileUploadService: FileUploadService) {} + + async generateImage(url: string): Promise { + const upload = await this.fileUploadService.createFileUploadInputFromUrl(url); + return this.filesService.upload(upload, {}); + } + } + ``` + +### Patch Changes + +- @comet/blocks-api@5.5.0 + +## 5.4.0 + +### Minor Changes + +- e146d8bb: Support the import of files from external DAMs + + To connect an external DAM, implement a component with the necessary logic (asset picker, upload functionality, ...). Pass this component to the `DamPage` via the `additionalToolbarItems` prop. + + ```tsx + } + /> + ``` + + You can find an [example](demo/admin/src/dam/ImportFromUnsplash.tsx) in the demo project. + +- 27bf643b: Add `PublicUploadsService` to public API + + The service can be used to programmatically create public uploads, such as when creating fixtures. + +- df5c959c: Remove license types `MICRO` and `SUBSCRIPTION` + + The `LicenseType` enum no longer contains the values `MICRO` and `SUBSCRIPTION`. The database migration will automatically update all licenses of type `MICRO` or `SUBSCRIPTION` to `RIGHTS_MANAGED`. + +### Patch Changes + +- 60f5208e: Fix encoding of special characters in names of uploaded files + + For example: + + Previously: + + - `€.jpg` -> `a.jpg` + - `ä.jpg` -> `ai.jpg` + + Now: + + - `€.jpg` -> `euro.jpg` + - `ä.jpg` -> `ae.jpg` + - @comet/blocks-api@5.4.0 + ## 5.3.0 ### Minor Changes diff --git a/packages/api/cms-api/generate-schema.ts b/packages/api/cms-api/generate-schema.ts index 4f2ac67550..638039767d 100644 --- a/packages/api/cms-api/generate-schema.ts +++ b/packages/api/cms-api/generate-schema.ts @@ -3,11 +3,9 @@ import { NestFactory } from "@nestjs/core"; import { Field, GraphQLSchemaBuilderModule, GraphQLSchemaFactory, ObjectType } from "@nestjs/graphql"; import { writeFile } from "fs/promises"; import { printSchema } from "graphql"; -import { GraphQLJSONObject } from "graphql-type-json"; import { BuildsResolver, - ContentScope, createAuthResolver, createPageTreeResolver, createRedirectsResolver, @@ -29,7 +27,6 @@ import { createFolderEntity } from "./src/dam/files/entities/folder.entity"; import { FileLicensesResolver } from "./src/dam/files/file-licenses.resolver"; import { createFilesResolver } from "./src/dam/files/files.resolver"; import { createFoldersResolver } from "./src/dam/files/folders.resolver"; -import { SitePreviewResolver } from "./src/page-tree/site-preview.resolver"; import { RedirectInputFactory } from "./src/redirects/dto/redirect-input.factory"; import { RedirectEntityFactory } from "./src/redirects/entities/redirect-entity.factory"; import { CurrentUserPermission } from "./src/user-permissions/dto/current-user"; @@ -61,8 +58,6 @@ class CurrentUser implements CurrentUserInterface { email: string; @Field() language: string; - @Field(() => [GraphQLJSONObject]) - contentScopes: ContentScope[]; @Field(() => [CurrentUserPermission]) permissions: CurrentUserPermission[]; } @@ -112,7 +107,6 @@ async function generateSchema(): Promise { RedirectsDependenciesResolver, PageTreeDependentsResolver, FileDependentsResolver, - SitePreviewResolver, UserResolver, UserPermissionResolver, UserContentScopesResolver, diff --git a/packages/api/cms-api/package.json b/packages/api/cms-api/package.json index caa170461a..add5931515 100644 --- a/packages/api/cms-api/package.json +++ b/packages/api/cms-api/package.json @@ -1,6 +1,6 @@ { "name": "@comet/cms-api", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -32,7 +32,7 @@ "test:watch": "jest --watch" }, "dependencies": { - "@comet/blocks-api": "workspace:^5.3.0", + "@comet/blocks-api": "workspace:^5.6.0", "@hapi/accept": "^5.0.2", "@nestjs/jwt": "^9.0.0", "@nestjs/mapped-types": "^1.2.2", @@ -77,7 +77,7 @@ "@aws-sdk/client-s3": "^3.47.0", "@aws-sdk/types": "^3.47.0", "@azure/storage-blob": "^12.0.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/eslint-config": "workspace:^5.6.0", "@kubernetes/client-node": "^0.18.1", "@mikro-orm/cli": "^5.7.1", "@mikro-orm/core": "^5.7.1", diff --git a/packages/api/cms-api/schema.gql b/packages/api/cms-api/schema.gql index e92717360b..29cc6098ac 100644 --- a/packages/api/cms-api/schema.gql +++ b/packages/api/cms-api/schema.gql @@ -63,8 +63,6 @@ type DamFileLicense { enum LicenseType { ROYALTY_FREE RIGHTS_MANAGED - SUBSCRIPTION - MICRO } """ @@ -144,6 +142,7 @@ type FilenameResponse { type CurrentUserPermission { permission: String! + contentScopes: [JSONObject!]! } type User { @@ -162,6 +161,8 @@ type UserPermission { reason: String requestedBy: String approvedBy: String + overrideContentScopes: Boolean! + contentScopes: [JSONObject!]! } enum UserPermissionSource { @@ -223,7 +224,6 @@ type CurrentUser { name: String! email: String! language: String! - contentScopes: [JSONObject!]! permissions: [CurrentUserPermission!]! } @@ -293,6 +293,8 @@ type DamFile { license: DamFileLicense createdAt: DateTime! updatedAt: DateTime! + importSourceId: String + importSourceType: String fileUrl: String! duplicates: [DamFile!]! damPath: String! @@ -353,7 +355,6 @@ type Query { kubernetesCronJob(name: String!): KubernetesCronJob! kubernetesJobs(cronJobName: String!): [KubernetesJob!]! currentUser: CurrentUser! - getSitePreviewJwt(path: String!, previewData: PreviewData!): String! userPermissionsUserById(id: String!): User! userPermissionsUsers(offset: Int! = 0, limit: Int! = 25, search: String, filter: UserFilter, sort: [UserSort!]): PaginatedUserList! userPermissionsPermissionList(userId: String!): [UserPermission!]! @@ -470,10 +471,6 @@ enum SlugAvailability { Reserved } -input PreviewData { - includeInvisible: Boolean! -} - input UserFilter { name: StringFilter email: StringFilter @@ -527,7 +524,8 @@ type Mutation { userPermissionsCreatePermission(userId: String!, input: UserPermissionInput!): UserPermission! userPermissionsUpdatePermission(id: String!, input: UserPermissionInput!): UserPermission! userPermissionsDeletePermission(id: ID!): Boolean! - userPermissionsUpdateContentScopes(userId: String!, input: UserContentScopesInput!): [JSONObject!]! + userPermissionsUpdateOverrideContentScopes(input: UserPermissionOverrideContentScopesInput!): UserPermission! + userPermissionsUpdateContentScopes(userId: String!, input: UserContentScopesInput!): Boolean! } input CreateBuildsInput { @@ -627,6 +625,12 @@ input UserPermissionInput { approvedBy: String } +input UserPermissionOverrideContentScopesInput { + permissionId: ID! + overrideContentScopes: Boolean! + contentScopes: [JSONObject!]! = [] +} + input UserContentScopesInput { contentScopes: [JSONObject!]! = [] } diff --git a/packages/api/cms-api/src/auth/current-user/current-user.ts b/packages/api/cms-api/src/auth/current-user/current-user.ts index f48d13e44c..64db83a0a7 100644 --- a/packages/api/cms-api/src/auth/current-user/current-user.ts +++ b/packages/api/cms-api/src/auth/current-user/current-user.ts @@ -7,6 +7,6 @@ export interface CurrentUserInterface { language: string; permissions?: { permission: string; + contentScopes: ContentScope[]; }[]; - contentScopes?: ContentScope[]; } diff --git a/packages/api/cms-api/src/blocks/createSeoBlock.ts b/packages/api/cms-api/src/blocks/createSeoBlock.ts index 597a3d5eec..5c9acf1c0b 100644 --- a/packages/api/cms-api/src/blocks/createSeoBlock.ts +++ b/packages/api/cms-api/src/blocks/createSeoBlock.ts @@ -51,7 +51,6 @@ interface CreateSeoBlockOptions { image?: ImageBlock; } -// Block-factories need the their BlockInputInterface to be public interface SeoBlockInputInterface extends SimpleBlockInputInterface { htmlTitle?: string; metaDescription?: string; diff --git a/packages/api/cms-api/src/blocks/createTextImageBlock.ts b/packages/api/cms-api/src/blocks/createTextImageBlock.ts index 448338b59f..881a1b50a7 100644 --- a/packages/api/cms-api/src/blocks/createTextImageBlock.ts +++ b/packages/api/cms-api/src/blocks/createTextImageBlock.ts @@ -28,7 +28,6 @@ interface CreateTextImageBlockOptions extends SimpleBlockInputInterface { text: TextBlockInput; diff --git a/packages/api/cms-api/src/builds/build-templates.service.ts b/packages/api/cms-api/src/builds/build-templates.service.ts index 28e23be747..153e0a657e 100644 --- a/packages/api/cms-api/src/builds/build-templates.service.ts +++ b/packages/api/cms-api/src/builds/build-templates.service.ts @@ -17,7 +17,7 @@ export class BuildTemplatesService { async getAllowedBuilderCronJobs(user: CurrentUserInterface): Promise { return (await this.getAllBuilderCronJobs()).filter((cronJob) => { - return this.accessControlService.isAllowedContentScope(user, this.kubernetesService.getContentScope(cronJob) ?? {}); + return this.accessControlService.isAllowed(user, "builds", this.kubernetesService.getContentScope(cronJob) ?? {}); }); } diff --git a/packages/api/cms-api/src/builds/builds.resolver.ts b/packages/api/cms-api/src/builds/builds.resolver.ts index 87512cdc9f..04431d8eed 100644 --- a/packages/api/cms-api/src/builds/builds.resolver.ts +++ b/packages/api/cms-api/src/builds/builds.resolver.ts @@ -37,7 +37,7 @@ export class BuildsResolver { throw new Error("Triggering build from different instance is not allowed"); } - if (!this.accessControlService.isAllowedContentScope(user, this.kubernetesService.getContentScope(cronJob) ?? {})) { + if (!this.accessControlService.isAllowed(user, "builds", this.kubernetesService.getContentScope(cronJob) ?? {})) { throw new Error("Triggering build not allowed"); } diff --git a/packages/api/cms-api/src/builds/builds.service.ts b/packages/api/cms-api/src/builds/builds.service.ts index c225df3c5e..127d7e0fb4 100644 --- a/packages/api/cms-api/src/builds/builds.service.ts +++ b/packages/api/cms-api/src/builds/builds.service.ts @@ -32,7 +32,7 @@ export class BuildsService { private async getAllowedBuildJobs(user: CurrentUserInterface): Promise { const allJobs = await this.kubernetesService.getAllJobs(`${BUILDER_LABEL} = true, ${INSTANCE_LABEL} = ${this.kubernetesService.helmRelease}`); return allJobs.filter((job) => { - return this.accessControlService.isAllowedContentScope(user, this.kubernetesService.getContentScope(job) ?? {}); + return this.accessControlService.isAllowed(user, "builds", this.kubernetesService.getContentScope(job) ?? {}); }); } diff --git a/packages/api/cms-api/src/cron-jobs/cron-jobs.resolver.ts b/packages/api/cms-api/src/cron-jobs/cron-jobs.resolver.ts index d4dcf53f76..14ba4a80cf 100644 --- a/packages/api/cms-api/src/cron-jobs/cron-jobs.resolver.ts +++ b/packages/api/cms-api/src/cron-jobs/cron-jobs.resolver.ts @@ -40,7 +40,7 @@ export class CronJobsResolver { .filter((cronJob) => { const contentScope = this.kubernetesService.getContentScope(cronJob); if (contentScope) { - return this.accessControlService.isAllowedContentScope(user, contentScope); + return this.accessControlService.isAllowed(user, "builds", contentScope); } return true; @@ -56,7 +56,7 @@ export class CronJobsResolver { const cronJob = await this.kubernetesService.getCronJob(name); const contentScope = this.kubernetesService.getContentScope(cronJob); - if (contentScope && !this.accessControlService.isAllowedContentScope(user, contentScope)) { + if (contentScope && !this.accessControlService.isAllowed(user, "builds", contentScope)) { throw new Error("Access denied"); } @@ -68,7 +68,7 @@ export class CronJobsResolver { async triggerKubernetesCronJob(@Args("name") name: string, @GetCurrentUser() user: CurrentUserInterface): Promise { const cronJob = await this.kubernetesService.getCronJob(name); const contentScope = this.kubernetesService.getContentScope(cronJob); - if (contentScope && !this.accessControlService.isAllowedContentScope(user, contentScope)) { + if (contentScope && !this.accessControlService.isAllowed(user, "builds", contentScope)) { throw new Error("Access denied"); } diff --git a/packages/api/cms-api/src/cron-jobs/jobs.resolver.ts b/packages/api/cms-api/src/cron-jobs/jobs.resolver.ts index 486a4995ef..b11362b397 100644 --- a/packages/api/cms-api/src/cron-jobs/jobs.resolver.ts +++ b/packages/api/cms-api/src/cron-jobs/jobs.resolver.ts @@ -27,7 +27,7 @@ export class JobsResolver { const cronJob = await this.kubernetesService.getCronJob(cronJobName); const contentScope = this.kubernetesService.getContentScope(cronJob); - if (contentScope && !this.accessControlService.isAllowedContentScope(user, contentScope)) { + if (contentScope && !this.accessControlService.isAllowed(user, "cronJobs", contentScope)) { throw new Error("Access denied"); } diff --git a/packages/api/cms-api/src/dam/dam.module.ts b/packages/api/cms-api/src/dam/dam.module.ts index 78a2efa46c..236706d30a 100644 --- a/packages/api/cms-api/src/dam/dam.module.ts +++ b/packages/api/cms-api/src/dam/dam.module.ts @@ -13,6 +13,7 @@ import { DamFileImage } from "./files/entities/file-image.entity"; import { createFolderEntity, FolderInterface } from "./files/entities/folder.entity"; import { FileImagesResolver } from "./files/file-image.resolver"; import { FileLicensesResolver } from "./files/file-licenses.resolver"; +import { FileUploadService } from "./files/file-upload.service"; import { FileValidationService } from "./files/file-validation.service"; import { createFilesController } from "./files/files.controller"; import { createFilesResolver } from "./files/files.resolver"; @@ -117,9 +118,10 @@ export class DamModule { FileImagesResolver, CalculateDominantImageColor, FileValidationService, + FileUploadService, ], controllers: [createFilesController({ Scope }), ImagesController], - exports: [ImgproxyService, FilesService, FoldersService, ImagesService, ScaledImagesCacheService, damConfigProvider], + exports: [ImgproxyService, FilesService, FoldersService, ImagesService, ScaledImagesCacheService, damConfigProvider, FileUploadService], }; } } diff --git a/packages/api/cms-api/src/dam/files/dam-upload-file.interceptor.ts b/packages/api/cms-api/src/dam/files/dam-upload-file.interceptor.ts index 01e8af6bc1..7648eee149 100644 --- a/packages/api/cms-api/src/dam/files/dam-upload-file.interceptor.ts +++ b/packages/api/cms-api/src/dam/files/dam-upload-file.interceptor.ts @@ -33,6 +33,8 @@ export function DamUploadFileInterceptor(fieldName: string): Type; + +/** + * @deprecated use `FileUploadInput` instead + */ +type FileUploadInterface = FileUploadInput; + +export { FileUploadInput, FileUploadInterface }; diff --git a/packages/api/cms-api/src/dam/files/dto/file-upload.interface.ts b/packages/api/cms-api/src/dam/files/dto/file-upload.interface.ts deleted file mode 100644 index f255574a42..0000000000 --- a/packages/api/cms-api/src/dam/files/dto/file-upload.interface.ts +++ /dev/null @@ -1 +0,0 @@ -export type FileUploadInterface = Omit; diff --git a/packages/api/cms-api/src/dam/files/dto/file.body.ts b/packages/api/cms-api/src/dam/files/dto/file.body.ts index 8fa72530d0..f9698f5d27 100644 --- a/packages/api/cms-api/src/dam/files/dto/file.body.ts +++ b/packages/api/cms-api/src/dam/files/dto/file.body.ts @@ -1,6 +1,6 @@ import { Type } from "@nestjs/common"; import { plainToInstance, Transform, Type as ClassTransformerType } from "class-transformer"; -import { IsOptional, IsString, ValidateNested } from "class-validator"; +import { IsNotEmpty, IsOptional, IsString, ValidateIf, ValidateNested } from "class-validator"; import { ImageCropAreaInput } from "../../images/dto/image-crop-area.input"; import { DamScopeInterface } from "../../types"; @@ -13,6 +13,8 @@ export interface UploadFileBodyInterface { altText?: string; license?: LicenseInput; imageCropArea?: ImageCropAreaInput; + importSourceId?: string; + importSourceType?: string; } export function createUploadFileBody({ Scope }: { Scope: Type }): Type { @@ -44,6 +46,16 @@ export function createUploadFileBody({ Scope }: { Scope: Type @ClassTransformerType(() => ImageCropAreaInput) @ValidateNested() imageCropArea?: ImageCropAreaInput; + + @IsString() + @IsNotEmpty() + @ValidateIf((input) => input.importSourceType !== undefined) + importSourceId?: string; + + @IsString() + @IsNotEmpty() + @ValidateIf((input) => input.importSourceId !== undefined) + importSourceType?: string; } return UploadFileBody; diff --git a/packages/api/cms-api/src/dam/files/dto/file.input.ts b/packages/api/cms-api/src/dam/files/dto/file.input.ts index ce19bd9a19..f393d54f91 100644 --- a/packages/api/cms-api/src/dam/files/dto/file.input.ts +++ b/packages/api/cms-api/src/dam/files/dto/file.input.ts @@ -1,6 +1,19 @@ import { Field, ID, InputType } from "@nestjs/graphql"; import { Type } from "class-transformer"; -import { IsDate, IsEnum, IsHash, IsInt, IsMimeType, IsNotEmpty, IsObject, IsOptional, IsString, IsUUID, ValidateNested } from "class-validator"; +import { + IsDate, + IsEnum, + IsHash, + IsInt, + IsMimeType, + IsNotEmpty, + IsObject, + IsOptional, + IsString, + IsUUID, + ValidateIf, + ValidateNested, +} from "class-validator"; import { IsNullable } from "../../../common/validators/is-nullable"; import { IsUndefinable } from "../../../common/validators/is-undefinable"; @@ -89,6 +102,16 @@ export class CreateFileInput { // TODO is this validation even used? @IsObject() scope?: DamScopeInterface; + + @IsString() + @IsNotEmpty() + @ValidateIf((input) => input.importSourceType !== undefined) + importSourceId?: string; + + @IsString() + @IsNotEmpty() + @ValidateIf((input) => input.importSourceId !== undefined) + importSourceType?: string; } @InputType({ isAbstract: true }) diff --git a/packages/api/cms-api/src/dam/files/entities/file.entity.ts b/packages/api/cms-api/src/dam/files/entities/file.entity.ts index 6966a00cbf..10cbb47449 100644 --- a/packages/api/cms-api/src/dam/files/entities/file.entity.ts +++ b/packages/api/cms-api/src/dam/files/entities/file.entity.ts @@ -39,6 +39,8 @@ export interface FileInterface extends BaseEntity { createdAt: Date; updatedAt: Date; scope?: DamScopeInterface; + importSourceId?: string; + importSourceType?: string; } export function createFileEntity({ Scope, Folder }: { Scope?: Type; Folder: Type }): Type { @@ -103,9 +105,8 @@ export function createFileEntity({ Scope, Folder }: { Scope?: Type DamFileImage, { nullable: true }) @OneToOne({ @@ -135,6 +136,14 @@ export function createFileEntity({ Scope, Folder }: { Scope?: Type @Field(() => [ID]) mpath: string[]; - @Property({ columnType: "boolean", default: false }) + @Property({ columnType: "boolean" }) @Field() - archived: boolean; + archived: boolean = false; - @Property({ columnType: "boolean", default: false }) + @Property({ columnType: "boolean" }) @Field() isInboxFromOtherScope: boolean = false; diff --git a/packages/api/cms-api/src/dam/files/entities/license.embeddable.ts b/packages/api/cms-api/src/dam/files/entities/license.embeddable.ts index f994971b39..c7fe60df51 100644 --- a/packages/api/cms-api/src/dam/files/entities/license.embeddable.ts +++ b/packages/api/cms-api/src/dam/files/entities/license.embeddable.ts @@ -4,8 +4,6 @@ import { Field, ObjectType, registerEnumType } from "@nestjs/graphql"; export enum LicenseType { ROYALTY_FREE = "ROYALTY_FREE", RIGHTS_MANAGED = "RIGHTS_MANAGED", - SUBSCRIPTION = "SUBSCRIPTION", - MICRO = "MICRO", } registerEnumType(LicenseType, { name: "LicenseType" }); diff --git a/packages/api/cms-api/src/dam/files/file-upload.service.ts b/packages/api/cms-api/src/dam/files/file-upload.service.ts new file mode 100644 index 0000000000..f1021b8db3 --- /dev/null +++ b/packages/api/cms-api/src/dam/files/file-upload.service.ts @@ -0,0 +1,51 @@ +import { Injectable } from "@nestjs/common"; +import FileType from "file-type"; +import fs from "fs"; +import got from "got"; +import os from "os"; +import stream from "stream"; +import { promisify } from "util"; +import { v4 as uuid } from "uuid"; + +import { FileUploadInput } from "./dto/file-upload.input"; +import { FilesService } from "./files.service"; + +const pipeline = promisify(stream.pipeline); + +@Injectable() +class FileUploadService { + async createFileUploadInputFromUrl(url: string): Promise { + const tempDir = fs.mkdtempSync(`${os.tmpdir()}/download`); + const fakeName = uuid(); + const tempFile = `${tempDir}/${fakeName}`; + + if (url.substring(0, 4) === "http") { + await pipeline(got.stream(url), fs.createWriteStream(tempFile)); + //TODO when downloading the file from http use mime type from reponse header + } else { + fs.copyFileSync(url, tempFile); + } + + const fileType = await FileType.fromFile(tempFile); + const stats = fs.statSync(tempFile); // TODO don't use sync + const filename = url.substring(url.lastIndexOf("/") + 1); + + return { + fieldname: FilesService.UPLOAD_FIELD, + originalname: `${filename}.${fileType?.ext}`, + encoding: "utf8", + mimetype: fileType?.mime as string, + size: stats.size, + destination: tempDir, + filename: fakeName, + path: tempFile, + }; + } +} + +/** + * @deprecated use FileUploadService#createFileUploadInputFromUrl instead + */ +const download = FileUploadService.prototype.createFileUploadInputFromUrl; + +export { download, FileUploadService }; diff --git a/packages/api/cms-api/src/dam/files/file-validation.service.ts b/packages/api/cms-api/src/dam/files/file-validation.service.ts index ac0cc54cbb..acb07c393e 100644 --- a/packages/api/cms-api/src/dam/files/file-validation.service.ts +++ b/packages/api/cms-api/src/dam/files/file-validation.service.ts @@ -1,13 +1,13 @@ import { readFile } from "fs/promises"; import * as mimedb from "mime-db"; -import { FileUploadInterface } from "./dto/file-upload.interface"; +import { FileUploadInput } from "./dto/file-upload.input"; import { svgContainsJavaScript } from "./files.utils"; export class FileValidationService { constructor(public config: { maxFileSize: number; acceptedMimeTypes: string[] }) {} - async validateFile(file: FileUploadInterface): Promise { + async validateFile(file: FileUploadInput): Promise { let error = await this.validateFileMetadata(file); if (error === undefined) { @@ -17,7 +17,7 @@ export class FileValidationService { return error; } - async validateFileMetadata(file: FileUploadInterface): Promise { + async validateFileMetadata(file: FileUploadInput): Promise { //maximum file size if (file.size > this.config.maxFileSize * 1024 * 1024) { return "File is too large"; @@ -50,7 +50,7 @@ export class FileValidationService { return undefined; } - async validateFileContents(file: FileUploadInterface): Promise { + async validateFileContents(file: FileUploadInput): Promise { if (file.mimetype === "image/svg+xml") { const fileContent = await readFile(file.path, { encoding: "utf-8" }); diff --git a/packages/api/cms-api/src/dam/files/files.controller.ts b/packages/api/cms-api/src/dam/files/files.controller.ts index 6639b5799c..12e87db69c 100644 --- a/packages/api/cms-api/src/dam/files/files.controller.ts +++ b/packages/api/cms-api/src/dam/files/files.controller.ts @@ -33,7 +33,7 @@ import { DamUploadFileInterceptor } from "./dam-upload-file.interceptor"; import { EmptyDamScope } from "./dto/empty-dam-scope"; import { createUploadFileBody, UploadFileBodyInterface } from "./dto/file.body"; import { FileParams, HashFileParams } from "./dto/file.params"; -import { FileUploadInterface } from "./dto/file-upload.interface"; +import { FileUploadInput } from "./dto/file-upload.input"; import { FileInterface } from "./entities/file.entity"; import { FilesService } from "./files.service"; import { calculatePartialRanges, createHashedPath } from "./files.utils"; @@ -63,7 +63,7 @@ export function createFilesController({ Scope: PassedScope }: { Scope?: Type { @@ -75,7 +75,7 @@ export function createFilesController({ Scope: PassedScope }: { Scope?: Type; Scope?: Type }): Type { const Scope = PassedScope ?? EmptyDamScope; @@ -55,6 +56,7 @@ export function createFilesResolver({ File, Scope: PassedScope }: { File: Type, @Inject(ACCESS_CONTROL_SERVICE) private accessControlService: AccessControlServiceInterface, @Inject(DAM_FILE_VALIDATION_SERVICE) private readonly fileValidationService: FileValidationService, + private readonly fileUploadService: FileUploadService, ) {} @Query(() => PaginatedDamFiles) @@ -97,7 +99,7 @@ export function createFilesResolver({ File, Scope: PassedScope }: { File: Type Scope, defaultValue: hasNonEmptyScope ? undefined : {} }) scope: typeof Scope, @Args("input", { type: () => UpdateFileInput }) { image: imageInput, ...input }: UpdateFileInput, ): Promise { - const file = await download(url); + const file = await this.fileUploadService.createFileUploadInputFromUrl(url); const validationResult = await this.fileValidationService.validateFile(file); if (validationResult !== undefined) { throw new CometValidationException(validationResult); @@ -122,7 +124,7 @@ export function createFilesResolver({ File, Scope: PassedScope }: { File: Type { const folder = folderId ? await this.foldersService.findOneById(folderId) : undefined; - return this.save(this.filesRepository.create({ ...data, license: { ...data.license }, folder: folder?.id })); + return this.save( + this.filesRepository.create({ + ...data, + license: { ...data.license }, + folder: folder?.id, + importSourceId: data.importSourceId, + importSourceType: data.importSourceType, + }), + ); } async updateById(id: string, data: UpdateFileInput): Promise { @@ -294,7 +302,7 @@ export class FilesService { } async upload( - file: FileUploadInterface, + file: FileUploadInput, { folderId, scope, ...assignData }: Omit & { scope?: DamScopeInterface }, ): Promise { let result: FileInterface | undefined = undefined; @@ -454,7 +462,7 @@ export class FilesService { if (!inboxFolder) { throw new Error("Specified inbox folder doesn't exist."); } - if (inboxFolder.scope && !this.accessControlService.isAllowedContentScope(user, inboxFolder.scope)) { + if (inboxFolder.scope && !this.accessControlService.isAllowed(user, "dam", inboxFolder.scope)) { throw new Error("User can't access the target scope"); } @@ -480,7 +488,7 @@ export class FilesService { const fileScopes = getUniqueFileScopes(files); const canAccessFileScopes = fileScopes.every((scope) => { - return this.accessControlService.isAllowedContentScope(user, scope); + return this.accessControlService.isAllowed(user, "dam", scope); }); if (!canAccessFileScopes) { throw new Error(`User can't access the scope of one or more files`); diff --git a/packages/api/cms-api/src/dam/files/files.utils.ts b/packages/api/cms-api/src/dam/files/files.utils.ts index ecaaed2661..26204f357e 100644 --- a/packages/api/cms-api/src/dam/files/files.utils.ts +++ b/packages/api/cms-api/src/dam/files/files.utils.ts @@ -1,53 +1,14 @@ import { XMLParser } from "fast-xml-parser"; -import FileType from "file-type"; -import fs from "fs"; import { unlink } from "fs/promises"; -import got from "got"; -import os from "os"; import { sep } from "path"; import slugify from "slugify"; -import stream from "stream"; -import { promisify } from "util"; -import { v4 as uuid } from "uuid"; -import { FileUploadInterface } from "./dto/file-upload.interface"; -import { FilesService } from "./files.service"; +import { FileUploadInput } from "./dto/file-upload.input"; export function slugifyFilename(filename: string, extension: string): string { return `${slugify(filename, { locale: "de", lower: true, strict: true })}${extension}`; } -const pipeline = promisify(stream.pipeline); - -// TODO find a better name for this function -export async function download(url: string): Promise { - const tempDir = fs.mkdtempSync(`${os.tmpdir()}/download`); - const fakeName = uuid(); - const tempFile = `${tempDir}/${fakeName}`; - - if (url.substr(0, 4) === "http") { - await pipeline(got.stream(url), fs.createWriteStream(tempFile)); - //TODO when downloading the file from http use mime type from reponse header - } else { - fs.copyFileSync(url, tempFile); - } - - const fileType = await FileType.fromFile(tempFile); - const stats = fs.statSync(tempFile); // TODO don't use sync - const filename = url.substring(url.lastIndexOf("/") + 1); - - return { - fieldname: FilesService.UPLOAD_FIELD, - originalname: `${filename}.${fileType?.ext}`, - encoding: "utf8", - mimetype: fileType?.mime as string, - size: stats.size, - destination: tempDir, - filename: fakeName, - path: tempFile, - }; -} - export const createHashedPath = (contentHash: string): string => [contentHash.substr(0, 2), contentHash.substr(2, 2), contentHash].join(sep); export const calculatePartialRanges = (size: number, range: string): { start: number; end: number; contentLength: number } => { @@ -108,13 +69,13 @@ export const svgContainsJavaScript = (svg: string) => { return recursivelyFindJSInSvg(jsonObj); }; -export const removeMulterTempFile = async (file: FileUploadInterface) => { +export const removeMulterTempFile = async (file: FileUploadInput) => { // https://github.com/expressjs/multer/blob/master/storage/disk.js#L54-L62 const path = file.path; - delete (file as Partial).destination; - delete (file as Partial).filename; - delete (file as Partial).path; + delete (file as Partial).destination; + delete (file as Partial).filename; + delete (file as Partial).path; await unlink(path); }; diff --git a/packages/api/cms-api/src/dam/images/images.controller.ts b/packages/api/cms-api/src/dam/images/images.controller.ts index 0640bffee1..0e52a67f32 100644 --- a/packages/api/cms-api/src/dam/images/images.controller.ts +++ b/packages/api/cms-api/src/dam/images/images.controller.ts @@ -71,7 +71,7 @@ export class ImagesController { throw new NotFoundException(); } - if (file.scope !== undefined && !this.accessControlService.isAllowedContentScope(user, file.scope)) { + if (file.scope !== undefined && !this.accessControlService.isAllowed(user, "dam", file.scope)) { throw new ForbiddenException(); } @@ -97,7 +97,7 @@ export class ImagesController { throw new NotFoundException(); } - if (file.scope !== undefined && !this.accessControlService.isAllowedContentScope(user, file.scope)) { + if (file.scope !== undefined && !this.accessControlService.isAllowed(user, "dam", file.scope)) { throw new ForbiddenException(); } diff --git a/packages/api/cms-api/src/index.ts b/packages/api/cms-api/src/index.ts index 361220c9bf..f009fe953f 100644 --- a/packages/api/cms-api/src/index.ts +++ b/packages/api/cms-api/src/index.ts @@ -72,14 +72,15 @@ export { DamConfig } from "./dam/dam.config"; export { DAM_CONFIG, IMGPROXY_CONFIG } from "./dam/dam.constants"; export { DamModule } from "./dam/dam.module"; export { CreateFileInput, ImageFileInput, UpdateFileInput } from "./dam/files/dto/file.input"; -export { FileUploadInterface } from "./dam/files/dto/file-upload.interface"; +export { FileUploadInterface } from "./dam/files/dto/file-upload.input"; export { CreateFolderInput, UpdateFolderInput } from "./dam/files/dto/folder.input"; export { createFileEntity, FileInterface } from "./dam/files/entities/file.entity"; export { DamFileImage } from "./dam/files/entities/file-image.entity"; export { createFolderEntity, FolderInterface } from "./dam/files/entities/folder.entity"; export { FileImagesResolver } from "./dam/files/file-image.resolver"; +export { download, FileUploadService } from "./dam/files/file-upload.service"; export { FilesService } from "./dam/files/files.service"; -export { download, slugifyFilename } from "./dam/files/files.utils"; +export { slugifyFilename } from "./dam/files/files.utils"; export { FoldersService } from "./dam/files/folders.service"; export { ImageInterface } from "./dam/images/dto/image.interface"; export { HashImageParams, ImageParams } from "./dam/images/dto/image.params"; diff --git a/packages/api/cms-api/src/mikro-orm/migrations/Migration20230808085034.ts b/packages/api/cms-api/src/mikro-orm/migrations/Migration20230808085034.ts new file mode 100644 index 0000000000..f9704c42c4 --- /dev/null +++ b/packages/api/cms-api/src/mikro-orm/migrations/Migration20230808085034.ts @@ -0,0 +1,8 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20230808085034 extends Migration { + + async up(): Promise { + this.addSql('alter table "DamFile" add column "importSourceId" text null, add column "importSourceType" text null;'); + } +} \ No newline at end of file diff --git a/packages/api/cms-api/src/mikro-orm/migrations/Migration20231204140305.ts b/packages/api/cms-api/src/mikro-orm/migrations/Migration20231204140305.ts new file mode 100644 index 0000000000..64949ad906 --- /dev/null +++ b/packages/api/cms-api/src/mikro-orm/migrations/Migration20231204140305.ts @@ -0,0 +1,25 @@ +import { Migration } from "@mikro-orm/migrations"; + +export class Migration20231204140305 extends Migration { + async up(): Promise { + this.addSql( + 'alter table "PageTreeNode" alter column "hideInMenu" drop default', + ); + this.addSql( + 'alter table "Redirect" alter column "active" drop default', + ); + this.addSql( + 'alter table "DamFile" alter column "archived" drop default', + ); + this.addSql( + 'alter table "DamFolder" alter column "archived" drop default', + ); + this.addSql( + 'alter table "DamFolder" alter column "isInboxFromOtherScope" drop default', + ); + } + + async down(): Promise { + throw new Error("No revert"); + } +} diff --git a/packages/api/cms-api/src/mikro-orm/migrations/Migration20231218092313.ts b/packages/api/cms-api/src/mikro-orm/migrations/Migration20231218092313.ts new file mode 100644 index 0000000000..1b54d31161 --- /dev/null +++ b/packages/api/cms-api/src/mikro-orm/migrations/Migration20231218092313.ts @@ -0,0 +1,16 @@ +import { Migration } from "@mikro-orm/migrations"; + +export class Migration20231218092313 extends Migration { + async up(): Promise { + this.addSql( + 'alter table "CometUserPermission" add column "overrideContentScopes" boolean not null default false, add column "contentScopes" jsonb not null default \'[]\'::jsonb;', + ); + this.addSql('alter table "CometUserPermission" alter column "overrideContentScopes" drop default'); + this.addSql('alter table "CometUserPermission" alter column "contentScopes" drop default'); + } + + async down(): Promise { + this.addSql('alter table "CometUserPermission" drop column "overrideContentScopes";'); + this.addSql('alter table "CometUserPermission" drop column "contentScopes";'); + } +} diff --git a/packages/api/cms-api/src/mikro-orm/migrations/Migration20231222090009.ts b/packages/api/cms-api/src/mikro-orm/migrations/Migration20231222090009.ts new file mode 100644 index 0000000000..7e8866b065 --- /dev/null +++ b/packages/api/cms-api/src/mikro-orm/migrations/Migration20231222090009.ts @@ -0,0 +1,16 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20231222090009 extends Migration { + + async up(): Promise { + this.addSql('alter table "DamFile" drop constraint if exists "DamFile_license_type_check";'); + this.addSql('update "DamFile" set "license_type" = \'RIGHTS_MANAGED\' where "license_type" = \'MICRO\' or "license_type" = \'SUBSCRIPTION\';') + this.addSql('alter table "DamFile" add constraint "DamFile_license_type_check" check ("license_type" in (\'ROYALTY_FREE\', \'RIGHTS_MANAGED\'));'); + } + + async down(): Promise { + this.addSql('alter table "DamFile" drop constraint if exists "DamFile_license_type_check";'); + this.addSql('alter table "DamFile" add constraint "DamFile_license_type_check" check ("license_type" in (\'ROYALTY_FREE\', \'RIGHTS_MANAGED\', \'SUBSCRIPTION\', \'MICRO\'));'); + } + +} diff --git a/packages/api/cms-api/src/mikro-orm/mikro-orm.module.ts b/packages/api/cms-api/src/mikro-orm/mikro-orm.module.ts index b211c73af2..2d77176624 100644 --- a/packages/api/cms-api/src/mikro-orm/mikro-orm.module.ts +++ b/packages/api/cms-api/src/mikro-orm/mikro-orm.module.ts @@ -16,9 +16,13 @@ import { Migration20230209111818 } from "./migrations/Migration20230209111818"; import { Migration20230302145445 } from "./migrations/Migration20230302145445"; import { Migration20230613150332 } from "./migrations/Migration20230613150332"; import { Migration20230802124224 } from "./migrations/Migration20230802124224"; +import { Migration20230808085034 } from "./migrations/Migration20230808085034"; import { Migration20230821090303 } from "./migrations/Migration20230821090303"; +import { Migration20231204140305 } from "./migrations/Migration20231204140305"; import { Migration20231206123505 } from "./migrations/Migration20231206123505"; import { Migration20231215103630 } from "./migrations/Migration20231215103630"; +import { Migration20231218092313 } from "./migrations/Migration20231218092313"; +import { Migration20231222090009 } from "./migrations/Migration20231222090009"; export const PG_UNIQUE_CONSTRAINT_VIOLATION = "23505"; @@ -72,9 +76,13 @@ export function createOrmConfig({ migrations, ...defaults }: MikroOrmNestjsOptio { name: "Migration20230209111818", class: Migration20230209111818 }, { name: "Migration20230613150332", class: Migration20230613150332 }, { name: "Migration20230802124224", class: Migration20230802124224 }, + { name: "Migration20230808085034", class: Migration20230808085034 }, { name: "Migration20230821090303", class: Migration20230821090303 }, { name: "Migration20231206123505", class: Migration20231206123505 }, { name: "Migration20231215103630", class: Migration20231215103630 }, + { name: "Migration20231222090009", class: Migration20231222090009 }, + { name: "Migration20231204140305", class: Migration20231204140305 }, + { name: "Migration20231218092313", class: Migration20231218092313 }, ...(migrations?.migrationsList || []), ].sort((migrationA, migrationB) => { if (migrationA.name < migrationB.name) { diff --git a/packages/api/cms-api/src/page-tree/entities/page-tree-node-base.entity.ts b/packages/api/cms-api/src/page-tree/entities/page-tree-node-base.entity.ts index 51162c74e2..fa7d59b356 100644 --- a/packages/api/cms-api/src/page-tree/entities/page-tree-node-base.entity.ts +++ b/packages/api/cms-api/src/page-tree/entities/page-tree-node-base.entity.ts @@ -36,9 +36,9 @@ export abstract class PageTreeNodeBase extends BaseEntity PageTreeNodeVisibility, default: PageTreeNodeVisibility.Unpublished }) + @Enum({ items: () => PageTreeNodeVisibility }) @Field(() => PageTreeNodeVisibility) - visibility: PageTreeNodeVisibility; + visibility: PageTreeNodeVisibility = PageTreeNodeVisibility.Unpublished; @Property({ columnType: "text" }) @Field() @@ -47,9 +47,9 @@ export abstract class PageTreeNodeBase extends BaseEntity; - @Property({ default: false }) + @Property() @Field() - hideInMenu: boolean; + hideInMenu: boolean = false; category: PageTreeNodeCategory; diff --git a/packages/api/cms-api/src/page-tree/page-tree-read-api.ts b/packages/api/cms-api/src/page-tree/page-tree-read-api.ts index 94cb87b043..b61db5ca65 100644 --- a/packages/api/cms-api/src/page-tree/page-tree-read-api.ts +++ b/packages/api/cms-api/src/page-tree/page-tree-read-api.ts @@ -278,7 +278,9 @@ export function createReadApi( async getNodeOrFail(id) { const node = await this.getNode(id); if (!node) { - throw new NotFoundError("foo"); + throw new NotFoundError( + `Cannot find PageTreeNode with ID ${id} and visibility ${Array.isArray(visibility) ? visibility.join(" or ") : visibility}`, + ); } return node; }, diff --git a/packages/api/cms-api/src/page-tree/page-tree.constants.ts b/packages/api/cms-api/src/page-tree/page-tree.constants.ts index 55684eeb94..2cc32955fd 100644 --- a/packages/api/cms-api/src/page-tree/page-tree.constants.ts +++ b/packages/api/cms-api/src/page-tree/page-tree.constants.ts @@ -5,5 +5,3 @@ export const PAGE_TREE_ENTITY = "PageTreeNode"; export const PAGE_TREE_CONFIG = "PageTreeConfig"; export const defaultReservedPaths = ["/admin", "/preview"]; - -export const SITE_PREVIEW_CONFIG = "SitePreviewConfig"; diff --git a/packages/api/cms-api/src/page-tree/page-tree.module.ts b/packages/api/cms-api/src/page-tree/page-tree.module.ts index 5fdf183530..2661dd5c64 100644 --- a/packages/api/cms-api/src/page-tree/page-tree.module.ts +++ b/packages/api/cms-api/src/page-tree/page-tree.module.ts @@ -10,10 +10,9 @@ import { DocumentSubscriberFactory } from "./document-subscriber"; import { PageTreeNodeBaseCreateInput, PageTreeNodeBaseUpdateInput } from "./dto/page-tree-node.input"; import { AttachedDocument } from "./entities/attached-document.entity"; import { PageTreeNodeBase } from "./entities/page-tree-node-base.entity"; -import { defaultReservedPaths, PAGE_TREE_CONFIG, PAGE_TREE_ENTITY, PAGE_TREE_REPOSITORY, SITE_PREVIEW_CONFIG } from "./page-tree.constants"; +import { defaultReservedPaths, PAGE_TREE_CONFIG, PAGE_TREE_ENTITY, PAGE_TREE_REPOSITORY } from "./page-tree.constants"; import { PageTreeService } from "./page-tree.service"; import { PageTreeReadApiService } from "./page-tree-read-api.service"; -import { SitePreviewResolver } from "./site-preview.resolver"; import type { PageTreeNodeInterface, ScopeInterface } from "./types"; import { PageExistsConstraint } from "./validators/page-exists.validator"; @@ -28,7 +27,6 @@ interface PageTreeModuleOptions { Documents: Type[]; Scope?: Type; reservedPaths?: string[]; - sitePreviewSecret: string; } @Global() @@ -86,13 +84,6 @@ export class PageTreeModule { inject: [PageTreeService], }, documentSubscriber, - { - provide: SITE_PREVIEW_CONFIG, - useValue: { - secret: options.sitePreviewSecret, - }, - }, - SitePreviewResolver, ], exports: [PageTreeService, PageTreeReadApiService, AttachedDocumentLoaderService], }; diff --git a/packages/api/cms-api/src/page-tree/site-preview.resolver.ts b/packages/api/cms-api/src/page-tree/site-preview.resolver.ts deleted file mode 100644 index c779798212..0000000000 --- a/packages/api/cms-api/src/page-tree/site-preview.resolver.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Inject } from "@nestjs/common"; -import { Args, ArgsType, Field, InputType, Query, Resolver } from "@nestjs/graphql"; -import { Type } from "class-transformer"; -import { IsBoolean, IsString, ValidateNested } from "class-validator"; -import jsonwebtoken from "jsonwebtoken"; - -import { SITE_PREVIEW_CONFIG } from "./page-tree.constants"; - -@InputType() -export class PreviewData { - @Field(() => Boolean) - @IsBoolean() - includeInvisible: boolean; -} - -@ArgsType() -class SitePreviewArgs { - @Field(() => String) - @IsString() - path: string; - - @Field(() => PreviewData) - @ValidateNested() - @Type(() => PreviewData) - previewData: PreviewData; -} - -export type SitePreviewConfig = { - secret: string; -}; - -@Resolver() -export class SitePreviewResolver { - constructor(@Inject(SITE_PREVIEW_CONFIG) private readonly config: SitePreviewConfig) {} - - @Query(() => String) - getSitePreviewJwt(@Args() args: SitePreviewArgs): string { - return jsonwebtoken.sign({ ...args }, this.config.secret, { expiresIn: 10 }); - } -} diff --git a/packages/api/cms-api/src/public-upload/public-upload-file.interceptor.ts b/packages/api/cms-api/src/public-upload/public-upload-file.interceptor.ts index b004aa0073..3d56fb1ffd 100644 --- a/packages/api/cms-api/src/public-upload/public-upload-file.interceptor.ts +++ b/packages/api/cms-api/src/public-upload/public-upload-file.interceptor.ts @@ -33,6 +33,8 @@ export function PublicUploadFileInterceptor(fieldName: string): Type { + async upload(file: PublicUploadFileUploadInterface): Promise { const contentHash = await hasha.fromFile(file.path, { algorithm: "md5" }); await this.blobStorageBackendService.upload(file, contentHash, this.config.directory); diff --git a/packages/api/cms-api/src/redirects/entities/redirect-entity.factory.ts b/packages/api/cms-api/src/redirects/entities/redirect-entity.factory.ts index 209941ec94..e15b92936b 100644 --- a/packages/api/cms-api/src/redirects/entities/redirect-entity.factory.ts +++ b/packages/api/cms-api/src/redirects/entities/redirect-entity.factory.ts @@ -60,9 +60,9 @@ export class RedirectEntityFactory { @Field({ nullable: true }) comment?: string; - @Property({ default: true }) + @Property() @Field() - active: boolean; + active: boolean = true; @Enum(() => RedirectGenerationType) @Field(() => RedirectGenerationType) diff --git a/packages/api/cms-api/src/user-permissions/access-control.service.ts b/packages/api/cms-api/src/user-permissions/access-control.service.ts index f1372af2da..5c6abf61e5 100644 --- a/packages/api/cms-api/src/user-permissions/access-control.service.ts +++ b/packages/api/cms-api/src/user-permissions/access-control.service.ts @@ -7,13 +7,11 @@ import { AccessControlServiceInterface } from "./user-permissions.types"; @Injectable() export abstract class AbstractAccessControlService implements AccessControlServiceInterface { - isAllowedContentScope(user: CurrentUserInterface, contentScope: ContentScope): boolean { - if (!user.contentScopes) return false; - return user.contentScopes.some((cs) => Object.entries(contentScope).every(([scope, value]) => cs[scope] === value)); + private checkContentScope(userContentScopes: ContentScope[], contentScope: ContentScope): boolean { + return userContentScopes.some((cs) => Object.entries(contentScope).every(([scope, value]) => cs[scope] === value)); } - - isAllowedPermission(user: CurrentUserInterface, permission: keyof Permission): boolean { + isAllowed(user: CurrentUserInterface, permission: keyof Permission, contentScope?: ContentScope): boolean { if (!user.permissions) return false; - return user.permissions.some((p) => p.permission === permission); + return user.permissions.some((p) => p.permission === permission && (!contentScope || this.checkContentScope(p.contentScopes, contentScope))); } } diff --git a/packages/api/cms-api/src/user-permissions/auth/user-permissions.guard.ts b/packages/api/cms-api/src/user-permissions/auth/user-permissions.guard.ts index a4d7a15fe6..7bd402cf3b 100644 --- a/packages/api/cms-api/src/user-permissions/auth/user-permissions.guard.ts +++ b/packages/api/cms-api/src/user-permissions/auth/user-permissions.guard.ts @@ -5,6 +5,7 @@ import { GqlContextType, GqlExecutionContext } from "@nestjs/graphql"; import { CurrentUserInterface } from "../../auth/current-user/current-user"; import { ContentScopeService } from "../content-scope.service"; import { RequiredPermission } from "../decorators/required-permission.decorator"; +import { ContentScope } from "../interfaces/content-scope.interface"; import { ACCESS_CONTROL_SERVICE } from "../user-permissions.constants"; import { AccessControlServiceInterface } from "../user-permissions.types"; @@ -38,8 +39,9 @@ export class UserPermissionsGuard implements CanActivate { throw new Error(`RequiredPermission decorator is missing in ${context.getClass().name}::${context.getHandler().name}()`); } + let contentScope: ContentScope | undefined; if (!this.isResolvingGraphQLField(context) && !requiredPermission.options?.skipScopeCheck) { - const contentScope = await this.contentScopeService.inferScopeFromExecutionContext(context); + contentScope = await this.contentScopeService.inferScopeFromExecutionContext(context); if (!contentScope) { throw new Error( `Could not get ContentScope. Either pass a scope-argument or add @AffectedEntity()-decorator or enable skipScopeCheck in @RequiredPermission() (${ @@ -47,15 +49,15 @@ export class UserPermissionsGuard implements CanActivate { }::${context.getHandler().name}())`, ); } - if (!this.accessControlService.isAllowedContentScope(user, contentScope)) { - return false; - } } const requiredPermissions = Array.isArray(requiredPermission.requiredPermission) ? requiredPermission.requiredPermission : [requiredPermission.requiredPermission]; - return requiredPermissions.some((permission) => this.accessControlService.isAllowedPermission(user, permission)); + if (requiredPermissions.length === 0) { + throw new Error(`RequiredPermission decorator has empty permissions in ${context.getClass().name}::${context.getHandler().name}()`); + } + return requiredPermissions.some((permission) => this.accessControlService.isAllowed(user, permission, contentScope)); } // See https://docs.nestjs.com/graphql/other-features#execute-enhancers-at-the-field-resolver-level diff --git a/packages/api/cms-api/src/user-permissions/dto/current-user.ts b/packages/api/cms-api/src/user-permissions/dto/current-user.ts index f1537b27eb..7c5285bd53 100644 --- a/packages/api/cms-api/src/user-permissions/dto/current-user.ts +++ b/packages/api/cms-api/src/user-permissions/dto/current-user.ts @@ -8,6 +8,8 @@ import { ContentScope } from "../interfaces/content-scope.interface"; export class CurrentUserPermission { @Field() permission: string; + @Field(() => [GraphQLJSONObject]) + contentScopes: ContentScope[]; } @ObjectType() @@ -20,8 +22,6 @@ export class CurrentUser implements CurrentUserInterface { email: string; @Field() language: string; - @Field(() => [GraphQLJSONObject]) - contentScopes: ContentScope[]; @Field(() => [CurrentUserPermission]) permissions: CurrentUserPermission[]; } diff --git a/packages/api/cms-api/src/user-permissions/dto/user-content-scopes.input.ts b/packages/api/cms-api/src/user-permissions/dto/user-content-scopes.input.ts index 59ae5eec28..d02ec06922 100644 --- a/packages/api/cms-api/src/user-permissions/dto/user-content-scopes.input.ts +++ b/packages/api/cms-api/src/user-permissions/dto/user-content-scopes.input.ts @@ -1,5 +1,5 @@ import { Field, InputType } from "@nestjs/graphql"; -import { IsArray } from "class-validator"; +import { IsArray, IsObject } from "class-validator"; import { GraphQLJSONObject } from "graphql-type-json"; import { ContentScope } from "../interfaces/content-scope.interface"; @@ -8,5 +8,6 @@ import { ContentScope } from "../interfaces/content-scope.interface"; export class UserContentScopesInput { @Field(() => [GraphQLJSONObject], { defaultValue: [] }) @IsArray() + @IsObject({ each: true }) contentScopes: ContentScope[] = []; } diff --git a/packages/api/cms-api/src/user-permissions/dto/user-permission.input.ts b/packages/api/cms-api/src/user-permissions/dto/user-permission.input.ts index 43e25f2e5c..279f158ea1 100644 --- a/packages/api/cms-api/src/user-permissions/dto/user-permission.input.ts +++ b/packages/api/cms-api/src/user-permissions/dto/user-permission.input.ts @@ -1,5 +1,24 @@ -import { Field, InputType } from "@nestjs/graphql"; -import { IsDate, IsOptional, IsString } from "class-validator"; +import { Field, ID, InputType } from "@nestjs/graphql"; +import { IsArray, IsBoolean, IsDate, IsObject, IsOptional, IsString, IsUUID } from "class-validator"; +import { GraphQLJSONObject } from "graphql-type-json"; + +import { ContentScope } from "../interfaces/content-scope.interface"; + +@InputType() +export class UserPermissionOverrideContentScopesInput { + @Field(() => ID) + @IsUUID() + permissionId: string; + + @Field(() => Boolean) + @IsBoolean() + overrideContentScopes: boolean; + + @Field(() => [GraphQLJSONObject], { defaultValue: [] }) + @IsArray() + @IsObject({ each: true }) + contentScopes: ContentScope[] = []; +} @InputType() export class UserPermissionInput { diff --git a/packages/api/cms-api/src/user-permissions/entities/user-permission.entity.ts b/packages/api/cms-api/src/user-permissions/entities/user-permission.entity.ts index 71079c10cf..67057aeb4b 100644 --- a/packages/api/cms-api/src/user-permissions/entities/user-permission.entity.ts +++ b/packages/api/cms-api/src/user-permissions/entities/user-permission.entity.ts @@ -1,7 +1,10 @@ import { BaseEntity, Entity, PrimaryKey, Property } from "@mikro-orm/core"; import { Field, ID, ObjectType, registerEnumType } from "@nestjs/graphql"; +import { GraphQLJSONObject } from "graphql-type-json"; import { v4 } from "uuid"; +import { ContentScope } from "../interfaces/content-scope.interface"; + export enum UserPermissionSource { MANUAL = "MANUAL", BY_RULE = "BY_RULE", @@ -46,4 +49,12 @@ export class UserPermission extends BaseEntity { @Field({ nullable: true }) @Property({ columnType: "text", nullable: true }) approvedBy?: string; + + @Field() + @Property() + overrideContentScopes: boolean = false; + + @Field(() => [GraphQLJSONObject]) + @Property({ type: "json" }) + contentScopes: ContentScope[] = []; } diff --git a/packages/api/cms-api/src/user-permissions/user-content-scopes.resolver.ts b/packages/api/cms-api/src/user-permissions/user-content-scopes.resolver.ts index 1a494ed039..85afd60ace 100644 --- a/packages/api/cms-api/src/user-permissions/user-content-scopes.resolver.ts +++ b/packages/api/cms-api/src/user-permissions/user-content-scopes.resolver.ts @@ -18,13 +18,13 @@ export class UserContentScopesResolver { private readonly userService: UserPermissionsService, ) {} - @Mutation(() => [GraphQLJSONObject]) + @Mutation(() => Boolean) @SkipBuild() async userPermissionsUpdateContentScopes( @Args("userId", { type: () => String }) userId: string, @Args("input", { type: () => UserContentScopesInput }) { contentScopes }: UserContentScopesInput, - ): Promise { - this.userService.checkContentScopes(contentScopes); + ): Promise { + await this.userService.checkContentScopes(contentScopes); let entity = await this.repository.findOne({ userId }); if (entity) { entity = this.repository.assign(entity, { userId, contentScopes }); @@ -32,7 +32,7 @@ export class UserContentScopesResolver { entity = this.repository.create({ userId, contentScopes }); } await this.repository.persistAndFlush(entity); - return this.userService.getContentScopes(userId); + return true; } @Query(() => [GraphQLJSONObject]) @@ -40,7 +40,10 @@ export class UserContentScopesResolver { @Args("userId", { type: () => String }) userId: string, @Args("skipManual", { type: () => Boolean, nullable: true }) skipManual = false, ): Promise { - return this.userService.getContentScopes(userId, skipManual); + return this.userService.normalizeContentScopes( + await this.userService.getContentScopes(userId, !skipManual), + await this.userService.getAvailableContentScopes(), + ); } @Query(() => [GraphQLJSONObject]) diff --git a/packages/api/cms-api/src/user-permissions/user-permission.resolver.ts b/packages/api/cms-api/src/user-permissions/user-permission.resolver.ts index d764e14277..1c97e6c9a2 100644 --- a/packages/api/cms-api/src/user-permissions/user-permission.resolver.ts +++ b/packages/api/cms-api/src/user-permissions/user-permission.resolver.ts @@ -5,8 +5,8 @@ import { IsString } from "class-validator"; import { SkipBuild } from "../builds/skip-build.decorator"; import { RequiredPermission } from "./decorators/required-permission.decorator"; -import { UserPermissionInput } from "./dto/user-permission.input"; -import { UserPermission } from "./entities/user-permission.entity"; +import { UserPermissionInput, UserPermissionOverrideContentScopesInput } from "./dto/user-permission.input"; +import { UserPermission, UserPermissionSource } from "./entities/user-permission.entity"; import { UserPermissionsService } from "./user-permissions.service"; @ArgsType() @@ -20,13 +20,13 @@ export class UserPermissionListArgs { @RequiredPermission(["userPermissions"], { skipScopeCheck: true }) export class UserPermissionResolver { constructor( - private readonly userService: UserPermissionsService, + private readonly service: UserPermissionsService, @InjectRepository(UserPermission) private readonly permissionRepository: EntityRepository, ) {} @Query(() => [UserPermission]) async userPermissionsPermissionList(@Args() args: UserPermissionListArgs): Promise { - return this.userService.getPermissions(args.userId); + return this.service.getPermissions(args.userId); } @Query(() => UserPermission) @@ -44,7 +44,7 @@ export class UserPermissionResolver { @Args("input", { type: () => UserPermissionInput }) input: UserPermissionInput, ): Promise { const permission = new UserPermission(); - this.userService.getUser(userId); //validate user exists + this.service.getUser(userId); //validate user exists permission.userId = userId; permission.assign(input); await this.permissionRepository.persistAndFlush(permission); @@ -53,7 +53,7 @@ export class UserPermissionResolver { @Query(() => [String]) async userPermissionsAvailablePermissions(): Promise { - return this.userService.getAvailablePermissions(); + return this.service.getAvailablePermissions(); } @Mutation(() => UserPermission) @@ -75,13 +75,28 @@ export class UserPermissionResolver { return true; } + @Mutation(() => UserPermission) + async userPermissionsUpdateOverrideContentScopes( + @Args("input", { type: () => UserPermissionOverrideContentScopesInput }) input: UserPermissionOverrideContentScopesInput, + ): Promise { + const permission = await this.getPermission(input.permissionId); + await this.service.checkContentScopes(input.contentScopes); + permission.overrideContentScopes = input.overrideContentScopes; + permission.contentScopes = input.contentScopes; + await this.permissionRepository.persistAndFlush(permission); + return permission; + } + async getPermission(id: string, userId?: string): Promise { const permission = await this.permissionRepository.findOne(id); - if (permission) return permission; + if (permission) { + permission.source = UserPermissionSource.MANUAL; + return permission; + } if (!userId) { throw new Error(`Permission not found: ${id}`); } - for (const p of await this.userService.getPermissions(userId)) { + for (const p of await this.service.getPermissions(userId)) { if (p.id === id) return p; } throw new Error("Permission not found"); diff --git a/packages/api/cms-api/src/user-permissions/user-permissions.service.ts b/packages/api/cms-api/src/user-permissions/user-permissions.service.ts index 8fb94e5cb1..ed954b8168 100644 --- a/packages/api/cms-api/src/user-permissions/user-permissions.service.ts +++ b/packages/api/cms-api/src/user-permissions/user-permissions.service.ts @@ -1,7 +1,7 @@ import { EntityRepository } from "@mikro-orm/core"; import { InjectRepository } from "@mikro-orm/nestjs"; import { Inject, Injectable } from "@nestjs/common"; -import { differenceInDays } from "date-fns"; +import { isFuture, isPast } from "date-fns"; import isEqual from "lodash.isequal"; import getUuid from "uuid-by-string"; @@ -79,67 +79,83 @@ export class UserPermissionsService { permission.id = getUuid(JSON.stringify(p)); permission.source = UserPermissionSource.BY_RULE; permission.userId = userId; - permission.assign({ - permission: p.permission, - validFrom: p.validFrom, - validTo: p.validTo, - reason: p.reason, - requestedBy: p.requestedBy, - approvedBy: p.approvedBy, - }); + permission.overrideContentScopes = !!p.contentScopes; + permission.assign(p); permissions.push(permission); } } } - return permissions.sort( - (a, b) => availablePermissions.indexOf(a.permission as keyof Permission) - availablePermissions.indexOf(b.permission as keyof Permission), - ); + return permissions + .filter((value) => availablePermissions.some((p) => p === value.permission)) // Filter out permissions that are not defined in availablePermissions (e.g. outdated database entries) + .sort( + (a, b) => + availablePermissions.indexOf(a.permission as keyof Permission) - availablePermissions.indexOf(b.permission as keyof Permission), + ); } - async getContentScopes(userId: string, skipManual = false): Promise { - const availableContentScopes = await this.getAvailableContentScopes(); + async getContentScopes(userId: string, includeContentScopesManual = true): Promise { const contentScopes: ContentScope[] = []; + if (this.accessControlService.getContentScopesForUser) { const user = await this.getUser(userId); if (user) { const userContentScopes = await this.accessControlService.getContentScopesForUser(user); if (userContentScopes === UserPermissions.allContentScopes) { - contentScopes.push(...availableContentScopes); + contentScopes.push(...(await this.getAvailableContentScopes())); } else { contentScopes.push(...userContentScopes); } } } - if (!skipManual) { + + if (includeContentScopesManual) { const entity = await this.contentScopeRepository.findOne({ userId }); if (entity) { contentScopes.push(...entity.contentScopes); } } - return [...new Set(contentScopes)] // Make values unique + + return contentScopes; + } + + normalizeContentScopes(contentScopes: ContentScope[], availableContentScopes: ContentScope[]): ContentScope[] { + return [...new Set(contentScopes.map((cs) => JSON.stringify(cs)))] // Make values unique + .map((cs) => JSON.parse(cs)) .filter((value) => availableContentScopes.some((cs) => isEqual(cs, value))) // Allow only values that are defined in availableContentScopes .sort((a, b) => availableContentScopes.indexOf(a) - availableContentScopes.indexOf(b)); // Order by availableContentScopes } async createCurrentUser(user: User): Promise { + const availableContentScopes = await this.getAvailableContentScopes(); + const userContentScopes = await this.getContentScopes(user.id); + const permissions = (await this.getPermissions(user.id)) + .filter((p) => (!p.validFrom || isPast(p.validFrom)) && (!p.validTo || isFuture(p.validTo))) + .reduce((acc: CurrentUser["permissions"], userPermission) => { + const contentScopes = userPermission.overrideContentScopes ? userPermission.contentScopes : userContentScopes; + const existingPermission = acc.find((p) => p.permission === userPermission.permission); + if (existingPermission) { + existingPermission.contentScopes = [...existingPermission.contentScopes, ...contentScopes]; + } else { + acc.push({ + permission: userPermission.permission, + contentScopes, + }); + } + return acc; + }, []) + .map((p) => { + p.contentScopes = this.normalizeContentScopes(p.contentScopes, availableContentScopes); + return p; + }); + const currentUser = new CurrentUser(); - Object.assign(currentUser, { + return Object.assign(currentUser, { id: user.id, name: user.name, email: user.email ?? "", language: user.language, - contentScopes: await this.getContentScopes(user.id), - permissions: (await this.getPermissions(user.id)) - .filter( - (p) => - (!p.validFrom || differenceInDays(new Date(), p.validFrom) >= 0) && - (!p.validTo || differenceInDays(p.validTo, new Date()) >= 0), - ) - .map((p) => ({ - permission: p.permission, - })), + permissions, }); - return currentUser; } } diff --git a/packages/api/cms-api/src/user-permissions/user-permissions.types.ts b/packages/api/cms-api/src/user-permissions/user-permissions.types.ts index 258330fdab..0d66e8ff61 100644 --- a/packages/api/cms-api/src/user-permissions/user-permissions.types.ts +++ b/packages/api/cms-api/src/user-permissions/user-permissions.types.ts @@ -14,15 +14,16 @@ export enum UserPermissions { export type Users = [User[], number]; -export type PermissionsForUser = - | Pick[] - | UserPermissions.allPermissions; +type PermissionForUser = { + permission: keyof Permission; + contentScopes?: ContentScope[]; +} & Pick; +export type PermissionsForUser = PermissionForUser[] | UserPermissions.allPermissions; export type ContentScopesForUser = ContentScope[] | UserPermissions.allContentScopes; export interface AccessControlServiceInterface { - isAllowedPermission(user: CurrentUserInterface, permission: keyof Permission): boolean; - isAllowedContentScope(user: CurrentUserInterface, contentScope: ContentScope): boolean; + isAllowed(user: CurrentUserInterface, permission: keyof Permission, contentScope?: ContentScope): boolean; getPermissionsForUser?: (user: User) => Promise | PermissionsForUser; getContentScopesForUser?: (user: User) => Promise | ContentScopesForUser; } diff --git a/packages/api/tsconfig.base.json b/packages/api/tsconfig.base.json index a2e2d5fbed..d7b1700ca5 100644 --- a/packages/api/tsconfig.base.json +++ b/packages/api/tsconfig.base.json @@ -3,7 +3,6 @@ "compilerOptions": { "target": "ES2015", "lib": ["ES2020"], - "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "sourceMap": true, diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 32cbc87f9d..de1c709d9f 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,11 @@ # @comet/cli +## 5.6.0 + +## 5.5.0 + +## 5.4.0 + ## 5.3.0 ## 5.2.0 diff --git a/packages/cli/package.json b/packages/cli/package.json index ac9675ad64..0727e6a476 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@comet/cli", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -29,7 +29,7 @@ "prettier": "^2.7.1" }, "devDependencies": { - "@comet/eslint-config": "^5.3.0", + "@comet/eslint-config": "^5.6.0", "@types/node": "^18.0.0", "eslint": "^8.0.0", "npm-run-all": "^4.1.5", diff --git a/packages/eslint-config/CHANGELOG.md b/packages/eslint-config/CHANGELOG.md index 5ff7a59a1d..0e53399157 100644 --- a/packages/eslint-config/CHANGELOG.md +++ b/packages/eslint-config/CHANGELOG.md @@ -1,5 +1,24 @@ # @comet/eslint-config +## 5.6.0 + +### Patch Changes + +- @comet/eslint-plugin@5.6.0 + +## 5.5.0 + +### Patch Changes + +- 80a6d8d3: Move import restriction for MUI's `Alert` to correct ESLint config + - @comet/eslint-plugin@5.5.0 + +## 5.4.0 + +### Patch Changes + +- @comet/eslint-plugin@5.4.0 + ## 5.3.0 ### Patch Changes diff --git a/packages/eslint-config/core-without-import.js b/packages/eslint-config/core-without-import.js deleted file mode 100644 index 90896fa0ed..0000000000 --- a/packages/eslint-config/core-without-import.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports = { - extends: ["eslint:recommended", "plugin:prettier/recommended"], - plugins: ["simple-import-sort", "unused-imports", "json-files", "@comet"], - rules: { - "no-unused-vars": "off", - "prefer-template": "error", - "simple-import-sort/exports": "error", - "simple-import-sort/imports": "error", - "unused-imports/no-unused-imports": "error", - "no-console": ["error", { allow: ["warn", "error"] }], - "no-return-await": "error", - "json-files/sort-package-json": "error", - "@comet/no-other-module-relative-import": ["warn"] - }, - overrides: [ - { - files: ["*.ts", "*.tsx"], - parser: "@typescript-eslint/parser", - extends: ["plugin:@typescript-eslint/recommended"], - plugins: ["@typescript-eslint"], - rules: { - "@typescript-eslint/no-unused-vars": ["error", { args: "none", ignoreRestSiblings: true }], - "@typescript-eslint/no-inferrable-types": ["error", { "ignoreProperties": true }], - }, - }, - ], -}; diff --git a/packages/eslint-config/core.js b/packages/eslint-config/core.js index f60edf52b1..02a2d963b1 100644 --- a/packages/eslint-config/core.js +++ b/packages/eslint-config/core.js @@ -1,11 +1,30 @@ -const core = require("./core-without-import"); - module.exports = { - extends: require.resolve("./core-without-import.js"), - plugins: [...core.plugins, "import"], + extends: ["eslint:recommended", "plugin:prettier/recommended"], + plugins: ["simple-import-sort", "unused-imports", "json-files", "@comet", "import"], rules: { + "no-unused-vars": "off", + "prefer-template": "error", + "simple-import-sort/exports": "error", + "simple-import-sort/imports": "error", + "unused-imports/no-unused-imports": "error", + "no-console": ["error", { allow: ["warn", "error"] }], + "no-return-await": "error", + "json-files/sort-package-json": "error", + "@comet/no-other-module-relative-import": ["warn"], "import/no-extraneous-dependencies": "error", "import/no-duplicates": "error", - "import/newline-after-import": "error" + "import/newline-after-import": "error", }, + overrides: [ + { + files: ["*.ts", "*.tsx"], + parser: "@typescript-eslint/parser", + extends: ["plugin:@typescript-eslint/recommended"], + plugins: ["@typescript-eslint"], + rules: { + "@typescript-eslint/no-unused-vars": ["error", { args: "none", ignoreRestSiblings: true }], + "@typescript-eslint/no-inferrable-types": ["error", { ignoreProperties: true }], + }, + }, + ], }; diff --git a/packages/eslint-config/nextjs.js b/packages/eslint-config/nextjs.js index ff0fab8c27..bad2edc057 100644 --- a/packages/eslint-config/nextjs.js +++ b/packages/eslint-config/nextjs.js @@ -1,25 +1,28 @@ module.exports = { - extends: [require.resolve("./core-without-import.js"), "next/core-web-vitals"], + extends: [require.resolve("./core.js"), "next/core-web-vitals"], rules: { "react/display-name": "off", "react/prop-types": "off", "react/self-closing-comp": "error", - "import/no-extraneous-dependencies": "error", - "import/newline-after-import": "error", "@comet/no-private-sibling-import": ["error", ["gql", "sc", "gql.generated"]], "no-restricted-imports": [ "error", { paths: [ { - name: "next/image", + name: "next/link", importNames: ["default"], - message: "Please use Image from @comet/cms-site instead", + message: "Please use Link from @comet/cms-site instead", + }, + { + name: "next/router", + importNames: ["useRouter"], + message: "Please use useRouter from @comet/cms-site instead", }, { - name: "@mui/material", - importNames: ["Alert"], - message: "Please use Alert from @comet/admin instead", + name: "next/image", + importNames: ["default"], + message: "Please use Image from @comet/cms-site instead", }, ], }, diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 1636012681..c42d046685 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@comet/eslint-config", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -23,7 +23,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-simple-import-sort": "^9.0.0", "eslint-plugin-unused-imports": "^2.0.0", - "@comet/eslint-plugin": "workspace:^5.3.0" + "@comet/eslint-plugin": "workspace:^5.6.0" }, "devDependencies": { "eslint": "^8.32.0", diff --git a/packages/eslint-config/react.js b/packages/eslint-config/react.js index 029294182a..5ffa016b08 100644 --- a/packages/eslint-config/react.js +++ b/packages/eslint-config/react.js @@ -34,10 +34,15 @@ module.exports = { importNames: ["styled"], message: "Please use styled from @mui/material/styles instead.", }, + { + name: "@mui/material", + importNames: ["Alert"], + message: "Please use Alert from @comet/admin instead", + }, ], }, ], - "@comet/no-private-sibling-import": ["error", ["gql", "sc", "gql.generated"]] + "@comet/no-private-sibling-import": ["error", ["gql", "sc", "gql.generated"]], }, overrides: [ { diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index a2e5ccd0de..bb423cda31 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -1,5 +1,11 @@ # @comet/eslint-plugin +## 5.6.0 + +## 5.5.0 + +## 5.4.0 + ## 5.3.0 ## 5.2.0 diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index f1205a54a9..d84a60ca85 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@comet/eslint-plugin", - "version": "5.3.0", + "version": "5.6.0", "main": "lib/index.js", "scripts": { "build": "$npm_execpath run clean && tsc", diff --git a/packages/site/cms-site/CHANGELOG.md b/packages/site/cms-site/CHANGELOG.md index 8ef631f4e8..ad1d2cb547 100644 --- a/packages/site/cms-site/CHANGELOG.md +++ b/packages/site/cms-site/CHANGELOG.md @@ -1,5 +1,32 @@ # @comet/cms-site +## 5.6.0 + +## 5.5.0 + +## 5.4.0 + +### Minor Changes + +- f9063860: Add `hasRichTextBlockContent` helper + + The helper can be used to conditionally render a `RichTextBlock`. + + **Example:** + + ```tsx + import { hasRichTextBlockContent } from "@comet/cms-site"; + + function TeaserBlock({ data: { image, text } }: PropsWithData) { + return ( + <> + + {hasRichTextBlockContent(text) && } + + ); + } + ``` + ## 5.3.0 ## 5.2.0 diff --git a/packages/site/cms-site/package.json b/packages/site/cms-site/package.json index b74a855286..a957865cbe 100644 --- a/packages/site/cms-site/package.json +++ b/packages/site/cms-site/package.json @@ -1,6 +1,6 @@ { "name": "@comet/cms-site", - "version": "5.3.0", + "version": "5.6.0", "repository": { "type": "git", "url": "https://github.com/vivid-planet/comet", @@ -14,7 +14,7 @@ ], "scripts": { "build": "$npm_execpath run clean && npm run generate-block-types && tsc --project tsconfig.build.json", - "clean": "rimraf lib", + "clean": "rimraf lib 'src/**/*.generated.ts'", "dev": "$npm_execpath generate-block-types && tsc --watch --preserveWatchOutput --project tsconfig.build.json", "generate-block-types": "comet generate-block-types", "generate-block-types:watch": "chokidar -s \"**/block-meta.json\" -c \"$npm_execpath generate-block-types\"", @@ -32,8 +32,8 @@ "use-debounce": "^6.0.0" }, "devDependencies": { - "@comet/cli": "workspace:^5.3.0", - "@comet/eslint-config": "workspace:^5.3.0", + "@comet/cli": "workspace:^5.6.0", + "@comet/eslint-config": "workspace:^5.6.0", "@gitbeaker/node": "^34.0.0", "@testing-library/react-hooks": "^8.0.0", "@types/draft-js": "^0.11.10", diff --git a/packages/site/cms-site/src/blocks/InternalLinkBlock.tsx b/packages/site/cms-site/src/blocks/InternalLinkBlock.tsx index abee2bc39c..804e3ddc0d 100644 --- a/packages/site/cms-site/src/blocks/InternalLinkBlock.tsx +++ b/packages/site/cms-site/src/blocks/InternalLinkBlock.tsx @@ -1,7 +1,7 @@ -import Link from "next/link"; import * as React from "react"; import { InternalLinkBlockData } from "../blocks.generated"; +import { Link } from "../link/Link"; import { PropsWithData } from "./PropsWithData"; interface InternalLinkBlockProps extends PropsWithData { diff --git a/packages/site/cms-site/src/index.ts b/packages/site/cms-site/src/index.ts index e692be19bd..86d2a017fb 100644 --- a/packages/site/cms-site/src/index.ts +++ b/packages/site/cms-site/src/index.ts @@ -17,10 +17,13 @@ export { useIFrameBridge } from "./iframebridge/useIFrameBridge"; export { isWithPreviewPropsData, withPreview, WithPreviewProps } from "./iframebridge/withPreview"; export type { ImageDimensions } from "./image/Image"; export { calculateInheritAspectRatio, generateImageUrl, getMaxDimensionsFromArea, Image, parseAspectRatio } from "./image/Image"; +export { Link } from "./link/Link"; export { BlockPreviewProvider } from "./preview/BlockPreviewProvider"; -export { handlePreviewApiRequest, PreviewData } from "./preview/handlePreviewApiRequest"; export { usePreview } from "./preview/usePreview"; +export { parsePreviewParams, parsePreviewState } from "./preview/utils"; export { PreviewSkeleton } from "./previewskeleton/PreviewSkeleton"; +export { useRouter } from "./router/useRouter"; export { sendSitePreviewIFrameMessage } from "./sitePreview/iframebridge/sendSitePreviewIFrameMessage"; export { SitePreviewIFrameMessageType } from "./sitePreview/iframebridge/SitePreviewIFrameMessage"; +export { PreviewPage, SitePreviewPage } from "./sitePreview/SitePreviewPage"; export { SitePreviewProvider } from "./sitePreview/SitePreviewProvider"; diff --git a/packages/site/cms-site/src/link/Link.tsx b/packages/site/cms-site/src/link/Link.tsx new file mode 100644 index 0000000000..56914f8da3 --- /dev/null +++ b/packages/site/cms-site/src/link/Link.tsx @@ -0,0 +1,17 @@ +// eslint-disable-next-line no-restricted-imports +import NextLink, { LinkProps as NextLinkProps } from "next/link"; +import * as React from "react"; + +import { usePreview } from "../preview/usePreview"; + +export type LinkProps = React.PropsWithChildren; + +export const Link = ({ children, href, ...restProps }: LinkProps): JSX.Element => { + const { pathToPreviewPath } = usePreview(); + + return ( + + {children} + + ); +}; diff --git a/packages/site/cms-site/src/preview/BlockPreviewProvider.tsx b/packages/site/cms-site/src/preview/BlockPreviewProvider.tsx index c4a5809999..d374758484 100644 --- a/packages/site/cms-site/src/preview/BlockPreviewProvider.tsx +++ b/packages/site/cms-site/src/preview/BlockPreviewProvider.tsx @@ -8,6 +8,8 @@ export const BlockPreviewProvider: React.FunctionComponent = ({ children }) => { value={{ previewType: "BlockPreview", showPreviewSkeletons: true, + pathToPreviewPath: () => "", + previewPathToPath: () => "", }} > {children} diff --git a/packages/site/cms-site/src/preview/PreviewContext.tsx b/packages/site/cms-site/src/preview/PreviewContext.tsx index abc93a40f3..6dcb8b3dfe 100644 --- a/packages/site/cms-site/src/preview/PreviewContext.tsx +++ b/packages/site/cms-site/src/preview/PreviewContext.tsx @@ -6,11 +6,18 @@ export type Url = string | UrlObject; export interface PreviewContextOptions { previewType: "SitePreview" | "BlockPreview" | "NoPreview"; showPreviewSkeletons: boolean; + // internal links only + pathToPreviewPath: (originalPagePath: Url) => Url; + + // converts a previewpath to a path + previewPathToPath: (previewPath: string) => string; } export const defaultPreviewContextValue: PreviewContextOptions = { previewType: "NoPreview", showPreviewSkeletons: false, + pathToPreviewPath: (path) => path, + previewPathToPath: (previewUrl: string) => previewUrl, }; export const PreviewContext = React.createContext(defaultPreviewContextValue); diff --git a/packages/site/cms-site/src/preview/handlePreviewApiRequest.ts b/packages/site/cms-site/src/preview/handlePreviewApiRequest.ts deleted file mode 100644 index f1ed795e01..0000000000 --- a/packages/site/cms-site/src/preview/handlePreviewApiRequest.ts +++ /dev/null @@ -1,19 +0,0 @@ -import jsonwebtoken from "jsonwebtoken"; -import { NextApiRequest, NextApiResponse } from "next"; - -export function handlePreviewApiRequest(req: NextApiRequest, res: NextApiResponse) { - if (!process.env.SITE_PREVIEW_SECRET) throw new Error("process.env.SITE_PREVIEW_SECRET is required"); - if (!req.query["jwt"]) throw new Error("jwt is required"); - - const data = jsonwebtoken.verify(req.query["jwt"].toString(), process.env.SITE_PREVIEW_SECRET) as { - path: string; - previewData: PreviewData; - }; - res.setPreviewData(data.previewData); - res.redirect(data.path); -} - -// See packages/api/cms-api/src/page-tree/site-preview.resolver.ts -export type PreviewData = { - includeInvisible: boolean; -}; diff --git a/packages/site/cms-site/src/preview/utils.ts b/packages/site/cms-site/src/preview/utils.ts new file mode 100644 index 0000000000..b6729391e5 --- /dev/null +++ b/packages/site/cms-site/src/preview/utils.ts @@ -0,0 +1,71 @@ +import { ParsedUrlQuery } from "querystring"; + +import { Url } from "./PreviewContext"; + +export const defaultPreviewPath = "/preview"; + +interface SitePreviewParams { + includeInvisibleBlocks: boolean; +} + +const defaultParams: SitePreviewParams = { + includeInvisibleBlocks: false, +}; + +export function parsePreviewParams(query: ParsedUrlQuery): SitePreviewParams { + let previewParams: SitePreviewParams = defaultParams; + const param = query.__preview; + + if (typeof param === "string") { + try { + previewParams = JSON.parse(param); + } catch { + // Ignore invalid preview state + } + } + + return previewParams; +} + +/** + * @deprecated Use parsePreviewParams instead + */ +const parsePreviewState = parsePreviewParams; + +export { parsePreviewState }; + +export function createPathToPreviewPath({ + path, + previewPath, + previewParams, +}: { + path: Url; + previewPath: string; + previewParams: SitePreviewParams; +}): Url { + if (typeof path === "string") { + const [pathname, search] = `${previewPath}${path}`.split("?"); + const searchParams = new URLSearchParams(search); + + searchParams.append("__preview", JSON.stringify(previewParams)); + + return `${pathname}?${searchParams.toString()}`; + } else { + let query = path.query; + + if (typeof query === "string") { + query += `&__preview=${JSON.stringify(previewParams)}`; + } else if (typeof query === "object") { + query = { + ...query, + __preview: JSON.stringify(previewParams), + }; + } + + return { + ...path, + pathname: `${previewPath}${path.pathname}`, + query, + }; + } +} diff --git a/packages/site/cms-site/src/router/useRouter.tsx b/packages/site/cms-site/src/router/useRouter.tsx new file mode 100644 index 0000000000..94aff49f4c --- /dev/null +++ b/packages/site/cms-site/src/router/useRouter.tsx @@ -0,0 +1,18 @@ +// eslint-disable-next-line no-restricted-imports +import { NextRouter, useRouter as useNextRouter } from "next/router"; + +import { usePreview } from "../preview/usePreview"; + +export function useRouter(): NextRouter { + const { previewPathToPath, pathToPreviewPath } = usePreview(); + const router = useNextRouter(); + + return { + ...router, + pathname: previewPathToPath(router.pathname), + asPath: previewPathToPath(router.asPath), + route: previewPathToPath(router.route), + push: (url, as, options) => router.push(pathToPreviewPath(url), as, options), + replace: (url, as, options) => router.replace(pathToPreviewPath(url), as, options), + }; +} diff --git a/packages/site/cms-site/src/sitePreview/SitePreviewPage.tsx b/packages/site/cms-site/src/sitePreview/SitePreviewPage.tsx new file mode 100644 index 0000000000..f5e798d1b4 --- /dev/null +++ b/packages/site/cms-site/src/sitePreview/SitePreviewPage.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import { SitePreviewProvider } from "./SitePreviewProvider"; + +export const SitePreviewPage: React.FunctionComponent = ({ children }) => { + return {children}; +}; + +/** + * @deprecated Use SitePreviewPage instead + */ +const PreviewPage = SitePreviewPage; + +export { PreviewPage }; diff --git a/packages/site/cms-site/src/sitePreview/SitePreviewProvider.tsx b/packages/site/cms-site/src/sitePreview/SitePreviewProvider.tsx index 8956d0e739..e25dd6278d 100644 --- a/packages/site/cms-site/src/sitePreview/SitePreviewProvider.tsx +++ b/packages/site/cms-site/src/sitePreview/SitePreviewProvider.tsx @@ -1,17 +1,24 @@ +// eslint-disable-next-line no-restricted-imports import { useRouter } from "next/router"; import * as React from "react"; -import { PreviewContext } from "../preview/PreviewContext"; +import { PreviewContext, Url } from "../preview/PreviewContext"; +import { createPathToPreviewPath, defaultPreviewPath, parsePreviewParams } from "../preview/utils"; import { sendSitePreviewIFrameMessage } from "./iframebridge/sendSitePreviewIFrameMessage"; import { SitePreviewIFrameLocationMessage, SitePreviewIFrameMessageType } from "./iframebridge/SitePreviewIFrameMessage"; -const SitePreview: React.FunctionComponent = ({ children }) => { +interface Props { + previewPath?: string; +} + +export const SitePreviewProvider: React.FunctionComponent = ({ children, previewPath = defaultPreviewPath }) => { const router = useRouter(); React.useEffect(() => { function sendUpstreamMessage() { const url = new URL(router.asPath, window.location.origin); const { pathname, searchParams } = url; + searchParams.delete("__preview"); // Remove __preview query parameter -> that's frontend preview internal const message: SitePreviewIFrameLocationMessage = { cometType: SitePreviewIFrameMessageType.SitePreviewLocation, @@ -26,19 +33,42 @@ const SitePreview: React.FunctionComponent = ({ children }) => { }; }, [router]); + const previewParams = parsePreviewParams(router.query); + + // maps the original-path to the preview-path + const pathToPreviewPath = React.useCallback( + (path: Url) => { + return createPathToPreviewPath({ path, previewPath, previewParams }); + }, + [previewPath, previewParams], + ); + const previewPathToPath = React.useCallback( + (previewUrl: string) => { + // Parse url + const [pathname, search] = previewUrl.split("?"); + + // remove previewPath Prefix e.g. '/preview' from path + const newPathname = pathname.replace(new RegExp(`^${previewPath}`), ""); + + // remove __preview query parameter from searchParams + const newSearchParams = new URLSearchParams(search); + newSearchParams.delete("__preview"); + const newSearch = newSearchParams.toString(); + + return `${newPathname.length === 0 ? "/" : newPathname}${newSearch.length === 0 ? "" : `?${newSearch}`}`; + }, + [previewPath], + ); return ( {children} ); }; - -export const SitePreviewProvider: React.FunctionComponent = ({ children }) => { - const router = useRouter(); - return <>{router.isPreview ? {children} : children}; -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9573a4d184..c06eed0649 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -347,7 +347,7 @@ importers: version: 0.8.3 babel-loader: specifier: ^8.0.0 - version: 8.3.0(@babel/core@7.20.12)(webpack@5.75.0) + version: 8.3.0(@babel/core@7.20.12)(webpack@5.89.0) babel-plugin-inline-react-svg: specifier: ^2.0.0 version: 2.0.1(@babel/core@7.20.12) @@ -365,7 +365,7 @@ importers: version: 7.0.3 css-loader: specifier: ^6.0.0 - version: 6.7.3(webpack@5.75.0) + version: 6.7.3(webpack@5.89.0) dotenv-cli: specifier: ^4.0.0 version: 4.1.1 @@ -380,7 +380,7 @@ importers: version: 3.7.0(graphql@15.8.0) html-webpack-plugin: specifier: ^5.0.0 - version: 5.5.0(webpack@5.75.0) + version: 5.5.0(webpack@5.89.0) npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -392,7 +392,7 @@ importers: version: 2.8.3 style-loader: specifier: ^3.0.0 - version: 3.3.1(webpack@5.75.0) + version: 3.3.1(webpack@5.89.0) ts-node: specifier: ^10.0.0 version: 10.9.1(@types/node@18.15.3)(typescript@4.9.4) @@ -401,13 +401,22 @@ importers: version: 4.9.4 webpack: specifier: ^5.0.0 - version: 5.75.0(webpack-cli@4.10.0) + version: 5.89.0(webpack-cli@4.10.0) webpack-cli: specifier: ^4.0.0 - version: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.75.0) + version: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.89.0) webpack-dev-server: specifier: ^4.0.0 - version: 4.11.1(webpack-cli@4.10.0)(webpack@5.75.0) + version: 4.11.1(webpack-cli@4.10.0)(webpack@5.89.0) + + demo/admin/server: + dependencies: + compression: + specifier: ^1.7.4 + version: 1.7.4 + express: + specifier: ^4.18.2 + version: 4.18.2 demo/api: dependencies: @@ -569,7 +578,7 @@ importers: version: 9.0.0 webpack: specifier: ^5.64.2 - version: 5.75.0(webpack-cli@4.10.0) + version: 5.75.0 devDependencies: '@comet/eslint-config': specifier: workspace:* @@ -961,7 +970,7 @@ importers: packages/admin/admin: dependencies: '@comet/admin-icons': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-icons '@mui/private-theming': specifier: ^5.0.0 @@ -1010,10 +1019,10 @@ importers: specifier: ^7.20.12 version: 7.20.12 '@comet/admin-babel-preset': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-babel-preset '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@emotion/react': specifier: ^11.5.0 @@ -1181,10 +1190,10 @@ importers: packages/admin/admin-color-picker: dependencies: '@comet/admin': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin '@comet/admin-icons': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-icons clsx: specifier: ^1.1.1 @@ -1206,10 +1215,10 @@ importers: specifier: ^7.20.12 version: 7.20.12 '@comet/admin-babel-preset': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-babel-preset '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@mui/icons-material': specifier: ^5.0.0 @@ -1263,10 +1272,10 @@ importers: packages/admin/admin-date-time: dependencies: '@comet/admin': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin '@comet/admin-icons': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-icons '@mui/utils': specifier: ^5.4.1 @@ -1288,10 +1297,10 @@ importers: specifier: ^7.20.12 version: 7.20.12 '@comet/admin-babel-preset': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-babel-preset '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@mui/material': specifier: ^5.0.0 @@ -1348,10 +1357,10 @@ importers: specifier: ^7.20.12 version: 7.20.12 '@comet/admin-babel-preset': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-babel-preset '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@mui/material': specifier: ^5.0.0 @@ -1405,7 +1414,7 @@ importers: packages/admin/admin-react-select: dependencies: '@comet/admin': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin classnames: specifier: ^2.2.6 @@ -1418,10 +1427,10 @@ importers: specifier: ^7.20.12 version: 7.20.12 '@comet/admin-babel-preset': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-babel-preset '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@mui/icons-material': specifier: ^5.0.0 @@ -1475,7 +1484,7 @@ importers: packages/admin/admin-rte: dependencies: '@comet/admin-icons': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-icons detect-browser: specifier: ^5.2.1 @@ -1494,10 +1503,10 @@ importers: specifier: ^7.20.12 version: 7.20.12 '@comet/admin-babel-preset': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-babel-preset '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@mui/icons-material': specifier: ^5.0.0 @@ -1802,12 +1811,12 @@ importers: version: 4.9.4 webpack: specifier: ^5.0.0 - version: 5.75.0(webpack-cli@4.10.0) + version: 5.75.0 packages/admin/admin-theme: dependencies: '@comet/admin-icons': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-icons '@mui/utils': specifier: ^5.4.1 @@ -1820,10 +1829,10 @@ importers: specifier: ^7.20.12 version: 7.20.12 '@comet/admin-babel-preset': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-babel-preset '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@mui/material': specifier: ^5.0.0 @@ -1868,10 +1877,10 @@ importers: packages/admin/blocks-admin: dependencies: '@comet/admin': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin '@comet/admin-icons': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-icons '@mui/lab': specifier: ^5.0.0-alpha.76 @@ -1902,13 +1911,13 @@ importers: specifier: ^7.20.12 version: 7.20.12 '@comet/admin-babel-preset': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-babel-preset '@comet/cli': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../cli '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@emotion/react': specifier: ^11.5.0 @@ -2001,22 +2010,22 @@ importers: packages/admin/cms-admin: dependencies: '@comet/admin': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin '@comet/admin-date-time': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-date-time '@comet/admin-icons': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-icons '@comet/admin-rte': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-rte '@comet/admin-theme': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-theme '@comet/blocks-admin': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../blocks-admin '@graphql-tools/graphql-file-loader': specifier: ^7.5.17 @@ -2134,13 +2143,13 @@ importers: specifier: ^7.20.12 version: 7.20.12 '@comet/admin-babel-preset': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../admin-babel-preset '@comet/cli': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../cli '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@emotion/react': specifier: ^11.5.0 @@ -2318,7 +2327,7 @@ importers: version: 3.0.2 devDependencies: '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@nestjs/common': specifier: ^9.0.0 @@ -2375,7 +2384,7 @@ importers: packages/api/cms-api: dependencies: '@comet/blocks-api': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../blocks-api '@hapi/accept': specifier: ^5.0.2 @@ -2505,7 +2514,7 @@ importers: specifier: ^12.0.0 version: 12.12.0 '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@kubernetes/client-node': specifier: ^0.18.1 @@ -2638,7 +2647,7 @@ importers: version: 2.8.3 devDependencies: '@comet/eslint-config': - specifier: ^5.3.0 + specifier: ^5.6.0 version: link:../eslint-config '@types/node': specifier: ^18.0.0 @@ -2662,7 +2671,7 @@ importers: specifier: ^1.4.1 version: 1.4.1 '@comet/eslint-plugin': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../eslint-plugin '@next/eslint-plugin-next': specifier: ^12.0.0 @@ -2760,10 +2769,10 @@ importers: version: 6.0.1(react@17.0.2) devDependencies: '@comet/cli': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../cli '@comet/eslint-config': - specifier: workspace:^5.3.0 + specifier: workspace:^5.6.0 version: link:../../eslint-config '@gitbeaker/node': specifier: ^34.0.0 @@ -3212,7 +3221,7 @@ packages: '@babel/generator': 7.20.7 '@babel/parser': 7.20.13 '@babel/runtime': 7.20.13 - '@babel/traverse': 7.20.13(supports-color@5.5.0) + '@babel/traverse': 7.20.13 '@babel/types': 7.20.7 babel-preset-fbjs: 3.4.0(@babel/core@7.22.11) chalk: 4.1.2 @@ -4288,10 +4297,10 @@ packages: '@babel/helpers': 7.20.13 '@babel/parser': 7.20.13 '@babel/template': 7.20.7 - '@babel/traverse': 7.20.13(supports-color@5.5.0) + '@babel/traverse': 7.20.13 '@babel/types': 7.20.7 convert-source-map: 1.9.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.0 @@ -4313,7 +4322,7 @@ packages: '@babel/traverse': 7.22.11 '@babel/types': 7.22.11 convert-source-map: 1.9.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -4560,7 +4569,7 @@ packages: '@babel/helper-split-export-declaration': 7.18.6 '@babel/helper-validator-identifier': 7.19.1 '@babel/template': 7.20.7 - '@babel/traverse': 7.20.13(supports-color@5.5.0) + '@babel/traverse': 7.20.13 '@babel/types': 7.20.7 transitivePeerDependencies: - supports-color @@ -4633,7 +4642,7 @@ packages: '@babel/helper-member-expression-to-functions': 7.20.7 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/template': 7.20.7 - '@babel/traverse': 7.20.13(supports-color@5.5.0) + '@babel/traverse': 7.20.13 '@babel/types': 7.20.7 transitivePeerDependencies: - supports-color @@ -4708,7 +4717,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.20.7 - '@babel/traverse': 7.20.13(supports-color@5.5.0) + '@babel/traverse': 7.20.13 '@babel/types': 7.20.7 transitivePeerDependencies: - supports-color @@ -6753,7 +6762,7 @@ packages: '@babel/helper-split-export-declaration': 7.18.6 '@babel/parser': 7.20.13 '@babel/types': 7.20.7 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -6787,7 +6796,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.22.14 '@babel/types': 7.22.11 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -7091,7 +7100,7 @@ packages: '@slorber/static-site-generator-webpack-plugin': 4.0.7 '@svgr/webpack': 6.5.1 autoprefixer: 10.4.14(postcss@8.4.21) - babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@5.88.2) + babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@5.89.0) babel-plugin-dynamic-import-node: 2.3.3 boxen: 6.2.1 chalk: 4.1.2 @@ -7100,33 +7109,33 @@ packages: cli-table3: 0.6.3 combine-promises: 1.1.0 commander: 5.1.0 - copy-webpack-plugin: 11.0.0(webpack@5.88.2) + copy-webpack-plugin: 11.0.0(webpack@5.89.0) core-js: 3.27.2 - css-loader: 6.7.3(webpack@5.88.2) - css-minimizer-webpack-plugin: 4.2.2(clean-css@5.3.2)(webpack@5.88.2) + css-loader: 6.7.3(webpack@5.89.0) + css-minimizer-webpack-plugin: 4.2.2(clean-css@5.3.2)(webpack@5.89.0) cssnano: 5.1.15(postcss@8.4.21) del: 6.1.1 detect-port: 1.5.1 escape-html: 1.0.3 eta: 2.2.0 - file-loader: 6.2.0(webpack@5.88.2) + file-loader: 6.2.0(webpack@5.89.0) fs-extra: 10.1.0 html-minifier-terser: 6.1.0 html-tags: 3.2.0 - html-webpack-plugin: 5.5.0(webpack@5.88.2) + html-webpack-plugin: 5.5.0(webpack@5.89.0) import-fresh: 3.3.0 leven: 3.1.0 lodash: 4.17.21 - mini-css-extract-plugin: 2.7.6(webpack@5.88.2) + mini-css-extract-plugin: 2.7.6(webpack@5.89.0) postcss: 8.4.21 - postcss-loader: 7.3.3(postcss@8.4.21)(webpack@5.88.2) + postcss-loader: 7.3.3(postcss@8.4.21)(webpack@5.89.0) prompts: 2.4.2 react: 17.0.2 - react-dev-utils: 12.0.1(typescript@4.9.4)(webpack@5.88.2) + react-dev-utils: 12.0.1(typescript@4.9.4)(webpack@5.89.0) react-dom: 17.0.2(react@17.0.2) react-helmet-async: 1.3.0(react-dom@17.0.2)(react@17.0.2) react-loadable: /@docusaurus/react-loadable@5.5.2(react@17.0.2) - react-loadable-ssr-addon-v5-slorber: 1.0.1(@docusaurus/react-loadable@5.5.2)(webpack@5.88.2) + react-loadable-ssr-addon-v5-slorber: 1.0.1(@docusaurus/react-loadable@5.5.2)(webpack@5.89.0) react-router: 5.3.4(react@17.0.2) react-router-config: 5.1.1(react-router@5.3.4)(react@17.0.2) react-router-dom: 5.3.4(react@17.0.2) @@ -7134,16 +7143,16 @@ packages: semver: 7.3.8 serve-handler: 6.1.5 shelljs: 0.8.5 - terser-webpack-plugin: 5.3.6(webpack@5.88.2) + terser-webpack-plugin: 5.3.6(webpack@5.89.0) tslib: 2.4.1 update-notifier: 5.1.0 - url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.88.2) + url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.89.0) wait-on: 6.0.1 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) webpack-bundle-analyzer: 4.9.0 - webpack-dev-server: 4.11.1(webpack@5.88.2) + webpack-dev-server: 4.11.1(webpack-cli@4.10.0)(webpack@5.89.0) webpack-merge: 5.8.0 - webpackbar: 5.0.2(webpack@5.88.2) + webpackbar: 5.0.2(webpack@5.89.0) transitivePeerDependencies: - '@docusaurus/types' - '@parcel/css' @@ -7194,7 +7203,7 @@ packages: '@docusaurus/utils': 2.4.1(@docusaurus/types@2.4.1) '@mdx-js/mdx': 1.6.22 escape-html: 1.0.3 - file-loader: 6.2.0(webpack@5.88.2) + file-loader: 6.2.0(webpack@5.89.0) fs-extra: 10.1.0 image-size: 1.0.2 mdast-util-to-string: 2.0.0 @@ -7205,8 +7214,8 @@ packages: tslib: 2.4.1 unified: 9.2.2 unist-util-visit: 2.0.3 - url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.88.2) - webpack: 5.88.2 + url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.89.0) + webpack: 5.89.0(webpack-cli@4.10.0) transitivePeerDependencies: - '@docusaurus/types' - '@swc/core' @@ -7262,7 +7271,7 @@ packages: tslib: 2.4.1 unist-util-visit: 2.0.3 utility-types: 3.10.0 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) transitivePeerDependencies: - '@parcel/css' - '@swc/core' @@ -7305,7 +7314,7 @@ packages: react-dom: 17.0.2(react@17.0.2) tslib: 2.4.1 utility-types: 3.10.0 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) transitivePeerDependencies: - '@parcel/css' - '@swc/core' @@ -7340,7 +7349,7 @@ packages: react: 17.0.2 react-dom: 17.0.2(react@17.0.2) tslib: 2.4.1 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) transitivePeerDependencies: - '@parcel/css' - '@swc/core' @@ -7779,7 +7788,7 @@ packages: react-dom: 17.0.2(react@17.0.2) react-helmet-async: 1.3.0(react-dom@17.0.2)(react@17.0.2) utility-types: 3.10.0 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) webpack-merge: 5.8.0 transitivePeerDependencies: - '@swc/core' @@ -7831,7 +7840,7 @@ packages: '@docusaurus/types': 2.4.1(react-dom@17.0.2)(react@17.0.2) '@svgr/webpack': 6.5.1 escape-string-regexp: 4.0.0 - file-loader: 6.2.0(webpack@5.88.2) + file-loader: 6.2.0(webpack@5.89.0) fs-extra: 10.1.0 github-slugger: 1.5.0 globby: 11.1.0 @@ -7842,8 +7851,8 @@ packages: resolve-pathname: 3.0.0 shelljs: 0.8.5 tslib: 2.4.1 - url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.88.2) - webpack: 5.88.2 + url-loader: 4.1.1(file-loader@6.2.0)(webpack@5.89.0) + webpack: 5.89.0(webpack-cli@4.10.0) transitivePeerDependencies: - '@swc/core' - esbuild @@ -8142,7 +8151,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) espree: 9.5.2 globals: 13.19.0 ignore: 5.2.4 @@ -8966,7 +8975,7 @@ packages: dependencies: '@babel/parser': 7.20.13 '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.20.12) - '@babel/traverse': 7.20.13(supports-color@5.5.0) + '@babel/traverse': 7.20.13 '@babel/types': 7.20.7 '@graphql-tools/utils': 9.1.4(graphql@15.8.0) graphql: 15.8.0 @@ -9149,7 +9158,7 @@ packages: '@types/json-stable-stringify': 1.0.34 '@types/jsonwebtoken': 9.0.1 chalk: 4.1.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) dotenv: 16.0.3 graphql: 15.8.0 graphql-request: 5.1.0(graphql@15.8.0) @@ -9500,7 +9509,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -9573,7 +9582,7 @@ packages: chalk: 4.1.2 ci-info: 3.7.1 exit: 0.1.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-changed-files: 29.5.0 jest-config: 29.5.0(@types/node@18.15.3)(ts-node@10.9.1) jest-haste-map: 29.5.0 @@ -9667,7 +9676,7 @@ packages: collect-v8-coverage: 1.0.1 exit: 0.1.2 glob: 7.2.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.0 istanbul-lib-instrument: 5.2.1 istanbul-lib-report: 3.0.0 @@ -9696,7 +9705,7 @@ packages: dependencies: '@jridgewell/trace-mapping': 0.3.17 callsites: 3.1.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /@jest/test-result@29.5.0: @@ -9714,7 +9723,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/test-result': 29.5.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-haste-map: 29.5.0 slash: 3.0.0 dev: true @@ -9729,7 +9738,7 @@ packages: chalk: 4.1.2 convert-source-map: 1.9.0 fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-haste-map: 26.6.2 jest-regex-util: 26.0.0 jest-util: 26.6.2 @@ -9753,7 +9762,7 @@ packages: chalk: 4.1.2 convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-haste-map: 29.5.0 jest-regex-util: 29.4.3 jest-util: 29.5.0 @@ -9810,6 +9819,10 @@ packages: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} @@ -9829,12 +9842,21 @@ packages: /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + /@jridgewell/trace-mapping@0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: @@ -9861,7 +9883,7 @@ packages: underscore: 1.13.6 ws: 8.12.0 optionalDependencies: - openid-client: 5.4.0 + openid-client: 5.6.4 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -10506,7 +10528,7 @@ packages: tsconfig-paths: 4.1.1 tsconfig-paths-webpack-plugin: 4.0.0 typescript: 4.9.4 - webpack: 5.75.0(webpack-cli@4.10.0) + webpack: 5.75.0 webpack-node-externals: 3.0.0 transitivePeerDependencies: - '@swc/core' @@ -11839,7 +11861,7 @@ packages: tslib: 2.4.1 dev: false - /@pmmmwh/react-refresh-webpack-plugin@0.5.10(@types/webpack@5.28.0)(react-refresh@0.11.0)(webpack@5.88.2): + /@pmmmwh/react-refresh-webpack-plugin@0.5.10(@types/webpack@5.28.0)(react-refresh@0.11.0)(webpack@5.89.0): resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==} engines: {node: '>= 10.13'} peerDependencies: @@ -11874,9 +11896,9 @@ packages: html-entities: 2.3.3 loader-utils: 2.0.4 react-refresh: 0.11.0 - schema-utils: 3.1.1 + schema-utils: 3.3.0 source-map: 0.7.4 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /@polka/url@1.0.0-next.21: @@ -12270,34 +12292,34 @@ packages: '@types/node': 16.18.65 '@types/webpack': 4.41.33 autoprefixer: 9.8.8 - babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@4.46.0) + babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@4.47.0) case-sensitive-paths-webpack-plugin: 2.4.0 core-js: 3.27.2 - css-loader: 3.6.0(webpack@4.46.0) - file-loader: 6.2.0(webpack@4.46.0) + css-loader: 3.6.0(webpack@4.47.0) + file-loader: 6.2.0(webpack@4.47.0) find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 4.1.6(eslint@8.32.0)(typescript@4.9.4)(webpack@4.46.0) + fork-ts-checker-webpack-plugin: 4.1.6(eslint@8.32.0)(typescript@4.9.4)(webpack@4.47.0) glob: 7.2.3 glob-promise: 3.4.0(glob@7.2.3) global: 4.4.0 - html-webpack-plugin: 4.5.2(webpack@4.46.0) + html-webpack-plugin: 4.5.2(webpack@4.47.0) pnp-webpack-plugin: 1.6.4(typescript@4.9.4) postcss: 7.0.39 postcss-flexbugs-fixes: 4.2.1 - postcss-loader: 4.3.0(postcss@7.0.39)(webpack@4.46.0) - raw-loader: 4.0.2(webpack@4.46.0) + postcss-loader: 4.3.0(postcss@7.0.39)(webpack@4.47.0) + raw-loader: 4.0.2(webpack@4.47.0) react: 17.0.2 react-dom: 17.0.2(react@17.0.2) stable: 0.1.8 - style-loader: 1.3.0(webpack@4.46.0) - terser-webpack-plugin: 4.2.3(webpack@4.46.0) + style-loader: 1.3.0(webpack@4.47.0) + terser-webpack-plugin: 4.2.3(webpack@4.47.0) ts-dedent: 2.2.0 typescript: 4.9.4 - url-loader: 4.1.1(file-loader@6.2.0)(webpack@4.46.0) + url-loader: 4.1.1(file-loader@6.2.0)(webpack@4.47.0) util-deprecate: 1.0.2 - webpack: 4.46.0 - webpack-dev-middleware: 3.7.3(webpack@4.46.0) - webpack-filter-warnings-plugin: 1.2.1(webpack@4.46.0) + webpack: 4.47.0 + webpack-dev-middleware: 3.7.3(webpack@4.47.0) + webpack-filter-warnings-plugin: 1.2.1(webpack@4.47.0) webpack-hot-middleware: 2.25.3 webpack-virtual-modules: 0.2.2 transitivePeerDependencies: @@ -12336,28 +12358,28 @@ packages: '@storybook/store': 6.5.16(react-dom@17.0.2)(react@17.0.2) '@storybook/theming': 6.5.16(react-dom@17.0.2)(react@17.0.2) '@types/node': 14.18.36 - babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@5.88.2) + babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@5.89.0) babel-plugin-named-exports-order: 0.0.2 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 core-js: 3.27.2 - css-loader: 5.2.7(webpack@5.88.2) - fork-ts-checker-webpack-plugin: 6.5.2(eslint@8.32.0)(typescript@4.9.4)(webpack@5.88.2) + css-loader: 5.2.7(webpack@5.89.0) + fork-ts-checker-webpack-plugin: 6.5.2(eslint@8.32.0)(typescript@4.9.4)(webpack@5.89.0) glob: 7.2.3 glob-promise: 3.4.0(glob@7.2.3) - html-webpack-plugin: 5.5.0(webpack@5.88.2) + html-webpack-plugin: 5.5.0(webpack@5.89.0) path-browserify: 1.0.1 process: 0.11.10 react: 17.0.2 react-dom: 17.0.2(react@17.0.2) stable: 0.1.8 - style-loader: 2.0.0(webpack@5.88.2) - terser-webpack-plugin: 5.3.6(webpack@5.88.2) + style-loader: 2.0.0(webpack@5.89.0) + terser-webpack-plugin: 5.3.6(webpack@5.89.0) ts-dedent: 2.2.0 typescript: 4.9.4 util-deprecate: 1.0.2 - webpack: 5.88.2 - webpack-dev-middleware: 4.3.0(webpack@5.88.2) + webpack: 5.89.0(webpack-cli@4.10.0) + webpack-dev-middleware: 4.3.0(webpack@5.89.0) webpack-hot-middleware: 2.25.3 webpack-virtual-modules: 0.4.6 transitivePeerDependencies: @@ -12456,7 +12478,7 @@ packages: util-deprecate: 1.0.2 dev: false - /@storybook/core-client@6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@4.46.0): + /@storybook/core-client@6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@4.47.0): resolution: {integrity: sha512-14IRaDrVtKrQ+gNWC0wPwkCNfkZOKghYV/swCUnQX3rP99defsZK8Hc7xHIYoAiOP5+sc3sweRAxgmFiJeQ1Ig==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -12490,10 +12512,10 @@ packages: typescript: 4.9.4 unfetch: 4.2.0 util-deprecate: 1.0.2 - webpack: 4.46.0 + webpack: 4.47.0 dev: false - /@storybook/core-client@6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@5.88.2): + /@storybook/core-client@6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@5.89.0): resolution: {integrity: sha512-14IRaDrVtKrQ+gNWC0wPwkCNfkZOKghYV/swCUnQX3rP99defsZK8Hc7xHIYoAiOP5+sc3sweRAxgmFiJeQ1Ig==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -12527,7 +12549,7 @@ packages: typescript: 4.9.4 unfetch: 4.2.0 util-deprecate: 1.0.2 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /@storybook/core-common@6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4): @@ -12566,7 +12588,7 @@ packages: '@storybook/semver': 7.3.2 '@types/node': 16.18.65 '@types/pretty-hrtime': 1.0.1 - babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@4.46.0) + babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@4.47.0) babel-plugin-macros: 3.1.0 babel-plugin-polyfill-corejs3: 0.1.7(@babel/core@7.22.11) chalk: 4.1.2 @@ -12574,7 +12596,7 @@ packages: express: 4.18.2 file-system-cache: 1.1.0 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.2(eslint@8.32.0)(typescript@4.9.4)(webpack@4.46.0) + fork-ts-checker-webpack-plugin: 6.5.2(eslint@8.32.0)(typescript@4.9.4)(webpack@4.47.0) fs-extra: 9.1.0 glob: 7.2.3 handlebars: 4.7.7 @@ -12592,7 +12614,7 @@ packages: ts-dedent: 2.2.0 typescript: 4.9.4 util-deprecate: 1.0.2 - webpack: 4.46.0 + webpack: 4.47.0 transitivePeerDependencies: - eslint - supports-color @@ -12626,7 +12648,7 @@ packages: '@discoveryjs/json-ext': 0.5.7 '@storybook/builder-webpack4': 6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) '@storybook/builder-webpack5': 6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) - '@storybook/core-client': 6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@4.46.0) + '@storybook/core-client': 6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@4.47.0) '@storybook/core-common': 6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) '@storybook/core-events': 6.5.16 '@storybook/csf': 0.0.2--canary.4566f4d.1 @@ -12670,7 +12692,7 @@ packages: typescript: 4.9.4 util-deprecate: 1.0.2 watchpack: 2.4.0 - webpack: 4.46.0 + webpack: 4.47.0 ws: 8.12.0 x-default-browser: 0.4.0 transitivePeerDependencies: @@ -12686,7 +12708,7 @@ packages: - webpack-command dev: false - /@storybook/core@6.5.16(@storybook/builder-webpack5@6.5.16)(@storybook/manager-webpack5@6.5.16)(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@5.88.2): + /@storybook/core@6.5.16(@storybook/builder-webpack5@6.5.16)(@storybook/manager-webpack5@6.5.16)(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@5.89.0): resolution: {integrity: sha512-CEF3QFTsm/VMnMKtRNr4rRdLeIkIG0g1t26WcmxTdSThNPBd8CsWzQJ7Jqu7CKiut+MU4A1LMOwbwCE5F2gmyA==} peerDependencies: '@storybook/builder-webpack5': '*' @@ -12704,13 +12726,13 @@ packages: optional: true dependencies: '@storybook/builder-webpack5': 6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) - '@storybook/core-client': 6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@5.88.2) + '@storybook/core-client': 6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@5.89.0) '@storybook/core-server': 6.5.16(@storybook/builder-webpack5@6.5.16)(@storybook/manager-webpack5@6.5.16)(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) '@storybook/manager-webpack5': 6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) react: 17.0.2 react-dom: 17.0.2(react@17.0.2) typescript: 4.9.4 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) transitivePeerDependencies: - '@storybook/mdx2-csf' - bluebird @@ -12786,23 +12808,23 @@ packages: '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.22.11) '@babel/preset-react': 7.18.6(@babel/core@7.22.11) '@storybook/addons': 6.5.16(react-dom@17.0.2)(react@17.0.2) - '@storybook/core-client': 6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@4.46.0) + '@storybook/core-client': 6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@4.47.0) '@storybook/core-common': 6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) '@storybook/node-logger': 6.5.16 '@storybook/theming': 6.5.16(react-dom@17.0.2)(react@17.0.2) '@storybook/ui': 6.5.16(react-dom@17.0.2)(react@17.0.2) '@types/node': 16.18.65 '@types/webpack': 4.41.33 - babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@4.46.0) + babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@4.47.0) case-sensitive-paths-webpack-plugin: 2.4.0 chalk: 4.1.2 core-js: 3.27.2 - css-loader: 3.6.0(webpack@4.46.0) + css-loader: 3.6.0(webpack@4.47.0) express: 4.18.2 - file-loader: 6.2.0(webpack@4.46.0) + file-loader: 6.2.0(webpack@4.47.0) find-up: 5.0.0 fs-extra: 9.1.0 - html-webpack-plugin: 4.5.2(webpack@4.46.0) + html-webpack-plugin: 4.5.2(webpack@4.47.0) node-fetch: 2.6.8 pnp-webpack-plugin: 1.6.4(typescript@4.9.4) react: 17.0.2 @@ -12810,15 +12832,15 @@ packages: read-pkg-up: 7.0.1 regenerator-runtime: 0.13.11 resolve-from: 5.0.0 - style-loader: 1.3.0(webpack@4.46.0) + style-loader: 1.3.0(webpack@4.47.0) telejson: 6.0.8 - terser-webpack-plugin: 4.2.3(webpack@4.46.0) + terser-webpack-plugin: 4.2.3(webpack@4.47.0) ts-dedent: 2.2.0 typescript: 4.9.4 - url-loader: 4.1.1(file-loader@6.2.0)(webpack@4.46.0) + url-loader: 4.1.1(file-loader@6.2.0)(webpack@4.47.0) util-deprecate: 1.0.2 - webpack: 4.46.0 - webpack-dev-middleware: 3.7.3(webpack@4.46.0) + webpack: 4.47.0 + webpack-dev-middleware: 3.7.3(webpack@4.47.0) webpack-virtual-modules: 0.2.2 transitivePeerDependencies: - bluebird @@ -12844,21 +12866,21 @@ packages: '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.22.11) '@babel/preset-react': 7.18.6(@babel/core@7.22.11) '@storybook/addons': 6.5.16(react-dom@17.0.2)(react@17.0.2) - '@storybook/core-client': 6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@5.88.2) + '@storybook/core-client': 6.5.16(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@5.89.0) '@storybook/core-common': 6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) '@storybook/node-logger': 6.5.16 '@storybook/theming': 6.5.16(react-dom@17.0.2)(react@17.0.2) '@storybook/ui': 6.5.16(react-dom@17.0.2)(react@17.0.2) '@types/node': 14.18.36 - babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@5.88.2) + babel-loader: 8.3.0(@babel/core@7.22.11)(webpack@5.89.0) case-sensitive-paths-webpack-plugin: 2.4.0 chalk: 4.1.2 core-js: 3.27.2 - css-loader: 5.2.7(webpack@5.88.2) + css-loader: 5.2.7(webpack@5.89.0) express: 4.18.2 find-up: 5.0.0 fs-extra: 9.1.0 - html-webpack-plugin: 5.5.0(webpack@5.88.2) + html-webpack-plugin: 5.5.0(webpack@5.89.0) node-fetch: 2.6.8 process: 0.11.10 react: 17.0.2 @@ -12866,14 +12888,14 @@ packages: read-pkg-up: 7.0.1 regenerator-runtime: 0.13.11 resolve-from: 5.0.0 - style-loader: 2.0.0(webpack@5.88.2) + style-loader: 2.0.0(webpack@5.89.0) telejson: 6.0.8 - terser-webpack-plugin: 5.3.6(webpack@5.88.2) + terser-webpack-plugin: 5.3.6(webpack@5.89.0) ts-dedent: 2.2.0 typescript: 4.9.4 util-deprecate: 1.0.2 - webpack: 5.88.2 - webpack-dev-middleware: 4.3.0(webpack@5.88.2) + webpack: 5.89.0(webpack-cli@4.10.0) + webpack-dev-middleware: 4.3.0(webpack@5.89.0) webpack-virtual-modules: 0.4.6 transitivePeerDependencies: - '@swc/core' @@ -12967,7 +12989,7 @@ packages: util-deprecate: 1.0.2 dev: false - /@storybook/react-docgen-typescript-plugin@1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0(typescript@4.9.4)(webpack@5.88.2): + /@storybook/react-docgen-typescript-plugin@1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0(typescript@4.9.4)(webpack@5.89.0): resolution: {integrity: sha512-eVg3BxlOm2P+chijHBTByr90IZVUtgRW56qEOLX7xlww2NBuKrcavBlcmn+HH7GIUktquWkMPtvy6e0W0NgA5w==} peerDependencies: typescript: '>= 3.x' @@ -12981,7 +13003,7 @@ packages: react-docgen-typescript: 2.2.2(typescript@4.9.4) tslib: 2.4.1 typescript: 4.9.4 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) transitivePeerDependencies: - supports-color dev: false @@ -13017,17 +13039,17 @@ packages: '@babel/core': 7.20.12 '@babel/preset-flow': 7.18.6(@babel/core@7.20.12) '@babel/preset-react': 7.18.6(@babel/core@7.20.12) - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.0)(react-refresh@0.11.0)(webpack@5.88.2) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.0)(react-refresh@0.11.0)(webpack@5.89.0) '@storybook/addons': 6.5.16(react-dom@17.0.2)(react@17.0.2) '@storybook/builder-webpack5': 6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) '@storybook/client-logger': 6.5.16 - '@storybook/core': 6.5.16(@storybook/builder-webpack5@6.5.16)(@storybook/manager-webpack5@6.5.16)(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@5.88.2) + '@storybook/core': 6.5.16(@storybook/builder-webpack5@6.5.16)(@storybook/manager-webpack5@6.5.16)(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4)(webpack@5.89.0) '@storybook/core-common': 6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/docs-tools': 6.5.16(react-dom@17.0.2)(react@17.0.2) '@storybook/manager-webpack5': 6.5.16(eslint@8.32.0)(react-dom@17.0.2)(react@17.0.2)(typescript@4.9.4) '@storybook/node-logger': 6.5.16 - '@storybook/react-docgen-typescript-plugin': 1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0(typescript@4.9.4)(webpack@5.88.2) + '@storybook/react-docgen-typescript-plugin': 1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0(typescript@4.9.4)(webpack@5.89.0) '@storybook/semver': 7.3.2 '@storybook/store': 6.5.16(react-dom@17.0.2)(react@17.0.2) '@types/estree': 0.0.51 @@ -13055,7 +13077,7 @@ packages: ts-dedent: 2.2.0 typescript: 4.9.4 util-deprecate: 1.0.2 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) transitivePeerDependencies: - '@storybook/mdx2-csf' - '@swc/core' @@ -13662,7 +13684,7 @@ packages: resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} dependencies: '@types/eslint': 8.37.0 - '@types/estree': 1.0.1 + '@types/estree': 1.0.5 /@types/eslint@8.37.0: resolution: {integrity: sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==} @@ -13673,8 +13695,8 @@ packages: /@types/eslint@8.4.10: resolution: {integrity: sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==} dependencies: - '@types/estree': 0.0.51 - '@types/json-schema': 7.0.11 + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 dev: false /@types/estree@0.0.50: @@ -13687,6 +13709,9 @@ packages: /@types/estree@1.0.1: resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + /@types/express-serve-static-core@4.17.31: resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==} dependencies: @@ -13942,6 +13967,9 @@ packages: /@types/json-schema@7.0.11: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + /@types/json-stable-stringify@1.0.34: resolution: {integrity: sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw==} @@ -14494,7 +14522,7 @@ packages: dependencies: '@types/node': 18.15.3 tapable: 2.2.1 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) transitivePeerDependencies: - '@swc/core' - esbuild @@ -14637,7 +14665,7 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.15 '@types/semver': 7.3.13 '@typescript-eslint/scope-manager': 5.49.0 '@typescript-eslint/types': 5.49.0 @@ -15051,14 +15079,14 @@ packages: '@xtuc/long': 4.2.2 dev: false - /@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0)(webpack@5.75.0): + /@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0)(webpack@5.89.0): resolution: {integrity: sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==} peerDependencies: webpack: 4.x.x || 5.x.x webpack-cli: 4.x.x dependencies: - webpack: 5.75.0(webpack-cli@4.10.0) - webpack-cli: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.75.0) + webpack: 5.89.0(webpack-cli@4.10.0) + webpack-cli: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.89.0) /@webpack-cli/info@1.5.0(webpack-cli@4.10.0): resolution: {integrity: sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==} @@ -15066,7 +15094,7 @@ packages: webpack-cli: 4.x.x dependencies: envinfo: 7.8.1 - webpack-cli: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.75.0) + webpack-cli: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.89.0) /@webpack-cli/serve@1.7.0(webpack-cli@4.10.0)(webpack-dev-server@4.11.1): resolution: {integrity: sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==} @@ -15077,8 +15105,8 @@ packages: webpack-dev-server: optional: true dependencies: - webpack-cli: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.75.0) - webpack-dev-server: 4.11.1(webpack-cli@4.10.0)(webpack@5.75.0) + webpack-cli: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.89.0) + webpack-dev-server: 4.11.1(webpack-cli@4.10.0)(webpack@5.89.0) /@whatwg-node/fetch@0.6.2: resolution: {integrity: sha512-fCUycF1W+bI6XzwJFnbdDuxIldfKM3w8+AzVCLGlucm0D+AQ8ZMm2j84hdcIhfV6ZdE4Y1HFVrHosAxdDZ+nPw==} @@ -15256,7 +15284,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) transitivePeerDependencies: - supports-color dev: true @@ -15657,7 +15685,7 @@ packages: engines: {node: '>= 6'} dependencies: glob: 7.2.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 lazystream: 1.0.1 lodash.defaults: 4.2.0 lodash.difference: 4.5.0 @@ -16021,7 +16049,7 @@ packages: babel-plugin-istanbul: 6.1.1 babel-preset-jest: 29.5.0(@babel/core@7.22.11) chalk: 4.1.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color @@ -16039,9 +16067,24 @@ packages: loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.75.0(webpack-cli@4.10.0) + webpack: 5.75.0 + + /babel-loader@8.3.0(@babel/core@7.20.12)(webpack@5.89.0): + resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==} + engines: {node: '>= 8.9'} + peerDependencies: + '@babel/core': ^7.0.0 + webpack: '>=2' + dependencies: + '@babel/core': 7.20.12 + find-cache-dir: 3.3.2 + loader-utils: 2.0.4 + make-dir: 3.1.0 + schema-utils: 2.7.1 + webpack: 5.89.0(webpack-cli@4.10.0) + dev: true - /babel-loader@8.3.0(@babel/core@7.22.11)(webpack@4.46.0): + /babel-loader@8.3.0(@babel/core@7.22.11)(webpack@4.47.0): resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==} engines: {node: '>= 8.9'} peerDependencies: @@ -16053,10 +16096,10 @@ packages: loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 4.46.0 + webpack: 4.47.0 dev: false - /babel-loader@8.3.0(@babel/core@7.22.11)(webpack@5.88.2): + /babel-loader@8.3.0(@babel/core@7.22.11)(webpack@5.89.0): resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==} engines: {node: '>= 8.9'} peerDependencies: @@ -16068,7 +16111,7 @@ packages: loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /babel-plugin-add-react-displayname@0.0.5: @@ -16843,7 +16886,7 @@ packages: chownr: 1.1.4 figgy-pudding: 3.5.2 glob: 7.2.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 infer-owner: 1.0.4 lru-cache: 5.1.1 mississippi: 3.0.0 @@ -17221,7 +17264,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -17603,7 +17646,7 @@ packages: engines: {node: '>=8'} dependencies: dot-prop: 5.3.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 make-dir: 3.1.0 unique-string: 2.0.0 write-file-atomic: 3.0.3 @@ -17715,7 +17758,7 @@ packages: toggle-selection: 1.0.6 dev: false - /copy-webpack-plugin@11.0.0(webpack@5.88.2): + /copy-webpack-plugin@11.0.0(webpack@5.89.0): resolution: {integrity: sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -17725,9 +17768,9 @@ packages: glob-parent: 6.0.2 globby: 13.1.3 normalize-path: 3.0.0 - schema-utils: 4.0.0 + schema-utils: 4.2.0 serialize-javascript: 6.0.1 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /core-js-compat@3.27.2: @@ -17833,7 +17876,7 @@ packages: resolution: {integrity: sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw==} engines: {node: '>=8'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 make-dir: 3.1.0 nested-error-stacks: 2.1.1 p-event: 4.2.0 @@ -17989,7 +18032,7 @@ packages: postcss: 8.4.21 dev: false - /css-loader@3.6.0(webpack@4.46.0): + /css-loader@3.6.0(webpack@4.47.0): resolution: {integrity: sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==} engines: {node: '>= 8.9.0'} peerDependencies: @@ -18008,10 +18051,10 @@ packages: postcss-value-parser: 4.2.0 schema-utils: 2.7.1 semver: 6.3.1 - webpack: 4.46.0 + webpack: 4.47.0 dev: false - /css-loader@5.2.7(webpack@5.88.2): + /css-loader@5.2.7(webpack@5.89.0): resolution: {integrity: sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -18025,12 +18068,12 @@ packages: postcss-modules-scope: 3.0.0(postcss@8.4.21) postcss-modules-values: 4.0.0(postcss@8.4.21) postcss-value-parser: 4.2.0 - schema-utils: 3.1.1 + schema-utils: 3.3.0 semver: 7.3.8 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false - /css-loader@6.7.3(webpack@5.75.0): + /css-loader@6.7.3(webpack@5.89.0): resolution: {integrity: sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==} engines: {node: '>= 12.13.0'} peerDependencies: @@ -18044,27 +18087,9 @@ packages: postcss-modules-values: 4.0.0(postcss@8.4.21) postcss-value-parser: 4.2.0 semver: 7.3.8 - webpack: 5.75.0(webpack-cli@4.10.0) - dev: true + webpack: 5.89.0(webpack-cli@4.10.0) - /css-loader@6.7.3(webpack@5.88.2): - resolution: {integrity: sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 - dependencies: - icss-utils: 5.1.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.21) - postcss-modules-local-by-default: 4.0.0(postcss@8.4.21) - postcss-modules-scope: 3.0.0(postcss@8.4.21) - postcss-modules-values: 4.0.0(postcss@8.4.21) - postcss-value-parser: 4.2.0 - semver: 7.3.8 - webpack: 5.88.2 - dev: false - - /css-minimizer-webpack-plugin@4.2.2(clean-css@5.3.2)(webpack@5.88.2): + /css-minimizer-webpack-plugin@4.2.2(clean-css@5.3.2)(webpack@5.89.0): resolution: {integrity: sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -18093,10 +18118,10 @@ packages: cssnano: 5.1.15(postcss@8.4.21) jest-worker: 29.5.0 postcss: 8.4.21 - schema-utils: 4.0.0 + schema-utils: 4.2.0 serialize-javascript: 6.0.1 source-map: 0.6.1 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /css-select@1.2.0: @@ -18536,7 +18561,7 @@ packages: engines: {node: '>=10'} dependencies: globby: 11.1.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 is-glob: 4.0.3 is-path-cwd: 2.2.0 is-path-inside: 3.0.3 @@ -19142,7 +19167,7 @@ packages: resolution: {integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==} engines: {node: '>=6.9.0'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 memory-fs: 0.5.0 tapable: 1.1.3 @@ -19157,7 +19182,7 @@ packages: resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} engines: {node: '>=10.13.0'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 tapable: 2.2.1 /enquirer@2.3.6: @@ -19278,6 +19303,9 @@ packages: /es-module-lexer@1.3.0: resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==} + /es-module-lexer@1.4.1: + resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} + /es-set-tostringtag@2.0.1: resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} engines: {node: '>= 0.4'} @@ -19397,7 +19425,7 @@ packages: eslint-plugin-import: '*' dependencies: debug: 4.3.4(supports-color@9.3.1) - enhanced-resolve: 5.12.0 + enhanced-resolve: 5.15.0 eslint: 8.32.0 eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.49.0)(eslint-import-resolver-typescript@3.5.3)(eslint@8.32.0) get-tsconfig: 4.3.0 @@ -19683,7 +19711,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 @@ -20272,26 +20300,26 @@ packages: dependencies: flat-cache: 3.0.4 - /file-loader@6.2.0(webpack@4.46.0): + /file-loader@6.2.0(webpack@4.47.0): resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} engines: {node: '>= 10.13.0'} peerDependencies: webpack: ^4.0.0 || ^5.0.0 dependencies: loader-utils: 2.0.4 - schema-utils: 3.1.1 - webpack: 4.46.0 + schema-utils: 3.3.0 + webpack: 4.47.0 dev: false - /file-loader@6.2.0(webpack@5.88.2): + /file-loader@6.2.0(webpack@5.89.0): resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} engines: {node: '>= 10.13.0'} peerDependencies: webpack: ^4.0.0 || ^5.0.0 dependencies: loader-utils: 2.0.4 - schema-utils: 3.1.1 - webpack: 5.88.2 + schema-utils: 3.3.0 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /file-saver@2.0.5: @@ -20522,7 +20550,7 @@ packages: /forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - /fork-ts-checker-webpack-plugin@4.1.6(eslint@8.32.0)(typescript@4.9.4)(webpack@4.46.0): + /fork-ts-checker-webpack-plugin@4.1.6(eslint@8.32.0)(typescript@4.9.4)(webpack@4.47.0): resolution: {integrity: sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==} engines: {node: '>=6.11.5', yarn: '>=1.0.0'} peerDependencies: @@ -20544,13 +20572,13 @@ packages: semver: 5.7.1 tapable: 1.1.3 typescript: 4.9.4 - webpack: 4.46.0 + webpack: 4.47.0 worker-rpc: 0.1.1 transitivePeerDependencies: - supports-color dev: false - /fork-ts-checker-webpack-plugin@6.5.2(eslint@8.32.0)(typescript@4.9.4)(webpack@4.46.0): + /fork-ts-checker-webpack-plugin@6.5.2(eslint@8.32.0)(typescript@4.9.4)(webpack@4.47.0): resolution: {integrity: sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==} engines: {node: '>=10', yarn: '>=1.0.0'} peerDependencies: @@ -20565,7 +20593,7 @@ packages: optional: true dependencies: '@babel/code-frame': 7.18.6 - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.15 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 6.0.0 @@ -20579,10 +20607,10 @@ packages: semver: 7.3.8 tapable: 1.1.3 typescript: 4.9.4 - webpack: 4.46.0 + webpack: 4.47.0 dev: false - /fork-ts-checker-webpack-plugin@6.5.2(eslint@8.32.0)(typescript@4.9.4)(webpack@5.88.2): + /fork-ts-checker-webpack-plugin@6.5.2(eslint@8.32.0)(typescript@4.9.4)(webpack@5.89.0): resolution: {integrity: sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==} engines: {node: '>=10', yarn: '>=1.0.0'} peerDependencies: @@ -20597,7 +20625,7 @@ packages: optional: true dependencies: '@babel/code-frame': 7.18.6 - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.15 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 6.0.0 @@ -20611,7 +20639,7 @@ packages: semver: 7.3.8 tapable: 1.1.3 typescript: 4.9.4 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /fork-ts-checker-webpack-plugin@7.2.14(typescript@4.9.4)(webpack@5.75.0): @@ -20634,11 +20662,11 @@ packages: memfs: 3.4.13 minimatch: 3.1.2 node-abort-controller: 3.0.1 - schema-utils: 3.1.1 + schema-utils: 3.3.0 semver: 7.3.8 tapable: 2.2.1 typescript: 4.9.4 - webpack: 5.75.0(webpack-cli@4.10.0) + webpack: 5.75.0 dev: true /form-data-encoder@1.7.2: @@ -20728,7 +20756,7 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.0 @@ -20745,7 +20773,7 @@ packages: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} engines: {node: '>=14.14'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.0 @@ -20753,7 +20781,7 @@ packages: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 @@ -20761,7 +20789,7 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 @@ -20795,7 +20823,7 @@ packages: /fs-write-stream-atomic@1.0.10: resolution: {integrity: sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 iferr: 0.1.5 imurmurhash: 0.1.4 readable-stream: 2.3.7 @@ -20816,8 +20844,8 @@ packages: dev: false optional: true - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true @@ -20827,7 +20855,7 @@ packages: resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} engines: {node: '>=0.6'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 inherits: 2.0.4 mkdirp: 0.5.6 rimraf: 2.7.1 @@ -21228,6 +21256,9 @@ packages: /graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + /grapheme-splitter@1.0.4: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} @@ -21729,7 +21760,7 @@ packages: resolution: {integrity: sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==} dev: false - /html-webpack-plugin@4.5.2(webpack@4.46.0): + /html-webpack-plugin@4.5.2(webpack@4.47.0): resolution: {integrity: sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==} engines: {node: '>=6.9'} peerDependencies: @@ -21744,24 +21775,10 @@ packages: pretty-error: 2.1.2 tapable: 1.1.3 util.promisify: 1.0.0 - webpack: 4.46.0 + webpack: 4.47.0 dev: false - /html-webpack-plugin@5.5.0(webpack@5.75.0): - resolution: {integrity: sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==} - engines: {node: '>=10.13.0'} - peerDependencies: - webpack: ^5.20.0 - dependencies: - '@types/html-minifier-terser': 6.1.0 - html-minifier-terser: 6.1.0 - lodash: 4.17.21 - pretty-error: 4.0.0 - tapable: 2.2.1 - webpack: 5.75.0(webpack-cli@4.10.0) - dev: true - - /html-webpack-plugin@5.5.0(webpack@5.88.2): + /html-webpack-plugin@5.5.0(webpack@5.89.0): resolution: {integrity: sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==} engines: {node: '>=10.13.0'} peerDependencies: @@ -21772,8 +21789,7 @@ packages: lodash: 4.17.21 pretty-error: 4.0.0 tapable: 2.2.1 - webpack: 5.88.2 - dev: false + webpack: 5.89.0(webpack-cli@4.10.0) /htmlparser2@3.10.1: resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} @@ -21843,7 +21859,7 @@ packages: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) transitivePeerDependencies: - supports-color dev: true @@ -21904,7 +21920,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) transitivePeerDependencies: - supports-color dev: true @@ -22916,7 +22932,7 @@ packages: '@jest/types': 29.5.0 chalk: 4.1.2 exit: 0.1.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 import-local: 3.1.0 jest-config: 29.5.0(@types/node@18.15.3)(ts-node@10.9.1) jest-util: 29.5.0 @@ -22950,7 +22966,7 @@ packages: ci-info: 3.7.1 deepmerge: 4.2.2 glob: 7.2.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-circus: 29.5.0 jest-environment-node: 29.5.0 jest-get-type: 29.4.3 @@ -23061,7 +23077,7 @@ packages: '@types/node': 18.15.3 anymatch: 3.1.3 fb-watchman: 2.0.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-regex-util: 26.0.0 jest-serializer: 26.6.2 jest-util: 26.6.2 @@ -23070,7 +23086,7 @@ packages: sane: 4.1.0 walker: 1.0.8 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 transitivePeerDependencies: - supports-color dev: false @@ -23084,14 +23100,14 @@ packages: '@types/node': 18.15.3 anymatch: 3.1.3 fb-watchman: 2.0.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-regex-util: 29.4.3 jest-util: 29.5.0 jest-worker: 29.5.0 micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /jest-junit@15.0.0: @@ -23130,7 +23146,7 @@ packages: '@jest/types': 29.5.0 '@types/stack-utils': 2.0.1 chalk: 4.1.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 micromatch: 4.0.5 pretty-format: 29.5.0 slash: 3.0.0 @@ -23183,7 +23199,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-haste-map: 29.5.0 jest-pnp-resolver: 1.2.3(jest-resolve@29.5.0) jest-util: 29.5.0 @@ -23205,7 +23221,7 @@ packages: '@types/node': 18.15.3 chalk: 4.1.2 emittery: 0.13.1 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-docblock: 29.4.3 jest-environment-node: 29.5.0 jest-haste-map: 29.5.0 @@ -23238,7 +23254,7 @@ packages: cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 glob: 7.2.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-haste-map: 29.5.0 jest-message-util: 29.5.0 jest-mock: 29.5.0 @@ -23257,7 +23273,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@types/node': 18.15.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: false /jest-snapshot@29.5.0: @@ -23278,7 +23294,7 @@ packages: babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.11) chalk: 4.1.2 expect: 29.5.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-diff: 29.5.0 jest-get-type: 29.4.3 jest-matcher-utils: 29.5.0 @@ -23298,7 +23314,7 @@ packages: '@jest/types': 26.6.2 '@types/node': 18.15.3 chalk: 4.1.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 is-ci: 2.0.0 micromatch: 4.0.5 dev: false @@ -23311,7 +23327,7 @@ packages: '@types/node': 18.15.3 chalk: 4.1.2 ci-info: 3.7.1 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 picomatch: 2.3.1 /jest-validate@29.5.0: @@ -23406,6 +23422,12 @@ packages: /jose@4.11.2: resolution: {integrity: sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A==} + dev: false + + /jose@4.15.4: + resolution: {integrity: sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==} + requiresBuild: true + optional: true /jpeg-js@0.4.4: resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} @@ -23560,14 +23582,14 @@ packages: /jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: universalify: 2.0.0 optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 /jsonify@0.0.1: resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} @@ -23945,7 +23967,7 @@ packages: engines: {node: '>=0.10.0'} requiresBuild: true dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 parse-json: 2.2.0 pify: 2.3.0 pinkie-promise: 2.0.1 @@ -23957,7 +23979,7 @@ packages: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 parse-json: 4.0.0 pify: 3.0.0 strip-bom: 3.0.0 @@ -23967,7 +23989,7 @@ packages: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} engines: {node: '>=6'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 @@ -24693,14 +24715,14 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - /mini-css-extract-plugin@2.7.6(webpack@5.88.2): + /mini-css-extract-plugin@2.7.6(webpack@5.89.0): resolution: {integrity: sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 dependencies: - schema-utils: 4.0.0 - webpack: 5.88.2 + schema-utils: 4.2.0 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /minimalistic-assert@1.0.1: @@ -25423,8 +25445,8 @@ packages: /obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - /oidc-token-hash@5.0.1: - resolution: {integrity: sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==} + /oidc-token-hash@5.0.3: + resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} engines: {node: ^10.13.0 || >=12.0.0} requiresBuild: true optional: true @@ -25475,14 +25497,14 @@ packages: hasBin: true dev: false - /openid-client@5.4.0: - resolution: {integrity: sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ==} + /openid-client@5.6.4: + resolution: {integrity: sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA==} requiresBuild: true dependencies: - jose: 4.11.2 + jose: 4.15.4 lru-cache: 6.0.0 object-hash: 2.2.0 - oidc-token-hash: 5.0.1 + oidc-token-hash: 5.0.3 optional: true /opentracing@0.14.7: @@ -25948,7 +25970,7 @@ packages: engines: {node: '>=0.10.0'} requiresBuild: true dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 pify: 2.3.0 pinkie-promise: 2.0.1 dev: false @@ -26276,7 +26298,7 @@ packages: postcss: 7.0.39 dev: false - /postcss-loader@4.3.0(postcss@7.0.39)(webpack@4.46.0): + /postcss-loader@4.3.0(postcss@7.0.39)(webpack@4.47.0): resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -26287,12 +26309,12 @@ packages: klona: 2.0.6 loader-utils: 2.0.4 postcss: 7.0.39 - schema-utils: 3.1.1 + schema-utils: 3.3.0 semver: 7.3.8 - webpack: 4.46.0 + webpack: 4.47.0 dev: false - /postcss-loader@7.3.3(postcss@8.4.21)(webpack@5.88.2): + /postcss-loader@7.3.3(postcss@8.4.21)(webpack@5.89.0): resolution: {integrity: sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -26303,7 +26325,7 @@ packages: jiti: 1.19.1 postcss: 8.4.21 semver: 7.3.8 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /postcss-merge-idents@5.1.1(postcss@8.4.21): @@ -27107,7 +27129,7 @@ packages: iconv-lite: 0.4.24 unpipe: 1.0.0 - /raw-loader@4.0.2(webpack@4.46.0): + /raw-loader@4.0.2(webpack@4.47.0): resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -27115,7 +27137,7 @@ packages: dependencies: loader-utils: 2.0.4 schema-utils: 3.1.1 - webpack: 4.46.0 + webpack: 4.47.0 dev: false /raw-loader@4.0.2(webpack@5.88.2): @@ -27202,7 +27224,7 @@ packages: shallow-equal: 1.2.1 dev: false - /react-dev-utils@12.0.1(typescript@4.9.4)(webpack@5.88.2): + /react-dev-utils@12.0.1(typescript@4.9.4)(webpack@5.89.0): resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==} engines: {node: '>=14'} peerDependencies: @@ -27221,7 +27243,7 @@ packages: escape-string-regexp: 4.0.0 filesize: 8.0.7 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.2(eslint@8.32.0)(typescript@4.9.4)(webpack@5.88.2) + fork-ts-checker-webpack-plugin: 6.5.2(eslint@8.32.0)(typescript@4.9.4)(webpack@5.89.0) global-modules: 2.0.0 globby: 11.1.0 gzip-size: 6.0.0 @@ -27237,7 +27259,7 @@ packages: strip-ansi: 6.0.1 text-table: 0.2.0 typescript: 4.9.4 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) transitivePeerDependencies: - eslint - supports-color @@ -27526,7 +27548,7 @@ packages: unescape: 1.0.1 dev: false - /react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@5.5.2)(webpack@5.88.2): + /react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@5.5.2)(webpack@5.89.0): resolution: {integrity: sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==} engines: {node: '>=10.13.0'} peerDependencies: @@ -27535,7 +27557,7 @@ packages: dependencies: '@babel/runtime': 7.20.13 react-loadable: /@docusaurus/react-loadable@5.5.2(react@17.0.2) - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /react-refresh@0.11.0: @@ -27777,7 +27799,7 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 @@ -27822,7 +27844,7 @@ packages: engines: {node: '>=0.10'} requiresBuild: true dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 micromatch: 3.1.10 readable-stream: 2.3.7 transitivePeerDependencies: @@ -28183,7 +28205,7 @@ packages: resolution: {integrity: sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==} engines: {node: '>=6'} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@9.3.1) module-details-from-path: 1.0.3 resolve: 1.22.1 transitivePeerDependencies: @@ -28448,7 +28470,7 @@ packages: resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} engines: {node: '>= 8.9.0'} dependencies: - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) dev: false @@ -28465,7 +28487,7 @@ packages: resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==} engines: {node: '>= 10.13.0'} dependencies: - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) @@ -28473,7 +28495,7 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) @@ -28486,6 +28508,16 @@ packages: ajv-formats: 2.1.1 ajv-keywords: 5.1.0(ajv@8.12.0) + /schema-utils@4.2.0: + resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==} + engines: {node: '>= 12.13.0'} + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.12.0 + ajv-formats: 2.1.1 + ajv-keywords: 5.1.0(ajv@8.12.0) + dev: false + /scroll-into-view-if-needed@2.2.31: resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} dependencies: @@ -28589,6 +28621,11 @@ packages: dependencies: randombytes: 2.1.0 + /serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + dependencies: + randombytes: 2.1.0 + /serve-favicon@2.5.0: resolution: {integrity: sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA==} engines: {node: '>= 0.8.0'} @@ -29435,7 +29472,7 @@ packages: peek-readable: 4.1.0 dev: false - /style-loader@1.3.0(webpack@4.46.0): + /style-loader@1.3.0(webpack@4.47.0): resolution: {integrity: sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==} engines: {node: '>= 8.9.0'} peerDependencies: @@ -29443,27 +29480,27 @@ packages: dependencies: loader-utils: 2.0.4 schema-utils: 2.7.1 - webpack: 4.46.0 + webpack: 4.47.0 dev: false - /style-loader@2.0.0(webpack@5.88.2): + /style-loader@2.0.0(webpack@5.89.0): resolution: {integrity: sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==} engines: {node: '>= 10.13.0'} peerDependencies: webpack: ^4.0.0 || ^5.0.0 dependencies: loader-utils: 2.0.4 - schema-utils: 3.1.1 - webpack: 5.88.2 + schema-utils: 3.3.0 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false - /style-loader@3.3.1(webpack@5.75.0): + /style-loader@3.3.1(webpack@5.89.0): resolution: {integrity: sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 dependencies: - webpack: 5.75.0(webpack-cli@4.10.0) + webpack: 5.89.0(webpack-cli@4.10.0) dev: true /style-to-object@0.3.0: @@ -29753,7 +29790,7 @@ packages: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} - /terser-webpack-plugin@1.4.5(webpack@4.46.0): + /terser-webpack-plugin@1.4.5(webpack@4.47.0): resolution: {integrity: sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==} engines: {node: '>= 6.9.0'} peerDependencies: @@ -29766,12 +29803,12 @@ packages: serialize-javascript: 4.0.0 source-map: 0.6.1 terser: 4.8.1 - webpack: 4.46.0 + webpack: 4.47.0 webpack-sources: 1.4.3 worker-farm: 1.7.0 dev: false - /terser-webpack-plugin@4.2.3(webpack@4.46.0): + /terser-webpack-plugin@4.2.3(webpack@4.47.0): resolution: {integrity: sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -29781,16 +29818,39 @@ packages: find-cache-dir: 3.3.2 jest-worker: 26.6.2 p-limit: 3.1.0 - schema-utils: 3.1.1 + schema-utils: 3.3.0 serialize-javascript: 5.0.1 source-map: 0.6.1 - terser: 5.16.1 - webpack: 4.46.0 + terser: 5.26.0 + webpack: 4.47.0 webpack-sources: 1.4.3 transitivePeerDependencies: - bluebird dev: false + /terser-webpack-plugin@5.3.10(webpack@5.89.0): + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.20 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.26.0 + webpack: 5.89.0(webpack-cli@4.10.0) + /terser-webpack-plugin@5.3.6(webpack@5.75.0): resolution: {integrity: sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==} engines: {node: '>= 10.13.0'} @@ -29812,9 +29872,9 @@ packages: schema-utils: 3.1.1 serialize-javascript: 6.0.1 terser: 5.16.1 - webpack: 5.75.0(webpack-cli@4.10.0) + webpack: 5.75.0 - /terser-webpack-plugin@5.3.6(webpack@5.88.2): + /terser-webpack-plugin@5.3.6(webpack@5.89.0): resolution: {integrity: sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -29835,7 +29895,7 @@ packages: schema-utils: 3.1.1 serialize-javascript: 6.0.1 terser: 5.16.1 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /terser-webpack-plugin@5.3.9(webpack@5.88.2): @@ -29892,6 +29952,16 @@ packages: commander: 2.20.3 source-map-support: 0.5.21 + /terser@5.26.0: + resolution: {integrity: sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.5 + acorn: 8.8.2 + commander: 2.20.3 + source-map-support: 0.5.21 + /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -30221,7 +30291,7 @@ packages: micromatch: 4.0.5 semver: 7.3.8 typescript: 4.9.4 - webpack: 5.75.0(webpack-cli@4.10.0) + webpack: 5.75.0 dev: true /ts-log@2.2.5: @@ -30297,7 +30367,7 @@ packages: engines: {node: '>=10.13.0'} dependencies: chalk: 4.1.2 - enhanced-resolve: 5.12.0 + enhanced-resolve: 5.15.0 tsconfig-paths: 4.1.2 dev: true @@ -30797,7 +30867,7 @@ packages: deprecated: Please see https://github.com/lydell/urix#deprecated dev: false - /url-loader@4.1.1(file-loader@6.2.0)(webpack@4.46.0): + /url-loader@4.1.1(file-loader@6.2.0)(webpack@4.47.0): resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -30807,14 +30877,14 @@ packages: file-loader: optional: true dependencies: - file-loader: 6.2.0(webpack@4.46.0) + file-loader: 6.2.0(webpack@4.47.0) loader-utils: 2.0.4 mime-types: 2.1.35 - schema-utils: 3.1.1 - webpack: 4.46.0 + schema-utils: 3.3.0 + webpack: 4.47.0 dev: false - /url-loader@4.1.1(file-loader@6.2.0)(webpack@5.88.2): + /url-loader@4.1.1(file-loader@6.2.0)(webpack@5.89.0): resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -30824,11 +30894,11 @@ packages: file-loader: optional: true dependencies: - file-loader: 6.2.0(webpack@5.88.2) + file-loader: 6.2.0(webpack@5.89.0) loader-utils: 2.0.4 mime-types: 2.1.35 - schema-utils: 3.1.1 - webpack: 5.88.2 + schema-utils: 3.3.0 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /url-parse-lax@3.0.0: @@ -31126,7 +31196,7 @@ packages: /watchpack@1.7.5: resolution: {integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 neo-async: 2.6.2 optionalDependencies: chokidar: 3.5.3 @@ -31140,7 +31210,7 @@ packages: engines: {node: '>=10.13.0'} dependencies: glob-to-regexp: 0.4.1 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 /wbuf@1.7.3: resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} @@ -31208,7 +31278,7 @@ packages: - utf-8-validate dev: false - /webpack-cli@4.10.0(webpack-dev-server@4.11.1)(webpack@5.75.0): + /webpack-cli@4.10.0(webpack-dev-server@4.11.1)(webpack@5.89.0): resolution: {integrity: sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==} engines: {node: '>=10.13.0'} hasBin: true @@ -31229,7 +31299,7 @@ packages: optional: true dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0)(webpack@5.75.0) + '@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0)(webpack@5.89.0) '@webpack-cli/info': 1.5.0(webpack-cli@4.10.0) '@webpack-cli/serve': 1.7.0(webpack-cli@4.10.0)(webpack-dev-server@4.11.1) colorette: 2.0.19 @@ -31239,11 +31309,11 @@ packages: import-local: 3.1.0 interpret: 2.2.0 rechoir: 0.7.1 - webpack: 5.75.0(webpack-cli@4.10.0) - webpack-dev-server: 4.11.1(webpack-cli@4.10.0)(webpack@5.75.0) + webpack: 5.89.0(webpack-cli@4.10.0) + webpack-dev-server: 4.11.1(webpack-cli@4.10.0)(webpack@5.89.0) webpack-merge: 5.8.0 - /webpack-dev-middleware@3.7.3(webpack@4.46.0): + /webpack-dev-middleware@3.7.3(webpack@4.47.0): resolution: {integrity: sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==} engines: {node: '>= 6'} peerDependencies: @@ -31253,11 +31323,11 @@ packages: mime: 2.6.0 mkdirp: 0.5.6 range-parser: 1.2.1 - webpack: 4.46.0 + webpack: 4.47.0 webpack-log: 2.0.0 dev: false - /webpack-dev-middleware@4.3.0(webpack@5.88.2): + /webpack-dev-middleware@4.3.0(webpack@5.89.0): resolution: {integrity: sha512-PjwyVY95/bhBh6VUqt6z4THplYcsvQ8YNNBTBM873xLVmw8FLeALn0qurHbs9EmcfhzQis/eoqypSnZeuUz26w==} engines: {node: '>= v10.23.3'} peerDependencies: @@ -31268,24 +31338,11 @@ packages: memfs: 3.4.13 mime-types: 2.1.35 range-parser: 1.2.1 - schema-utils: 3.1.1 - webpack: 5.88.2 + schema-utils: 3.3.0 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false - /webpack-dev-middleware@5.3.3(webpack@5.75.0): - resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - colorette: 2.0.19 - memfs: 3.4.13 - mime-types: 2.1.35 - range-parser: 1.2.1 - schema-utils: 4.0.0 - webpack: 5.75.0(webpack-cli@4.10.0) - - /webpack-dev-middleware@5.3.3(webpack@5.88.2): + /webpack-dev-middleware@5.3.3(webpack@5.89.0): resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} engines: {node: '>= 12.13.0'} peerDependencies: @@ -31296,10 +31353,9 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.0.0 - webpack: 5.88.2 - dev: false + webpack: 5.89.0(webpack-cli@4.10.0) - /webpack-dev-server@4.11.1(webpack-cli@4.10.0)(webpack@5.75.0): + /webpack-dev-server@4.11.1(webpack-cli@4.10.0)(webpack@5.89.0): resolution: {integrity: sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==} engines: {node: '>= 12.13.0'} hasBin: true @@ -31337,9 +31393,9 @@ packages: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.75.0(webpack-cli@4.10.0) - webpack-cli: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.75.0) - webpack-dev-middleware: 5.3.3(webpack@5.75.0) + webpack: 5.89.0(webpack-cli@4.10.0) + webpack-cli: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.89.0) + webpack-dev-middleware: 5.3.3(webpack@5.89.0) ws: 8.12.0 transitivePeerDependencies: - bufferutil @@ -31347,61 +31403,13 @@ packages: - supports-color - utf-8-validate - /webpack-dev-server@4.11.1(webpack@5.88.2): - resolution: {integrity: sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==} - engines: {node: '>= 12.13.0'} - hasBin: true - peerDependencies: - webpack: ^4.37.0 || ^5.0.0 - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - dependencies: - '@types/bonjour': 3.5.10 - '@types/connect-history-api-fallback': 1.3.5 - '@types/express': 4.17.16 - '@types/serve-index': 1.9.1 - '@types/serve-static': 1.15.0 - '@types/sockjs': 0.3.33 - '@types/ws': 8.5.4 - ansi-html-community: 0.0.8 - bonjour-service: 1.1.0 - chokidar: 3.5.3 - colorette: 2.0.19 - compression: 1.7.4 - connect-history-api-fallback: 2.0.0 - default-gateway: 6.0.3 - express: 4.18.2 - graceful-fs: 4.2.10 - html-entities: 2.3.3 - http-proxy-middleware: 2.0.6(@types/express@4.17.16) - ipaddr.js: 2.0.1 - open: 8.4.0 - p-retry: 4.6.2 - rimraf: 3.0.2 - schema-utils: 4.0.0 - selfsigned: 2.1.1 - serve-index: 1.9.1 - sockjs: 0.3.24 - spdy: 4.0.2 - webpack: 5.88.2 - webpack-dev-middleware: 5.3.3(webpack@5.88.2) - ws: 8.12.0 - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - dev: false - - /webpack-filter-warnings-plugin@1.2.1(webpack@4.46.0): + /webpack-filter-warnings-plugin@1.2.1(webpack@4.47.0): resolution: {integrity: sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==} engines: {node: '>= 4.3 < 5.0.0 || >= 5.10'} peerDependencies: webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 dependencies: - webpack: 4.46.0 + webpack: 4.47.0 dev: false /webpack-hot-middleware@2.25.3: @@ -31455,8 +31463,8 @@ packages: resolution: {integrity: sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==} dev: false - /webpack@4.46.0: - resolution: {integrity: sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==} + /webpack@4.47.0: + resolution: {integrity: sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==} engines: {node: '>=6.11.5'} hasBin: true peerDependencies: @@ -31488,14 +31496,14 @@ packages: node-libs-browser: 2.2.1 schema-utils: 1.0.0 tapable: 1.1.3 - terser-webpack-plugin: 1.4.5(webpack@4.46.0) + terser-webpack-plugin: 1.4.5(webpack@4.47.0) watchpack: 1.7.5 webpack-sources: 1.4.3 transitivePeerDependencies: - supports-color dev: false - /webpack@5.75.0(webpack-cli@4.10.0): + /webpack@5.75.0: resolution: {integrity: sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==} engines: {node: '>=10.13.0'} hasBin: true @@ -31528,7 +31536,6 @@ packages: tapable: 2.2.1 terser-webpack-plugin: 5.3.6(webpack@5.75.0) watchpack: 2.4.0 - webpack-cli: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.75.0) webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' @@ -31574,7 +31581,47 @@ packages: - esbuild - uglify-js - /webpackbar@5.0.2(webpack@5.88.2): + /webpack@5.89.0(webpack-cli@4.10.0): + resolution: {integrity: sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.4 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/wasm-edit': 1.11.6 + '@webassemblyjs/wasm-parser': 1.11.6 + acorn: 8.8.2 + acorn-import-assertions: 1.9.0(acorn@8.8.2) + browserslist: 4.21.9 + chrome-trace-event: 1.0.3 + enhanced-resolve: 5.15.0 + es-module-lexer: 1.4.1 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.10(webpack@5.89.0) + watchpack: 2.4.0 + webpack-cli: 4.10.0(webpack-dev-server@4.11.1)(webpack@5.89.0) + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + /webpackbar@5.0.2(webpack@5.89.0): resolution: {integrity: sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==} engines: {node: '>=12'} peerDependencies: @@ -31584,7 +31631,7 @@ packages: consola: 2.15.3 pretty-time: 1.1.0 std-env: 3.3.3 - webpack: 5.88.2 + webpack: 5.89.0(webpack-cli@4.10.0) dev: false /websocket-driver@0.7.4: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ada00cacbd..984202b2bf 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,4 @@ packages: - "packages/**" - - "demo/*" + - "demo/**" - "docs"