diff --git a/playground/.strapi/client/app.js b/playground/.strapi/client/app.js
new file mode 100644
index 0000000..1996af5
--- /dev/null
+++ b/playground/.strapi/client/app.js
@@ -0,0 +1,16 @@
+/**
+ * This file was automatically generated by Strapi.
+ * Any modifications made will be discarded.
+ */
+import i18N from "@strapi/plugin-i18n/strapi-admin";
+import usersPermissions from "@strapi/plugin-users-permissions/strapi-admin";
+import configSync from "strapi-plugin-config-sync/strapi-admin";
+import { renderAdmin } from "@strapi/strapi/admin";
+
+renderAdmin(document.getElementById("strapi"), {
+ plugins: {
+ i18n: i18N,
+ "users-permissions": usersPermissions,
+ "config-sync": configSync,
+ },
+});
diff --git a/playground/.strapi/client/index.html b/playground/.strapi/client/index.html
new file mode 100644
index 0000000..08d9c27
--- /dev/null
+++ b/playground/.strapi/client/index.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+ Strapi Admin
+
+
+
+
+
+
+
diff --git a/playground/config/plugins.js b/playground/config/plugins.js
index e321c68..ed733d3 100644
--- a/playground/config/plugins.js
+++ b/playground/config/plugins.js
@@ -3,7 +3,18 @@ module.exports = {
enabled: true,
config: {
importOnBootstrap: false,
- minify: true,
+ minify: false,
+ customTypes: [
+ {
+ configName: "home",
+ queryString: "api::home.home",
+ uid: ["slug"],
+ components: [
+ "Profile",
+ "Profile.ContactInfo",
+ ],
+ }
+ ]
},
},
};
diff --git a/playground/src/api/home/content-types/home/schema.json b/playground/src/api/home/content-types/home/schema.json
new file mode 100644
index 0000000..4f55a03
--- /dev/null
+++ b/playground/src/api/home/content-types/home/schema.json
@@ -0,0 +1,28 @@
+{
+ "kind": "singleType",
+ "collectionName": "homes",
+ "info": {
+ "singularName": "home",
+ "pluralName": "homes",
+ "displayName": "Home",
+ "description": ""
+ },
+ "options": {
+ "draftAndPublish": false
+ },
+ "pluginOptions": {},
+ "attributes": {
+ "title": {
+ "type": "string"
+ },
+ "Profile": {
+ "type": "component",
+ "repeatable": false,
+ "component": "core.profile"
+ },
+ "slug": {
+ "type": "uid",
+ "targetField": "title"
+ }
+ }
+}
diff --git a/playground/src/api/home/controllers/home.js b/playground/src/api/home/controllers/home.js
new file mode 100644
index 0000000..b4dc6c0
--- /dev/null
+++ b/playground/src/api/home/controllers/home.js
@@ -0,0 +1,9 @@
+'use strict';
+
+/**
+ * home controller
+ */
+
+const { createCoreController } = require('@strapi/strapi').factories;
+
+module.exports = createCoreController('api::home.home');
diff --git a/playground/src/api/home/routes/home.js b/playground/src/api/home/routes/home.js
new file mode 100644
index 0000000..63ca7fd
--- /dev/null
+++ b/playground/src/api/home/routes/home.js
@@ -0,0 +1,9 @@
+'use strict';
+
+/**
+ * home router
+ */
+
+const { createCoreRouter } = require('@strapi/strapi').factories;
+
+module.exports = createCoreRouter('api::home.home');
diff --git a/playground/src/api/home/services/home.js b/playground/src/api/home/services/home.js
new file mode 100644
index 0000000..64e6d32
--- /dev/null
+++ b/playground/src/api/home/services/home.js
@@ -0,0 +1,9 @@
+'use strict';
+
+/**
+ * home service
+ */
+
+const { createCoreService } = require('@strapi/strapi').factories;
+
+module.exports = createCoreService('api::home.home');
diff --git a/playground/src/api/page/content-types/page/schema.json b/playground/src/api/page/content-types/page/schema.json
new file mode 100644
index 0000000..f5c7e93
--- /dev/null
+++ b/playground/src/api/page/content-types/page/schema.json
@@ -0,0 +1,19 @@
+{
+ "kind": "collectionType",
+ "collectionName": "pages",
+ "info": {
+ "singularName": "page",
+ "pluralName": "pages",
+ "displayName": "Page"
+ },
+ "options": {
+ "draftAndPublish": false
+ },
+ "pluginOptions": {},
+ "attributes": {
+ "title": {
+ "type": "string",
+ "required": true
+ }
+ }
+}
diff --git a/playground/src/api/page/controllers/page.js b/playground/src/api/page/controllers/page.js
new file mode 100644
index 0000000..61fe8a7
--- /dev/null
+++ b/playground/src/api/page/controllers/page.js
@@ -0,0 +1,9 @@
+'use strict';
+
+/**
+ * page controller
+ */
+
+const { createCoreController } = require('@strapi/strapi').factories;
+
+module.exports = createCoreController('api::page.page');
diff --git a/playground/src/api/page/routes/page.js b/playground/src/api/page/routes/page.js
new file mode 100644
index 0000000..258307a
--- /dev/null
+++ b/playground/src/api/page/routes/page.js
@@ -0,0 +1,9 @@
+'use strict';
+
+/**
+ * page router
+ */
+
+const { createCoreRouter } = require('@strapi/strapi').factories;
+
+module.exports = createCoreRouter('api::page.page');
diff --git a/playground/src/api/page/services/page.js b/playground/src/api/page/services/page.js
new file mode 100644
index 0000000..d5abde4
--- /dev/null
+++ b/playground/src/api/page/services/page.js
@@ -0,0 +1,9 @@
+'use strict';
+
+/**
+ * page service
+ */
+
+const { createCoreService } = require('@strapi/strapi').factories;
+
+module.exports = createCoreService('api::page.page');
diff --git a/playground/src/components/core/contact-info.json b/playground/src/components/core/contact-info.json
new file mode 100644
index 0000000..ba435db
--- /dev/null
+++ b/playground/src/components/core/contact-info.json
@@ -0,0 +1,15 @@
+{
+ "collectionName": "components_core_contact_infos",
+ "info": {
+ "displayName": "ContactInfo"
+ },
+ "options": {},
+ "attributes": {
+ "Phonenumber": {
+ "type": "string"
+ },
+ "Email": {
+ "type": "string"
+ }
+ }
+}
diff --git a/playground/src/components/core/profile.json b/playground/src/components/core/profile.json
new file mode 100644
index 0000000..18848ef
--- /dev/null
+++ b/playground/src/components/core/profile.json
@@ -0,0 +1,18 @@
+{
+ "collectionName": "components_core_profiles",
+ "info": {
+ "displayName": "Profile",
+ "description": ""
+ },
+ "options": {},
+ "attributes": {
+ "title": {
+ "type": "string"
+ },
+ "ContactInfo": {
+ "type": "component",
+ "repeatable": true,
+ "component": "core.contact-info"
+ }
+ }
+}
diff --git a/playground/types/generated/components.d.ts b/playground/types/generated/components.d.ts
new file mode 100644
index 0000000..7fb9838
--- /dev/null
+++ b/playground/types/generated/components.d.ts
@@ -0,0 +1,33 @@
+import type { Schema, Attribute } from '@strapi/strapi';
+
+export interface CoreProfile extends Schema.Component {
+ collectionName: 'components_core_profiles';
+ info: {
+ displayName: 'Profile';
+ description: '';
+ };
+ attributes: {
+ title: Attribute.String;
+ ContactInfo: Attribute.Component<'core.contact-info', true>;
+ };
+}
+
+export interface CoreContactInfo extends Schema.Component {
+ collectionName: 'components_core_contact_infos';
+ info: {
+ displayName: 'ContactInfo';
+ };
+ attributes: {
+ Phonenumber: Attribute.String;
+ Email: Attribute.String;
+ };
+}
+
+declare module '@strapi/types' {
+ export module Shared {
+ export interface Components {
+ 'core.profile': CoreProfile;
+ 'core.contact-info': CoreContactInfo;
+ }
+ }
+}
diff --git a/playground/types/generated/contentTypes.d.ts b/playground/types/generated/contentTypes.d.ts
new file mode 100644
index 0000000..64d1520
--- /dev/null
+++ b/playground/types/generated/contentTypes.d.ts
@@ -0,0 +1,858 @@
+import type { Schema, Attribute } from '@strapi/strapi';
+
+export interface AdminPermission extends Schema.CollectionType {
+ collectionName: 'admin_permissions';
+ info: {
+ name: 'Permission';
+ description: '';
+ singularName: 'permission';
+ pluralName: 'permissions';
+ displayName: 'Permission';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ action: Attribute.String &
+ Attribute.Required &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ actionParameters: Attribute.JSON & Attribute.DefaultTo<{}>;
+ subject: Attribute.String &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ properties: Attribute.JSON & Attribute.DefaultTo<{}>;
+ conditions: Attribute.JSON & Attribute.DefaultTo<[]>;
+ role: Attribute.Relation<'admin::permission', 'manyToOne', 'admin::role'>;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'admin::permission',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'admin::permission',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface AdminUser extends Schema.CollectionType {
+ collectionName: 'admin_users';
+ info: {
+ name: 'User';
+ description: '';
+ singularName: 'user';
+ pluralName: 'users';
+ displayName: 'User';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ firstname: Attribute.String &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ lastname: Attribute.String &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ username: Attribute.String;
+ email: Attribute.Email &
+ Attribute.Required &
+ Attribute.Private &
+ Attribute.Unique &
+ Attribute.SetMinMaxLength<{
+ minLength: 6;
+ }>;
+ password: Attribute.Password &
+ Attribute.Private &
+ Attribute.SetMinMaxLength<{
+ minLength: 6;
+ }>;
+ resetPasswordToken: Attribute.String & Attribute.Private;
+ registrationToken: Attribute.String & Attribute.Private;
+ isActive: Attribute.Boolean &
+ Attribute.Private &
+ Attribute.DefaultTo;
+ roles: Attribute.Relation<'admin::user', 'manyToMany', 'admin::role'> &
+ Attribute.Private;
+ blocked: Attribute.Boolean & Attribute.Private & Attribute.DefaultTo;
+ preferedLanguage: Attribute.String;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<'admin::user', 'oneToOne', 'admin::user'> &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<'admin::user', 'oneToOne', 'admin::user'> &
+ Attribute.Private;
+ };
+}
+
+export interface AdminRole extends Schema.CollectionType {
+ collectionName: 'admin_roles';
+ info: {
+ name: 'Role';
+ description: '';
+ singularName: 'role';
+ pluralName: 'roles';
+ displayName: 'Role';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ name: Attribute.String &
+ Attribute.Required &
+ Attribute.Unique &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ code: Attribute.String &
+ Attribute.Required &
+ Attribute.Unique &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ description: Attribute.String;
+ users: Attribute.Relation<'admin::role', 'manyToMany', 'admin::user'>;
+ permissions: Attribute.Relation<
+ 'admin::role',
+ 'oneToMany',
+ 'admin::permission'
+ >;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<'admin::role', 'oneToOne', 'admin::user'> &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<'admin::role', 'oneToOne', 'admin::user'> &
+ Attribute.Private;
+ };
+}
+
+export interface AdminApiToken extends Schema.CollectionType {
+ collectionName: 'strapi_api_tokens';
+ info: {
+ name: 'Api Token';
+ singularName: 'api-token';
+ pluralName: 'api-tokens';
+ displayName: 'Api Token';
+ description: '';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ name: Attribute.String &
+ Attribute.Required &
+ Attribute.Unique &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ description: Attribute.String &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }> &
+ Attribute.DefaultTo<''>;
+ type: Attribute.Enumeration<['read-only', 'full-access', 'custom']> &
+ Attribute.Required &
+ Attribute.DefaultTo<'read-only'>;
+ accessKey: Attribute.String &
+ Attribute.Required &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ lastUsedAt: Attribute.DateTime;
+ permissions: Attribute.Relation<
+ 'admin::api-token',
+ 'oneToMany',
+ 'admin::api-token-permission'
+ >;
+ expiresAt: Attribute.DateTime;
+ lifespan: Attribute.BigInteger;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'admin::api-token',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'admin::api-token',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface AdminApiTokenPermission extends Schema.CollectionType {
+ collectionName: 'strapi_api_token_permissions';
+ info: {
+ name: 'API Token Permission';
+ description: '';
+ singularName: 'api-token-permission';
+ pluralName: 'api-token-permissions';
+ displayName: 'API Token Permission';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ action: Attribute.String &
+ Attribute.Required &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ token: Attribute.Relation<
+ 'admin::api-token-permission',
+ 'manyToOne',
+ 'admin::api-token'
+ >;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'admin::api-token-permission',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'admin::api-token-permission',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface AdminTransferToken extends Schema.CollectionType {
+ collectionName: 'strapi_transfer_tokens';
+ info: {
+ name: 'Transfer Token';
+ singularName: 'transfer-token';
+ pluralName: 'transfer-tokens';
+ displayName: 'Transfer Token';
+ description: '';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ name: Attribute.String &
+ Attribute.Required &
+ Attribute.Unique &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ description: Attribute.String &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }> &
+ Attribute.DefaultTo<''>;
+ accessKey: Attribute.String &
+ Attribute.Required &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ lastUsedAt: Attribute.DateTime;
+ permissions: Attribute.Relation<
+ 'admin::transfer-token',
+ 'oneToMany',
+ 'admin::transfer-token-permission'
+ >;
+ expiresAt: Attribute.DateTime;
+ lifespan: Attribute.BigInteger;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'admin::transfer-token',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'admin::transfer-token',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface AdminTransferTokenPermission extends Schema.CollectionType {
+ collectionName: 'strapi_transfer_token_permissions';
+ info: {
+ name: 'Transfer Token Permission';
+ description: '';
+ singularName: 'transfer-token-permission';
+ pluralName: 'transfer-token-permissions';
+ displayName: 'Transfer Token Permission';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ action: Attribute.String &
+ Attribute.Required &
+ Attribute.SetMinMaxLength<{
+ minLength: 1;
+ }>;
+ token: Attribute.Relation<
+ 'admin::transfer-token-permission',
+ 'manyToOne',
+ 'admin::transfer-token'
+ >;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'admin::transfer-token-permission',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'admin::transfer-token-permission',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface PluginUploadFile extends Schema.CollectionType {
+ collectionName: 'files';
+ info: {
+ singularName: 'file';
+ pluralName: 'files';
+ displayName: 'File';
+ description: '';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ name: Attribute.String & Attribute.Required;
+ alternativeText: Attribute.String;
+ caption: Attribute.String;
+ width: Attribute.Integer;
+ height: Attribute.Integer;
+ formats: Attribute.JSON;
+ hash: Attribute.String & Attribute.Required;
+ ext: Attribute.String;
+ mime: Attribute.String & Attribute.Required;
+ size: Attribute.Decimal & Attribute.Required;
+ url: Attribute.String & Attribute.Required;
+ previewUrl: Attribute.String;
+ provider: Attribute.String & Attribute.Required;
+ provider_metadata: Attribute.JSON;
+ related: Attribute.Relation<'plugin::upload.file', 'morphToMany'>;
+ folder: Attribute.Relation<
+ 'plugin::upload.file',
+ 'manyToOne',
+ 'plugin::upload.folder'
+ > &
+ Attribute.Private;
+ folderPath: Attribute.String &
+ Attribute.Required &
+ Attribute.Private &
+ Attribute.SetMinMax<
+ {
+ min: 1;
+ },
+ number
+ >;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'plugin::upload.file',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'plugin::upload.file',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface PluginUploadFolder extends Schema.CollectionType {
+ collectionName: 'upload_folders';
+ info: {
+ singularName: 'folder';
+ pluralName: 'folders';
+ displayName: 'Folder';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ name: Attribute.String &
+ Attribute.Required &
+ Attribute.SetMinMax<
+ {
+ min: 1;
+ },
+ number
+ >;
+ pathId: Attribute.Integer & Attribute.Required & Attribute.Unique;
+ parent: Attribute.Relation<
+ 'plugin::upload.folder',
+ 'manyToOne',
+ 'plugin::upload.folder'
+ >;
+ children: Attribute.Relation<
+ 'plugin::upload.folder',
+ 'oneToMany',
+ 'plugin::upload.folder'
+ >;
+ files: Attribute.Relation<
+ 'plugin::upload.folder',
+ 'oneToMany',
+ 'plugin::upload.file'
+ >;
+ path: Attribute.String &
+ Attribute.Required &
+ Attribute.SetMinMax<
+ {
+ min: 1;
+ },
+ number
+ >;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'plugin::upload.folder',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'plugin::upload.folder',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface PluginContentReleasesRelease extends Schema.CollectionType {
+ collectionName: 'strapi_releases';
+ info: {
+ singularName: 'release';
+ pluralName: 'releases';
+ displayName: 'Release';
+ };
+ options: {
+ draftAndPublish: false;
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ name: Attribute.String & Attribute.Required;
+ releasedAt: Attribute.DateTime;
+ scheduledAt: Attribute.DateTime;
+ timezone: Attribute.String;
+ status: Attribute.Enumeration<
+ ['ready', 'blocked', 'failed', 'done', 'empty']
+ > &
+ Attribute.Required;
+ actions: Attribute.Relation<
+ 'plugin::content-releases.release',
+ 'oneToMany',
+ 'plugin::content-releases.release-action'
+ >;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'plugin::content-releases.release',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'plugin::content-releases.release',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface PluginContentReleasesReleaseAction
+ extends Schema.CollectionType {
+ collectionName: 'strapi_release_actions';
+ info: {
+ singularName: 'release-action';
+ pluralName: 'release-actions';
+ displayName: 'Release Action';
+ };
+ options: {
+ draftAndPublish: false;
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ type: Attribute.Enumeration<['publish', 'unpublish']> & Attribute.Required;
+ entry: Attribute.Relation<
+ 'plugin::content-releases.release-action',
+ 'morphToOne'
+ >;
+ contentType: Attribute.String & Attribute.Required;
+ locale: Attribute.String;
+ release: Attribute.Relation<
+ 'plugin::content-releases.release-action',
+ 'manyToOne',
+ 'plugin::content-releases.release'
+ >;
+ isEntryValid: Attribute.Boolean;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'plugin::content-releases.release-action',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'plugin::content-releases.release-action',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface PluginI18NLocale extends Schema.CollectionType {
+ collectionName: 'i18n_locale';
+ info: {
+ singularName: 'locale';
+ pluralName: 'locales';
+ collectionName: 'locales';
+ displayName: 'Locale';
+ description: '';
+ };
+ options: {
+ draftAndPublish: false;
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ name: Attribute.String &
+ Attribute.SetMinMax<
+ {
+ min: 1;
+ max: 50;
+ },
+ number
+ >;
+ code: Attribute.String & Attribute.Unique;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'plugin::i18n.locale',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'plugin::i18n.locale',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface PluginUsersPermissionsPermission
+ extends Schema.CollectionType {
+ collectionName: 'up_permissions';
+ info: {
+ name: 'permission';
+ description: '';
+ singularName: 'permission';
+ pluralName: 'permissions';
+ displayName: 'Permission';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ action: Attribute.String & Attribute.Required;
+ role: Attribute.Relation<
+ 'plugin::users-permissions.permission',
+ 'manyToOne',
+ 'plugin::users-permissions.role'
+ >;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'plugin::users-permissions.permission',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'plugin::users-permissions.permission',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface PluginUsersPermissionsRole extends Schema.CollectionType {
+ collectionName: 'up_roles';
+ info: {
+ name: 'role';
+ description: '';
+ singularName: 'role';
+ pluralName: 'roles';
+ displayName: 'Role';
+ };
+ pluginOptions: {
+ 'content-manager': {
+ visible: false;
+ };
+ 'content-type-builder': {
+ visible: false;
+ };
+ };
+ attributes: {
+ name: Attribute.String &
+ Attribute.Required &
+ Attribute.SetMinMaxLength<{
+ minLength: 3;
+ }>;
+ description: Attribute.String;
+ type: Attribute.String & Attribute.Unique;
+ permissions: Attribute.Relation<
+ 'plugin::users-permissions.role',
+ 'oneToMany',
+ 'plugin::users-permissions.permission'
+ >;
+ users: Attribute.Relation<
+ 'plugin::users-permissions.role',
+ 'oneToMany',
+ 'plugin::users-permissions.user'
+ >;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'plugin::users-permissions.role',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'plugin::users-permissions.role',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface PluginUsersPermissionsUser extends Schema.CollectionType {
+ collectionName: 'up_users';
+ info: {
+ name: 'user';
+ description: '';
+ singularName: 'user';
+ pluralName: 'users';
+ displayName: 'User';
+ };
+ options: {
+ draftAndPublish: false;
+ timestamps: true;
+ };
+ attributes: {
+ username: Attribute.String &
+ Attribute.Required &
+ Attribute.Unique &
+ Attribute.SetMinMaxLength<{
+ minLength: 3;
+ }>;
+ email: Attribute.Email &
+ Attribute.Required &
+ Attribute.SetMinMaxLength<{
+ minLength: 6;
+ }>;
+ provider: Attribute.String;
+ password: Attribute.Password &
+ Attribute.Private &
+ Attribute.SetMinMaxLength<{
+ minLength: 6;
+ }>;
+ resetPasswordToken: Attribute.String & Attribute.Private;
+ confirmationToken: Attribute.String & Attribute.Private;
+ confirmed: Attribute.Boolean & Attribute.DefaultTo;
+ blocked: Attribute.Boolean & Attribute.DefaultTo;
+ role: Attribute.Relation<
+ 'plugin::users-permissions.user',
+ 'manyToOne',
+ 'plugin::users-permissions.role'
+ >;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<
+ 'plugin::users-permissions.user',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<
+ 'plugin::users-permissions.user',
+ 'oneToOne',
+ 'admin::user'
+ > &
+ Attribute.Private;
+ };
+}
+
+export interface ApiHomeHome extends Schema.SingleType {
+ collectionName: 'homes';
+ info: {
+ singularName: 'home';
+ pluralName: 'homes';
+ displayName: 'Home';
+ description: '';
+ };
+ options: {
+ draftAndPublish: false;
+ };
+ attributes: {
+ title: Attribute.String;
+ Profile: Attribute.Component<'core.profile'>;
+ slug: Attribute.UID<'api::home.home', 'title'>;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<'api::home.home', 'oneToOne', 'admin::user'> &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<'api::home.home', 'oneToOne', 'admin::user'> &
+ Attribute.Private;
+ };
+}
+
+export interface ApiPagePage extends Schema.CollectionType {
+ collectionName: 'pages';
+ info: {
+ singularName: 'page';
+ pluralName: 'pages';
+ displayName: 'Page';
+ };
+ options: {
+ draftAndPublish: false;
+ };
+ attributes: {
+ title: Attribute.String & Attribute.Required;
+ createdAt: Attribute.DateTime;
+ updatedAt: Attribute.DateTime;
+ createdBy: Attribute.Relation<'api::page.page', 'oneToOne', 'admin::user'> &
+ Attribute.Private;
+ updatedBy: Attribute.Relation<'api::page.page', 'oneToOne', 'admin::user'> &
+ Attribute.Private;
+ };
+}
+
+declare module '@strapi/types' {
+ export module Shared {
+ export interface ContentTypes {
+ 'admin::permission': AdminPermission;
+ 'admin::user': AdminUser;
+ 'admin::role': AdminRole;
+ 'admin::api-token': AdminApiToken;
+ 'admin::api-token-permission': AdminApiTokenPermission;
+ 'admin::transfer-token': AdminTransferToken;
+ 'admin::transfer-token-permission': AdminTransferTokenPermission;
+ 'plugin::upload.file': PluginUploadFile;
+ 'plugin::upload.folder': PluginUploadFolder;
+ 'plugin::content-releases.release': PluginContentReleasesRelease;
+ 'plugin::content-releases.release-action': PluginContentReleasesReleaseAction;
+ 'plugin::i18n.locale': PluginI18NLocale;
+ 'plugin::users-permissions.permission': PluginUsersPermissionsPermission;
+ 'plugin::users-permissions.role': PluginUsersPermissionsRole;
+ 'plugin::users-permissions.user': PluginUsersPermissionsUser;
+ 'api::home.home': ApiHomeHome;
+ 'api::page.page': ApiPagePage;
+ }
+ }
+}
diff --git a/server/config/type.js b/server/config/type.js
index 3bb3b46..468a8e3 100644
--- a/server/config/type.js
+++ b/server/config/type.js
@@ -109,7 +109,10 @@ const ConfigType = class ConfigType {
if (softImport && !force) return false;
// Format JSON fields.
- configContent = sanitizeConfig(configContent);
+ configContent = sanitizeConfig({
+ config: configContent,
+ configName,
+ });
const query = { ...configContent };
this.jsonFields.map((field) => query[field] = JSON.stringify(configContent[field]));
@@ -120,8 +123,8 @@ const ConfigType = class ConfigType {
// Delete/create relations.
await Promise.all(this.relations.map(async ({ queryString, relationName, parentName, relationSortFields }) => {
const relationQueryApi = strapi.query(queryString);
- existingConfig = sanitizeConfig(existingConfig, relationName, relationSortFields);
- configContent = sanitizeConfig(configContent, relationName, relationSortFields);
+ existingConfig = sanitizeConfig({ config: existingConfig, configName, relation: relationName, relationSortFields });
+ configContent = sanitizeConfig({ config: configContent, configName, relation: relationName, relationSortFields });
const configToAdd = difference(configContent[relationName], existingConfig[relationName], relationSortFields);
const configToDelete = difference(existingConfig[relationName], configContent[relationName], relationSortFields);
@@ -201,7 +204,7 @@ const ConfigType = class ConfigType {
});
const configs = {};
- await Promise.all(Object.values(AllConfig).map(async (config) => {
+ await Promise.all(Object.entries(AllConfig).map(async ([configName, config]) => {
const combinedUid = getCombinedUid(this.uidKeys, config);
const combinedUidWhereFilter = getCombinedUidWhereFilter(this.uidKeys, config);
@@ -214,13 +217,13 @@ const ConfigType = class ConfigType {
const shouldExclude = !isEmpty(strapi.config.get('plugin.config-sync.excludedConfig').filter((option) => `${this.configPrefix}.${combinedUid}`.startsWith(option)));
if (shouldExclude) return;
- const formattedConfig = { ...sanitizeConfig(config) };
+ const formattedConfig = { ...sanitizeConfig({ config, configName }) };
await Promise.all(this.relations.map(async ({ queryString, relationName, relationSortFields, parentName }) => {
const relations = await noLimit(strapi.query(queryString), {
where: { [parentName]: combinedUidWhereFilter },
});
- relations.map((relation) => sanitizeConfig(relation));
+ relations.map((relation) => sanitizeConfig({ config: relation, configName: relationName }));
relationSortFields.map((sortField) => {
relations.sort(dynamicSort(sortField));
});
diff --git a/server/utils/index.js b/server/utils/index.js
index 4a6c6f9..b29c2a8 100644
--- a/server/utils/index.js
+++ b/server/utils/index.js
@@ -46,7 +46,12 @@ const dynamicSort = (property) => {
};
};
-const sanitizeConfig = (config, relation, relationSortFields) => {
+const sanitizeConfig = ({
+ config,
+ relation,
+ relationSortFields,
+ configName,
+}) => {
delete config._id;
delete config.id;
delete config.updatedAt;
@@ -74,6 +79,26 @@ const sanitizeConfig = (config, relation, relationSortFields) => {
config[relation] = formattedRelations;
}
+ // We recursively sanitize the config to remove environment specific data.
+ // Except for the plugin_content_manager_configuration.
+ // This is because that stores the "edit the view" data which includes only configuration, not content.
+ if (configName && !configName.startsWith('plugin_content_manager_configuration_')) {
+ const recursiveSanitizeConfig = (recursivedSanitizedConfig) => {
+ delete recursivedSanitizedConfig._id;
+ delete recursivedSanitizedConfig.id;
+ delete recursivedSanitizedConfig.updatedAt;
+ delete recursivedSanitizedConfig.createdAt;
+
+ Object.keys(recursivedSanitizedConfig).map((key, index) => {
+ if (recursivedSanitizedConfig[key] && typeof recursivedSanitizedConfig[key] === "object") {
+ recursiveSanitizeConfig(recursivedSanitizedConfig[key]);
+ }
+ });
+ };
+
+ recursiveSanitizeConfig(config);
+ }
+
return config;
};