From 23cae2040a8895a042e806a4c897efc936f6eb18 Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 10 Jul 2026 11:18:52 +0200 Subject: [PATCH] Improve application asset management (#22564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit App manifests could point the logo and screenshots at either external URLs or public folder paths, and that was handled inconsistently across install, sync and the marketplace. This makes assets always bundled files: - Manifests now use `logo` and `galleryImages` (a `string[]` of public folder paths) instead of `logoUrl` and `screenshots`. The old fields still work but are deprecated. Gallery order comes from the array index. Normalization (deprecated-field migration, and warning about + ignoring external URLs) happens in `defineApplication`, so the warnings surface at define time. - Logo is stored as a File record (`logoFileId`). - The registration gallery is configured via a `settings` jsonb column on `applicationRegistration` (`{ galleryImages: string[] }`) — populated from the manifest, read by the marketplace detail (falling back to the legacy `screenshots` column, then the manifest). No dedicated gallery table. - The marketplace detail DTO and front now use `galleryImages`. Verified against a local Postgres: the fast instance commands run with no pending-migration diff, the schema is correct, and the server boots. Typecheck, lint, codegen and the application unit tests pass. Not included yet: rehosting assets into storage for npm catalog and tarball registrations, versioned cache busting on the serving route, and a backfill for existing installs. ``<img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg">`` --- .../src/application-config.ts | 4 +- .../src/metadata/generated/schema.graphql | 3 +- .../src/metadata/generated/schema.ts | 4 + .../src/metadata/generated/types.ts | 3 + .../extend/apps/config/application.mdx | 8 +- .../extend/apps/config/public-assets.mdx | 2 +- .../extend/apps/operations/publishing.mdx | 12 +- .../document-generator/publishing.mdx | 10 +- .../src/generated-metadata/graphql.ts | 10 +- .../fragments/marketplaceAppDetailFragment.ts | 2 +- .../SettingsApplicationDetails.tsx | 2 +- .../SettingsAvailableApplicationDetails.tsx | 2 +- .../app-dev/expected-manifest.ts | 1 + .../app-dev/expected-manifest.ts | 1 + .../__tests__/apply-generated-cover.test.ts | 20 ++-- .../build/cover/apply-generated-cover.ts | 14 +-- .../build/manifest/manifest-build.ts | 40 ++++--- .../application/__tests__/define-app.spec.ts | 12 +- .../define/application/define-application.ts | 11 +- .../normalize-application-assets.test.ts | 98 +++++++++++++++++ .../utils/normalize-application-assets.ts | 62 +++++++++++ ...lery-images-to-application-registration.ts | 21 ++++ ...lery-images-on-application-registration.ts | 33 ++++++ .../instance-commands.constant.ts | 4 + .../application-install.service.ts | 23 ++-- .../dtos/marketplace-app-detail.dto.ts | 7 +- .../marketplace-query.service.ts | 14 ++- .../resolve-manifest-asset-urls.util.spec.ts | 41 +++++++ .../utils/resolve-manifest-asset-urls.util.ts | 4 + .../application-registration.entity.ts | 15 ++- .../application-registration.service.ts | 1 + .../application-tarball.service.ts | 104 +++++++++++++++++- ...ication-registration-gallery-image.type.ts | 4 + .../__tests__/is-image-file-path.util.spec.ts | 26 +++++ ...fest-application-to-display-fields.util.ts | 9 +- .../utils/is-image-file-path.util.ts | 21 ++++ .../utils/to-gallery-image-paths.util.ts | 13 +++ .../src/application/applicationType.ts | 8 ++ 38 files changed, 594 insertions(+), 75 deletions(-) create mode 100644 packages/twenty-sdk/src/sdk/define/application/utils/__tests__/normalize-application-assets.test.ts create mode 100644 packages/twenty-sdk/src/sdk/define/application/utils/normalize-application-assets.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1783615890055-add-gallery-images-to-application-registration.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-slow-1783615890056-backfill-gallery-images-on-application-registration.ts create mode 100644 packages/twenty-server/src/engine/core-modules/application/application-registration/types/application-registration-gallery-image.type.ts create mode 100644 packages/twenty-server/src/engine/core-modules/application/application-registration/utils/__tests__/is-image-file-path.util.spec.ts create mode 100644 packages/twenty-server/src/engine/core-modules/application/application-registration/utils/is-image-file-path.util.ts create mode 100644 packages/twenty-server/src/engine/core-modules/application/application-registration/utils/to-gallery-image-paths.util.ts diff --git a/packages/twenty-apps/examples/document-generator/src/application-config.ts b/packages/twenty-apps/examples/document-generator/src/application-config.ts index 15f734cf7c..1f30b09ff5 100644 --- a/packages/twenty-apps/examples/document-generator/src/application-config.ts +++ b/packages/twenty-apps/examples/document-generator/src/application-config.ts @@ -12,8 +12,8 @@ export default defineApplication({ universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, displayName: APP_DISPLAY_NAME, description: APP_DESCRIPTION, - logoUrl: 'public/document-generator.svg', - screenshots: [ + logo: 'public/document-generator.svg', + galleryImages: [ 'public/gallery/01-template-editor.png', 'public/gallery/02-command-menu.png', 'public/gallery/03-documents.png', diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index 7cecd8b9fd..4156f06ce4 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -2253,7 +2253,8 @@ type MarketplaceAppDetail { termsUrl: String emailSupport: String issueReportUrl: String - screenshots: [String!]! + screenshots: [String!]! @deprecated(reason: "Use galleryImages instead") + galleryImages: [String!]! defaultRoleUniversalIdentifier: String roles: [MarketplaceAppRole!] manifest: JSON @deprecated(reason: "Use the explicit MarketplaceAppDetail fields (description, author, roles, ...) instead") diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index 86081d4fbb..f5f3755cc5 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -1912,7 +1912,9 @@ export interface MarketplaceAppDetail { termsUrl?: Scalars['String'] emailSupport?: Scalars['String'] issueReportUrl?: Scalars['String'] + /** @deprecated Use galleryImages instead */ screenshots: Scalars['String'][] + galleryImages: Scalars['String'][] defaultRoleUniversalIdentifier?: Scalars['String'] roles?: MarketplaceAppRole[] /** @deprecated Use the explicit MarketplaceAppDetail fields (description, author, roles, ...) instead */ @@ -5062,7 +5064,9 @@ export interface MarketplaceAppDetailGenqlSelection{ termsUrl?: boolean | number emailSupport?: boolean | number issueReportUrl?: boolean | number + /** @deprecated Use galleryImages instead */ screenshots?: boolean | number + galleryImages?: boolean | number defaultRoleUniversalIdentifier?: boolean | number roles?: MarketplaceAppRoleGenqlSelection /** @deprecated Use the explicit MarketplaceAppDetail fields (description, author, roles, ...) instead */ diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index b1688e9490..09e516f4dc 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -4452,6 +4452,9 @@ export default { "screenshots": [ 1 ], + "galleryImages": [ + 1 + ], "defaultRoleUniversalIdentifier": [ 1 ], diff --git a/packages/twenty-docs/developers/extend/apps/config/application.mdx b/packages/twenty-docs/developers/extend/apps/config/application.mdx index 2de6729d2e..07e0d37fc9 100644 --- a/packages/twenty-docs/developers/extend/apps/config/application.mdx +++ b/packages/twenty-docs/developers/extend/apps/config/application.mdx @@ -108,10 +108,14 @@ If you plan to [publish your app](/developers/extend/apps/operations/publishing) |-------|-------------| | `author` | Author or company name | | `category` | App category for marketplace filtering | -| `logoUrl` | Path to your app logo (e.g., `public/logo.png`) | -| `screenshots` | Array of screenshot paths (e.g., `public/screenshot-1.png`) | +| `logo` | Path to your app logo bundled in `public/` (e.g., `public/logo.png`) | +| `galleryImages` | Array of gallery image paths bundled in `public/` (e.g., `public/screenshot-1.png`) | | `aboutDescription` | Longer markdown description for the "About" tab. If omitted, the marketplace uses the package's `README.md` from npm | | `websiteUrl` | Link to your website | | `termsUrl` | Link to terms of service | | `emailSupport` | Support email address | | `issueReportUrl` | Link to issue tracker | + + +`logoUrl` and `screenshots` are deprecated aliases of `logo` and `galleryImages`. External absolute URLs (`http://` or `https://`) are not supported for these fields: they are dropped with a warning at build time. Bundle the images in your app's `public/` folder instead. + diff --git a/packages/twenty-docs/developers/extend/apps/config/public-assets.mdx b/packages/twenty-docs/developers/extend/apps/config/public-assets.mdx index 8350077407..b8c8bd8cfe 100644 --- a/packages/twenty-docs/developers/extend/apps/config/public-assets.mdx +++ b/packages/twenty-docs/developers/extend/apps/config/public-assets.mdx @@ -11,7 +11,7 @@ Files placed in `public/` are: - **Publicly accessible** — once synced to the server, assets are served at a public URL. No authentication is needed to access them. - **Available in front components** — use asset URLs to display images, icons, or any media inside your React components. - **Available in logic functions** — reference asset URLs in emails, API responses, or any server-side logic. -- **Used for marketplace metadata** — the `logoUrl` and `screenshots` fields in `defineApplication()` reference files from this folder (e.g., `public/logo.png`). These are displayed in the marketplace when your app is published. +- **Used for marketplace metadata** — the `logo` and `galleryImages` fields in `defineApplication()` reference files from this folder (e.g., `public/logo.png`). These are displayed in the marketplace when your app is published. External absolute URLs are ignored for these fields — bundle the images in `public/` instead. - **Auto-synced in dev mode** — when you add, update, or delete a file in `public/`, it is synced to the server automatically. No restart needed. - **Included in builds** — `yarn twenty dev:build` bundles all public assets into the distribution output. diff --git a/packages/twenty-docs/developers/extend/apps/operations/publishing.mdx b/packages/twenty-docs/developers/extend/apps/operations/publishing.mdx index bdad1a0415..130e59fd20 100644 --- a/packages/twenty-docs/developers/extend/apps/operations/publishing.mdx +++ b/packages/twenty-docs/developers/extend/apps/operations/publishing.mdx @@ -180,15 +180,15 @@ Publishing to npm makes your app discoverable in the Twenty marketplace. Any Twe ### Marketplace metadata -The `defineApplication()` config supports optional fields that control how your app appears in the marketplace. Use `logoUrl` and `screenshots` to reference images from the `public/` folder: +The `defineApplication()` config supports optional fields that control how your app appears in the marketplace. Use `logo` and `galleryImages` to reference images from the `public/` folder: ```ts src/application-config.ts export default defineApplication({ universalIdentifier: '...', displayName: 'My App', description: 'A great app', - logoUrl: 'public/logo.png', - screenshots: [ + logo: 'public/logo.png', + galleryImages: [ 'public/screenshot-1.png', 'public/screenshot-2.png', ], @@ -197,12 +197,12 @@ export default defineApplication({ See the [defineApplication accordion](/developers/extend/apps/config/application#marketplace-metadata) in the Building Apps page for the full list of marketplace fields (`author`, `category`, `aboutDescription`, `websiteUrl`, `termsUrl`, etc.). -#### Recommended screenshot dimensions +#### Recommended gallery image dimensions -The marketplace renders `screenshots` in a fixed `8:5` container (for example, `1600×1000 px`). +The marketplace renders `galleryImages` in a fixed `8:5` container (for example, `1600×1000 px`). -Screenshots of any aspect ratio are displayed in full and are never cropped, but anything significantly taller or narrower than `8:5` will show empty bands on the sides. +Gallery images of any aspect ratio are displayed in full and are never cropped, but anything significantly taller or narrower than `8:5` will show empty bands on the sides. ### Publish diff --git a/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/publishing.mdx b/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/publishing.mdx index 570f45ec4f..cc3e181fb4 100644 --- a/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/publishing.mdx +++ b/packages/twenty-docs/developers/extend/apps/tutorials/document-generator/publishing.mdx @@ -10,7 +10,7 @@ Your app works. The last step is to describe it for the marketplace and publish. The [application config](/developers/extend/apps/config/application) carries the identity that shows up in the marketplace: author, category, logo, and support -links. Put a logo in `public/` and reference it with `logoUrl`. +links. Put a logo in `public/` and reference it with `logo`. ```ts filename="src/application-config.ts" import { defineApplication } from 'twenty-sdk/define'; @@ -20,7 +20,7 @@ export default defineApplication({ displayName: 'Document Generator', description: 'Create reusable document templates and generate personalized documents from your CRM records.', - logoUrl: 'public/document-generator.svg', + logo: 'public/document-generator.svg', author: 'Twenty', category: 'Productivity', websiteUrl: 'https://docs.twenty.com/developers/extend/apps', @@ -44,13 +44,13 @@ Also add the `twenty-app` keyword to `package.json` so the app is discoverable: ## Add gallery screenshots A marketplace listing sells itself with screenshots. Drop a few PNGs in -`public/gallery/` and reference them with `screenshots` — they render as a gallery -on the listing page. +`public/gallery/` and reference them with `galleryImages` — they render as a +gallery on the listing page, in array order. ```ts filename="src/application-config.ts" export default defineApplication({ // ...identity from above - screenshots: [ + galleryImages: [ 'public/gallery/01-generated-document.png', 'public/gallery/02-command-menu.png', 'public/gallery/03-template-editor.png', diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 549a1a4ddf..b0c28b7c74 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -2316,6 +2316,7 @@ export type MarketplaceAppDetail = { defaultRoleUniversalIdentifier?: Maybe; description?: Maybe; emailSupport?: Maybe; + galleryImages: Array; id: Scalars['String']['output']; isListed: Scalars['Boolean']['output']; isVetted: Scalars['Boolean']['output']; @@ -2326,6 +2327,7 @@ export type MarketplaceAppDetail = { manifest?: Maybe; name: Scalars['String']['output']; roles?: Maybe>; + /** @deprecated Use galleryImages instead */ screenshots: Array; sourcePackage?: Maybe; sourceType: ApplicationRegistrationSourceType; @@ -7245,7 +7247,7 @@ export type GetLogicFunctionSourceCodeQueryVariables = Exact<{ export type GetLogicFunctionSourceCodeQuery = { __typename?: 'Query', getLogicFunctionSourceCode?: string | null }; -export type MarketplaceAppDetailFieldsFragment = { __typename?: 'MarketplaceAppDetail', id: string, universalIdentifier: string, name: string, sourceType: ApplicationRegistrationSourceType, sourcePackage?: string | null, latestAvailableVersion?: string | null, isListed: boolean, isVetted: boolean, description?: string | null, author?: string | null, category?: string | null, logo?: string | null, websiteUrl?: string | null, aboutDescription?: string | null, termsUrl?: string | null, emailSupport?: string | null, issueReportUrl?: string | null, screenshots: Array, defaultRoleUniversalIdentifier?: string | null, roles?: Array<{ __typename?: 'MarketplaceAppRole', universalIdentifier: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings?: boolean | null, canAccessAllTools?: boolean | null, canReadAllObjectRecords?: boolean | null, canUpdateAllObjectRecords?: boolean | null, canSoftDeleteAllObjectRecords?: boolean | null, canDestroyAllObjectRecords?: boolean | null, permissionFlagUniversalIdentifiers?: Array | null, objectPermissions?: Array<{ __typename?: 'MarketplaceAppRoleObjectPermission', universalIdentifier: string, objectUniversalIdentifier: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null, fieldPermissions?: Array<{ __typename?: 'MarketplaceAppRoleFieldPermission', universalIdentifier: string, objectUniversalIdentifier: string, fieldUniversalIdentifier: string, canReadFieldValue?: boolean | null, canUpdateFieldValue?: boolean | null }> | null }> | null }; +export type MarketplaceAppDetailFieldsFragment = { __typename?: 'MarketplaceAppDetail', id: string, universalIdentifier: string, name: string, sourceType: ApplicationRegistrationSourceType, sourcePackage?: string | null, latestAvailableVersion?: string | null, isListed: boolean, isVetted: boolean, description?: string | null, author?: string | null, category?: string | null, logo?: string | null, websiteUrl?: string | null, aboutDescription?: string | null, termsUrl?: string | null, emailSupport?: string | null, issueReportUrl?: string | null, galleryImages: Array, defaultRoleUniversalIdentifier?: string | null, roles?: Array<{ __typename?: 'MarketplaceAppRole', universalIdentifier: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings?: boolean | null, canAccessAllTools?: boolean | null, canReadAllObjectRecords?: boolean | null, canUpdateAllObjectRecords?: boolean | null, canSoftDeleteAllObjectRecords?: boolean | null, canDestroyAllObjectRecords?: boolean | null, permissionFlagUniversalIdentifiers?: Array | null, objectPermissions?: Array<{ __typename?: 'MarketplaceAppRoleObjectPermission', universalIdentifier: string, objectUniversalIdentifier: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null, fieldPermissions?: Array<{ __typename?: 'MarketplaceAppRoleFieldPermission', universalIdentifier: string, objectUniversalIdentifier: string, fieldUniversalIdentifier: string, canReadFieldValue?: boolean | null, canUpdateFieldValue?: boolean | null }> | null }> | null }; export type MarketplaceAppFieldsFragment = { __typename?: 'MarketplaceApp', id: string, name: string, description: string, author: string, category: string, logo?: string | null, sourcePackage?: string | null, isVetted: boolean }; @@ -7276,7 +7278,7 @@ export type FindMarketplaceAppDetailQueryVariables = Exact<{ }>; -export type FindMarketplaceAppDetailQuery = { __typename?: 'Query', findMarketplaceAppDetail: { __typename?: 'MarketplaceAppDetail', id: string, universalIdentifier: string, name: string, sourceType: ApplicationRegistrationSourceType, sourcePackage?: string | null, latestAvailableVersion?: string | null, isListed: boolean, isVetted: boolean, description?: string | null, author?: string | null, category?: string | null, logo?: string | null, websiteUrl?: string | null, aboutDescription?: string | null, termsUrl?: string | null, emailSupport?: string | null, issueReportUrl?: string | null, screenshots: Array, defaultRoleUniversalIdentifier?: string | null, roles?: Array<{ __typename?: 'MarketplaceAppRole', universalIdentifier: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings?: boolean | null, canAccessAllTools?: boolean | null, canReadAllObjectRecords?: boolean | null, canUpdateAllObjectRecords?: boolean | null, canSoftDeleteAllObjectRecords?: boolean | null, canDestroyAllObjectRecords?: boolean | null, permissionFlagUniversalIdentifiers?: Array | null, objectPermissions?: Array<{ __typename?: 'MarketplaceAppRoleObjectPermission', universalIdentifier: string, objectUniversalIdentifier: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null, fieldPermissions?: Array<{ __typename?: 'MarketplaceAppRoleFieldPermission', universalIdentifier: string, objectUniversalIdentifier: string, fieldUniversalIdentifier: string, canReadFieldValue?: boolean | null, canUpdateFieldValue?: boolean | null }> | null }> | null } }; +export type FindMarketplaceAppDetailQuery = { __typename?: 'Query', findMarketplaceAppDetail: { __typename?: 'MarketplaceAppDetail', id: string, universalIdentifier: string, name: string, sourceType: ApplicationRegistrationSourceType, sourcePackage?: string | null, latestAvailableVersion?: string | null, isListed: boolean, isVetted: boolean, description?: string | null, author?: string | null, category?: string | null, logo?: string | null, websiteUrl?: string | null, aboutDescription?: string | null, termsUrl?: string | null, emailSupport?: string | null, issueReportUrl?: string | null, galleryImages: Array, defaultRoleUniversalIdentifier?: string | null, roles?: Array<{ __typename?: 'MarketplaceAppRole', universalIdentifier: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings?: boolean | null, canAccessAllTools?: boolean | null, canReadAllObjectRecords?: boolean | null, canUpdateAllObjectRecords?: boolean | null, canSoftDeleteAllObjectRecords?: boolean | null, canDestroyAllObjectRecords?: boolean | null, permissionFlagUniversalIdentifiers?: Array | null, objectPermissions?: Array<{ __typename?: 'MarketplaceAppRoleObjectPermission', universalIdentifier: string, objectUniversalIdentifier: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null, fieldPermissions?: Array<{ __typename?: 'MarketplaceAppRoleFieldPermission', universalIdentifier: string, objectUniversalIdentifier: string, fieldUniversalIdentifier: string, canReadFieldValue?: boolean | null, canUpdateFieldValue?: boolean | null }> | null }> | null } }; export type FindMarketplaceAppManifestQueryVariables = Exact<{ universalIdentifier: Scalars['String']['input']; @@ -8826,7 +8828,7 @@ export const CommandMenuItemFieldsFragmentDoc = {"kind":"Document","definitions" export const PageLayoutWidgetFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutWidgetFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidget"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalAvailabilityExpression"}},{"kind":"Field","name":{"kind":"Name","value":"gridPosition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}}]}},{"kind":"Field","name":{"kind":"Name","value":"position"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetGridPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetVerticalListPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"index"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetCanvasPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BarChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"groupMode"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LineChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"isStacked"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PieChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"groupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"groupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"dateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"orderBy"}},{"kind":"Field","name":{"kind":"Name","value":"manualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"showCenterMetric"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"hideEmptyCategory"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AggregateChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"numberFormat"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}},{"kind":"Field","name":{"kind":"Name","value":"ratioAggregateConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"optionValue"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"IframeConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StandaloneRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"body"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blocknote"}},{"kind":"Field","name":{"kind":"Name","value":"markdown"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalendarConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailThreadConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"fieldDisplayMode"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"newFieldDefaultVisibility"}},{"kind":"Field","name":{"kind":"Name","value":"shouldAllowUserToSeeHiddenFields"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FilesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NotesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TasksConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecordTableConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowRunConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowVersionConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FrontComponentConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponentId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutTabId"}}]}}]} as unknown as DocumentNode; export const PageLayoutTabFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutTabFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutTab"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"widgets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutWidgetFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutWidgetFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidget"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalAvailabilityExpression"}},{"kind":"Field","name":{"kind":"Name","value":"gridPosition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}}]}},{"kind":"Field","name":{"kind":"Name","value":"position"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetGridPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetVerticalListPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"index"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetCanvasPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BarChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"groupMode"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LineChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"isStacked"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PieChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"groupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"groupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"dateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"orderBy"}},{"kind":"Field","name":{"kind":"Name","value":"manualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"showCenterMetric"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"hideEmptyCategory"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AggregateChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"numberFormat"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}},{"kind":"Field","name":{"kind":"Name","value":"ratioAggregateConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"optionValue"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"IframeConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StandaloneRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"body"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blocknote"}},{"kind":"Field","name":{"kind":"Name","value":"markdown"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalendarConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailThreadConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"fieldDisplayMode"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"newFieldDefaultVisibility"}},{"kind":"Field","name":{"kind":"Name","value":"shouldAllowUserToSeeHiddenFields"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FilesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NotesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TasksConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecordTableConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowRunConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowVersionConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FrontComponentConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponentId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutTabId"}}]}}]} as unknown as DocumentNode; export const PageLayoutFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayout"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"defaultTabToFocusOnMobileAndSidePanelId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"tabs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutTabFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutWidgetFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidget"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalAvailabilityExpression"}},{"kind":"Field","name":{"kind":"Name","value":"gridPosition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}}]}},{"kind":"Field","name":{"kind":"Name","value":"position"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetGridPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetVerticalListPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"index"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetCanvasPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BarChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"groupMode"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LineChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"isStacked"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PieChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"groupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"groupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"dateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"orderBy"}},{"kind":"Field","name":{"kind":"Name","value":"manualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"showCenterMetric"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"hideEmptyCategory"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AggregateChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"numberFormat"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}},{"kind":"Field","name":{"kind":"Name","value":"ratioAggregateConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"optionValue"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"IframeConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StandaloneRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"body"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blocknote"}},{"kind":"Field","name":{"kind":"Name","value":"markdown"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalendarConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailThreadConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"fieldDisplayMode"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"newFieldDefaultVisibility"}},{"kind":"Field","name":{"kind":"Name","value":"shouldAllowUserToSeeHiddenFields"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FilesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NotesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TasksConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecordTableConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowRunConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowVersionConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FrontComponentConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponentId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutTabId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutTabFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutTab"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"widgets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutWidgetFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; -export const MarketplaceAppDetailFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MarketplaceAppDetailFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MarketplaceAppDetail"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sourceType"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePackage"}},{"kind":"Field","name":{"kind":"Name","value":"latestAvailableVersion"}},{"kind":"Field","name":{"kind":"Name","value":"isListed"}},{"kind":"Field","name":{"kind":"Name","value":"isVetted"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"author"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"websiteUrl"}},{"kind":"Field","name":{"kind":"Name","value":"aboutDescription"}},{"kind":"Field","name":{"kind":"Name","value":"termsUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailSupport"}},{"kind":"Field","name":{"kind":"Name","value":"issueReportUrl"}},{"kind":"Field","name":{"kind":"Name","value":"screenshots"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRoleUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"roles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateAllSettings"}},{"kind":"Field","name":{"kind":"Name","value":"canAccessAllTools"}},{"kind":"Field","name":{"kind":"Name","value":"canReadAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canSoftDeleteAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canDestroyAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"permissionFlagUniversalIdentifiers"}},{"kind":"Field","name":{"kind":"Name","value":"objectPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"objectUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"canReadObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canSoftDeleteObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canDestroyObjectRecords"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fieldPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"objectUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"fieldUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"canReadFieldValue"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateFieldValue"}}]}}]}}]}}]} as unknown as DocumentNode; +export const MarketplaceAppDetailFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MarketplaceAppDetailFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MarketplaceAppDetail"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sourceType"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePackage"}},{"kind":"Field","name":{"kind":"Name","value":"latestAvailableVersion"}},{"kind":"Field","name":{"kind":"Name","value":"isListed"}},{"kind":"Field","name":{"kind":"Name","value":"isVetted"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"author"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"websiteUrl"}},{"kind":"Field","name":{"kind":"Name","value":"aboutDescription"}},{"kind":"Field","name":{"kind":"Name","value":"termsUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailSupport"}},{"kind":"Field","name":{"kind":"Name","value":"issueReportUrl"}},{"kind":"Field","name":{"kind":"Name","value":"galleryImages"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRoleUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"roles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateAllSettings"}},{"kind":"Field","name":{"kind":"Name","value":"canAccessAllTools"}},{"kind":"Field","name":{"kind":"Name","value":"canReadAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canSoftDeleteAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canDestroyAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"permissionFlagUniversalIdentifiers"}},{"kind":"Field","name":{"kind":"Name","value":"objectPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"objectUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"canReadObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canSoftDeleteObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canDestroyObjectRecords"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fieldPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"objectUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"fieldUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"canReadFieldValue"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateFieldValue"}}]}}]}}]}}]} as unknown as DocumentNode; export const MarketplaceAppFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MarketplaceAppFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MarketplaceApp"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"author"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePackage"}},{"kind":"Field","name":{"kind":"Name","value":"isVetted"}}]}}]} as unknown as DocumentNode; export const NavigationMenuItemFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NavigationMenuItemFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NavigationMenuItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"userWorkspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"targetRecordId"}},{"kind":"Field","name":{"kind":"Name","value":"targetObjectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"folderId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"link"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const NavigationMenuItemQueryFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NavigationMenuItemQueryFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NavigationMenuItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NavigationMenuItemFields"}},{"kind":"Field","name":{"kind":"Name","value":"targetRecordIdentifier"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifier"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NavigationMenuItemFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NavigationMenuItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"userWorkspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"targetRecordId"}},{"kind":"Field","name":{"kind":"Name","value":"targetObjectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"folderId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"link"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; @@ -8959,7 +8961,7 @@ export const GetLogicFunctionSourceCodeDocument = {"kind":"Document","definition export const InstallApplicationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InstallApplication"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"installApplication"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"universalIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const UpgradeApplicationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpgradeApplication"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"appRegistrationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"targetVersion"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upgradeApplication"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"appRegistrationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"appRegistrationId"}}},{"kind":"Argument","name":{"kind":"Name","value":"targetVersion"},"value":{"kind":"Variable","name":{"kind":"Name","value":"targetVersion"}}}]}]}}]} as unknown as DocumentNode; export const FindManyMarketplaceAppsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindManyMarketplaceApps"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifiers"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findManyMarketplaceApps"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"universalIdentifiers"},"value":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifiers"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MarketplaceAppFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MarketplaceAppFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MarketplaceApp"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"author"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePackage"}},{"kind":"Field","name":{"kind":"Name","value":"isVetted"}}]}}]} as unknown as DocumentNode; -export const FindMarketplaceAppDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindMarketplaceAppDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findMarketplaceAppDetail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"universalIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MarketplaceAppDetailFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MarketplaceAppDetailFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MarketplaceAppDetail"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sourceType"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePackage"}},{"kind":"Field","name":{"kind":"Name","value":"latestAvailableVersion"}},{"kind":"Field","name":{"kind":"Name","value":"isListed"}},{"kind":"Field","name":{"kind":"Name","value":"isVetted"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"author"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"websiteUrl"}},{"kind":"Field","name":{"kind":"Name","value":"aboutDescription"}},{"kind":"Field","name":{"kind":"Name","value":"termsUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailSupport"}},{"kind":"Field","name":{"kind":"Name","value":"issueReportUrl"}},{"kind":"Field","name":{"kind":"Name","value":"screenshots"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRoleUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"roles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateAllSettings"}},{"kind":"Field","name":{"kind":"Name","value":"canAccessAllTools"}},{"kind":"Field","name":{"kind":"Name","value":"canReadAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canSoftDeleteAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canDestroyAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"permissionFlagUniversalIdentifiers"}},{"kind":"Field","name":{"kind":"Name","value":"objectPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"objectUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"canReadObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canSoftDeleteObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canDestroyObjectRecords"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fieldPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"objectUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"fieldUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"canReadFieldValue"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateFieldValue"}}]}}]}}]}}]} as unknown as DocumentNode; +export const FindMarketplaceAppDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindMarketplaceAppDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findMarketplaceAppDetail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"universalIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MarketplaceAppDetailFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MarketplaceAppDetailFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MarketplaceAppDetail"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sourceType"}},{"kind":"Field","name":{"kind":"Name","value":"sourcePackage"}},{"kind":"Field","name":{"kind":"Name","value":"latestAvailableVersion"}},{"kind":"Field","name":{"kind":"Name","value":"isListed"}},{"kind":"Field","name":{"kind":"Name","value":"isVetted"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"author"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"websiteUrl"}},{"kind":"Field","name":{"kind":"Name","value":"aboutDescription"}},{"kind":"Field","name":{"kind":"Name","value":"termsUrl"}},{"kind":"Field","name":{"kind":"Name","value":"emailSupport"}},{"kind":"Field","name":{"kind":"Name","value":"issueReportUrl"}},{"kind":"Field","name":{"kind":"Name","value":"galleryImages"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRoleUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"roles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateAllSettings"}},{"kind":"Field","name":{"kind":"Name","value":"canAccessAllTools"}},{"kind":"Field","name":{"kind":"Name","value":"canReadAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canSoftDeleteAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canDestroyAllObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"permissionFlagUniversalIdentifiers"}},{"kind":"Field","name":{"kind":"Name","value":"objectPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"objectUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"canReadObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canSoftDeleteObjectRecords"}},{"kind":"Field","name":{"kind":"Name","value":"canDestroyObjectRecords"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fieldPermissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"universalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"objectUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"fieldUniversalIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"canReadFieldValue"}},{"kind":"Field","name":{"kind":"Name","value":"canUpdateFieldValue"}}]}}]}}]}}]} as unknown as DocumentNode; export const FindMarketplaceAppManifestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindMarketplaceAppManifest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findMarketplaceAppDetail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"universalIdentifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"universalIdentifier"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"manifest"}}]}}]}}]} as unknown as DocumentNode; export const CreateManyNavigationMenuItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateManyNavigationMenuItems"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"inputs"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateNavigationMenuItemInput"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createManyNavigationMenuItems"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"inputs"},"value":{"kind":"Variable","name":{"kind":"Name","value":"inputs"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NavigationMenuItemQueryFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NavigationMenuItemFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NavigationMenuItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"userWorkspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"targetRecordId"}},{"kind":"Field","name":{"kind":"Name","value":"targetObjectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"folderId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"link"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NavigationMenuItemQueryFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NavigationMenuItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NavigationMenuItemFields"}},{"kind":"Field","name":{"kind":"Name","value":"targetRecordIdentifier"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifier"}}]}}]}}]} as unknown as DocumentNode; export const DeleteManyNavigationMenuItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteManyNavigationMenuItems"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ids"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteManyNavigationMenuItems"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ids"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ids"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NavigationMenuItemQueryFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NavigationMenuItemFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NavigationMenuItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"userWorkspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"targetRecordId"}},{"kind":"Field","name":{"kind":"Name","value":"targetObjectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"folderId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"link"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NavigationMenuItemQueryFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NavigationMenuItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NavigationMenuItemFields"}},{"kind":"Field","name":{"kind":"Name","value":"targetRecordIdentifier"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifier"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifier"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/modules/marketplace/graphql/fragments/marketplaceAppDetailFragment.ts b/packages/twenty-front/src/modules/marketplace/graphql/fragments/marketplaceAppDetailFragment.ts index fd2e802f21..414ace6095 100644 --- a/packages/twenty-front/src/modules/marketplace/graphql/fragments/marketplaceAppDetailFragment.ts +++ b/packages/twenty-front/src/modules/marketplace/graphql/fragments/marketplaceAppDetailFragment.ts @@ -19,7 +19,7 @@ export const MARKETPLACE_APP_DETAIL_FRAGMENT = gql` termsUrl emailSupport issueReportUrl - screenshots + galleryImages defaultRoleUniversalIdentifier roles { universalIdentifier diff --git a/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx b/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx index 03f01b5d4c..afcc4891bf 100644 --- a/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx +++ b/packages/twenty-front/src/pages/settings/applications/SettingsApplicationDetails.tsx @@ -100,7 +100,7 @@ export const SettingsApplicationDetails = () => { const description = detail?.description ?? resolvedDescription; const getScreenshots = () => { - if (detail?.screenshots?.length) return detail.screenshots; + if (detail?.galleryImages?.length) return detail.galleryImages; if (isStandardApplication) return STANDARD_APPLICATION_ILLUSTRATIONS; if (isCustomApplication) return CUSTOM_APPLICATION_ILLUSTRATIONS; return undefined; diff --git a/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx b/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx index 6e6966ac1a..6b657b2c8f 100644 --- a/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx +++ b/packages/twenty-front/src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx @@ -221,7 +221,7 @@ export const SettingsAvailableApplicationDetails = () => { displayName={displayName} description={description} aboutDescription={detail.aboutDescription ?? undefined} - screenshots={detail.screenshots} + screenshots={detail.galleryImages} author={detail.author ?? 'Unknown'} category={detail.category ?? undefined} contentEntries={contentEntries} diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/minimal-app/__integration__/app-dev/expected-manifest.ts b/packages/twenty-sdk/src/cli/__tests__/apps/minimal-app/__integration__/app-dev/expected-manifest.ts index 3fce9a28ba..02687cc108 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/minimal-app/__integration__/app-dev/expected-manifest.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/minimal-app/__integration__/app-dev/expected-manifest.ts @@ -12,6 +12,7 @@ export const EXPECTED_MANIFEST: Manifest = { universalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000001', displayName: 'Root App', description: 'An app with all entities at root level', + galleryImages: [], defaultRoleUniversalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000002', packageJsonChecksum: '[checksum]', yarnLockChecksum: '[checksum]', diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts index 61a7333b15..1ad0594ebf 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts @@ -229,6 +229,7 @@ export const EXPECTED_MANIFEST: Manifest = { }, description: 'A simple rich app', displayName: 'Rich App', + galleryImages: [], defaultRoleUniversalIdentifier: 'b648f87b-1d26-4961-b974-0908fd991061', universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7', yarnLockChecksum: 'd41d8cd98f00b204e9800998ecf8427e', diff --git a/packages/twenty-sdk/src/cli/utilities/build/cover/__tests__/apply-generated-cover.test.ts b/packages/twenty-sdk/src/cli/utilities/build/cover/__tests__/apply-generated-cover.test.ts index 199dbdbf5f..d0e6f5ee5d 100644 --- a/packages/twenty-sdk/src/cli/utilities/build/cover/__tests__/apply-generated-cover.test.ts +++ b/packages/twenty-sdk/src/cli/utilities/build/cover/__tests__/apply-generated-cover.test.ts @@ -41,14 +41,14 @@ describe('applyGeneratedCover', () => { await writeFile(join(appPath, relativePath), Buffer.from('logo')); }; - it('generates a cover when a local logo exists and no screenshots are set', async () => { + it('generates a cover when a local logo exists and no gallery images are set', async () => { await writeLogo('public/logo.png'); - const manifest = buildManifest({ logoUrl: 'public/logo.png' }); + const manifest = buildManifest({ logo: 'public/logo.png' }); const result = await applyGeneratedCover({ appPath, manifest }); expect(mockedGenerateCoverImage).toHaveBeenCalledTimes(1); - expect(result.manifest.application.screenshots).toEqual([ + expect(result.manifest.application.galleryImages).toEqual([ GENERATED_COVER_PATH, ]); expect( @@ -68,28 +68,28 @@ describe('applyGeneratedCover', () => { expect(mockedGenerateCoverImage).not.toHaveBeenCalled(); expect(result.generatedAssets).toEqual([]); - expect(result.manifest.application.screenshots).toBeUndefined(); + expect(result.manifest.application.galleryImages).toBeUndefined(); }); - it('does nothing when screenshots are already provided', async () => { + it('does nothing when gallery images are already provided', async () => { await writeLogo('public/logo.png'); const manifest = buildManifest({ - logoUrl: 'public/logo.png', - screenshots: ['public/shot.png'], + logo: 'public/logo.png', + galleryImages: ['public/shot.png'], }); const result = await applyGeneratedCover({ appPath, manifest }); expect(mockedGenerateCoverImage).not.toHaveBeenCalled(); expect(result.generatedAssets).toEqual([]); - expect(result.manifest.application.screenshots).toEqual([ + expect(result.manifest.application.galleryImages).toEqual([ 'public/shot.png', ]); }); it('does nothing when the logo is an absolute url', async () => { const manifest = buildManifest({ - logoUrl: 'https://example.com/logo.png', + logo: 'https://example.com/logo.png', }); const result = await applyGeneratedCover({ appPath, manifest }); @@ -99,7 +99,7 @@ describe('applyGeneratedCover', () => { }); it('does nothing when the logo file is missing', async () => { - const manifest = buildManifest({ logoUrl: 'public/missing.png' }); + const manifest = buildManifest({ logo: 'public/missing.png' }); const result = await applyGeneratedCover({ appPath, manifest }); diff --git a/packages/twenty-sdk/src/cli/utilities/build/cover/apply-generated-cover.ts b/packages/twenty-sdk/src/cli/utilities/build/cover/apply-generated-cover.ts index 3bc206f8a1..daa5dc68eb 100644 --- a/packages/twenty-sdk/src/cli/utilities/build/cover/apply-generated-cover.ts +++ b/packages/twenty-sdk/src/cli/utilities/build/cover/apply-generated-cover.ts @@ -18,23 +18,23 @@ export const applyGeneratedCover = async ({ appPath: string; manifest: Manifest; }): Promise<{ manifest: Manifest; generatedAssets: GeneratedAsset[] }> => { - const { logoUrl, screenshots } = manifest.application; + const { logo, galleryImages } = manifest.application; - if (!isDefined(logoUrl) || isAbsoluteUrl(logoUrl)) { + if (!isDefined(logo) || isAbsoluteUrl(logo)) { return { manifest, generatedAssets: [] }; } - if ((screenshots ?? []).length > 0) { + if ((galleryImages ?? []).length > 0) { return { manifest, generatedAssets: [] }; } - const logoPath = join(appPath, logoUrl); + const logoAbsolutePath = join(appPath, logo); - if (!(await pathExists(logoPath))) { + if (!(await pathExists(logoAbsolutePath))) { return { manifest, generatedAssets: [] }; } - const logoBuffer = await readFile(logoPath); + const logoBuffer = await readFile(logoAbsolutePath); const coverBuffer = await generateCoverImage({ logoBuffer }); const coverAsset: AssetManifest = { @@ -56,7 +56,7 @@ export const applyGeneratedCover = async ({ ...manifest, application: { ...manifest.application, - screenshots: [GENERATED_COVER_PATH], + galleryImages: [GENERATED_COVER_PATH], }, publicAssets, }, diff --git a/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts b/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts index 14b93ebdfc..ebda0130cd 100644 --- a/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts +++ b/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts @@ -571,21 +571,31 @@ export const buildManifest = async ( const application: ApplicationManifest | undefined = applicationConfig && resolvedDefaultRoleUniversalIdentifier - ? { - ...applicationConfig, - defaultRoleUniversalIdentifier: - resolvedDefaultRoleUniversalIdentifier, - aboutDescription: readmeContent, - yarnLockChecksum: null, - packageJsonChecksum: null, - requiredServerVersionRange: getEngineVersionRange(appPath), - ...(postInstallLogicFunctions.length >= 1 - ? { postInstallLogicFunction: postInstallLogicFunctions[0] } - : {}), - ...(preInstallLogicFunctions.length >= 1 - ? { preInstallLogicFunction: preInstallLogicFunctions[0] } - : {}), - } + ? (() => { + const { + logoUrl: _logoUrl, + screenshots: _screenshots, + ...applicationConfigRest + } = applicationConfig; + + return { + ...applicationConfigRest, + logo: applicationConfig.logo, + galleryImages: applicationConfig.galleryImages ?? [], + defaultRoleUniversalIdentifier: + resolvedDefaultRoleUniversalIdentifier, + aboutDescription: readmeContent, + yarnLockChecksum: null, + packageJsonChecksum: null, + requiredServerVersionRange: getEngineVersionRange(appPath), + ...(postInstallLogicFunctions.length >= 1 + ? { postInstallLogicFunction: postInstallLogicFunctions[0] } + : {}), + ...(preInstallLogicFunctions.length >= 1 + ? { preInstallLogicFunction: preInstallLogicFunctions[0] } + : {}), + }; + })() : undefined; const byId = (a: T, b: T) => diff --git a/packages/twenty-sdk/src/sdk/define/application/__tests__/define-app.spec.ts b/packages/twenty-sdk/src/sdk/define/application/__tests__/define-app.spec.ts index 99e4c9a2b3..cc0797c30e 100644 --- a/packages/twenty-sdk/src/sdk/define/application/__tests__/define-app.spec.ts +++ b/packages/twenty-sdk/src/sdk/define/application/__tests__/define-app.spec.ts @@ -12,7 +12,11 @@ describe('defineApplication', () => { const result = defineApplication(config); expect(result.success).toBe(true); - expect(result.config).toEqual(config); + expect(result.config).toEqual({ + ...config, + logo: undefined, + galleryImages: [], + }); expect(result.errors).toEqual([]); }); @@ -34,7 +38,11 @@ describe('defineApplication', () => { const result = defineApplication(config); expect(result.success).toBe(true); - expect(result.config).toEqual(config); + expect(result.config).toEqual({ + ...config, + logo: undefined, + galleryImages: [], + }); expect(result.config?.applicationVariables).toBeDefined(); expect(result.config?.defaultRoleUniversalIdentifier).toBe( '68bb56f3-8300-4cb5-8cc3-8da9ee66f1b2', diff --git a/packages/twenty-sdk/src/sdk/define/application/define-application.ts b/packages/twenty-sdk/src/sdk/define/application/define-application.ts index bdb2941505..d4f928c932 100644 --- a/packages/twenty-sdk/src/sdk/define/application/define-application.ts +++ b/packages/twenty-sdk/src/sdk/define/application/define-application.ts @@ -7,6 +7,7 @@ import { FieldMetadataType } from 'twenty-shared/types'; import { isNonEmptyArray } from 'twenty-shared/utils'; import { type ApplicationConfig } from '@/sdk/define/application/application-config'; +import { normalizeApplicationAssets } from '@/sdk/define/application/utils/normalize-application-assets'; import { type DefineEntity } from '@/sdk/define/common/types/define-entity.type'; import { createValidationResult } from '@/sdk/define/common/utils/create-validation-result'; @@ -52,8 +53,16 @@ export const defineApplication: DefineEntity = (config) => { ); } + const assetNormalization = normalizeApplicationAssets(config); + + warnings.push(...assetNormalization.warnings); + return createValidationResult({ - config, + config: { + ...config, + logo: assetNormalization.logo, + galleryImages: assetNormalization.galleryImages, + }, errors, warnings, }); diff --git a/packages/twenty-sdk/src/sdk/define/application/utils/__tests__/normalize-application-assets.test.ts b/packages/twenty-sdk/src/sdk/define/application/utils/__tests__/normalize-application-assets.test.ts new file mode 100644 index 0000000000..189d0e2cde --- /dev/null +++ b/packages/twenty-sdk/src/sdk/define/application/utils/__tests__/normalize-application-assets.test.ts @@ -0,0 +1,98 @@ +import { describe, expect, it } from 'vitest'; + +import { normalizeApplicationAssets } from '@/sdk/define/application/utils/normalize-application-assets'; +import { type ApplicationConfig } from '@/sdk/define/application/application-config'; + +const buildConfig = (config: Record): ApplicationConfig => + config as unknown as ApplicationConfig; + +describe('normalizeApplicationAssets', () => { + it('keeps a bundled logo', () => { + const result = normalizeApplicationAssets( + buildConfig({ logo: 'public/logo.png' }), + ); + + expect(result.logo).toBe('public/logo.png'); + expect(result.warnings).toEqual([]); + }); + + it('warns and ignores an absolute logo', () => { + const result = normalizeApplicationAssets( + buildConfig({ logo: 'https://example.com/logo.png' }), + ); + + expect(result.logo).toBeUndefined(); + expect(result.warnings).toHaveLength(1); + expect(result.warnings[0]).toContain('external URL'); + }); + + it('migrates a deprecated relative logoUrl with a warning', () => { + const result = normalizeApplicationAssets( + buildConfig({ logoUrl: 'public/logo.png' }), + ); + + expect(result.logo).toBe('public/logo.png'); + expect(result.warnings[0]).toContain('`logoUrl` is deprecated'); + }); + + it('warns and ignores an absolute logoUrl', () => { + const result = normalizeApplicationAssets( + buildConfig({ logoUrl: 'https://example.com/logo.png' }), + ); + + expect(result.logo).toBeUndefined(); + expect(result.warnings[0]).toContain('external URL'); + }); + + it('prefers logo over a deprecated logoUrl', () => { + const result = normalizeApplicationAssets( + buildConfig({ + logo: 'public/logo.png', + logoUrl: 'public/old.png', + }), + ); + + expect(result.logo).toBe('public/logo.png'); + expect(result.warnings).toEqual([]); + }); + + it('keeps galleryImages as-is', () => { + const result = normalizeApplicationAssets( + buildConfig({ + galleryImages: ['public/a.png', 'public/b.png'], + }), + ); + + expect(result.galleryImages).toEqual(['public/a.png', 'public/b.png']); + expect(result.warnings).toEqual([]); + }); + + it('migrates deprecated screenshots to galleryImages with a warning', () => { + const result = normalizeApplicationAssets( + buildConfig({ screenshots: ['public/a.png', 'public/b.png'] }), + ); + + expect(result.galleryImages).toEqual(['public/a.png', 'public/b.png']); + expect(result.warnings[0]).toContain('`screenshots` is deprecated'); + }); + + it('warns and drops absolute urls from gallery images', () => { + const result = normalizeApplicationAssets( + buildConfig({ + galleryImages: ['public/a.png', 'https://example.com/b.png'], + }), + ); + + expect(result.galleryImages).toEqual(['public/a.png']); + expect(result.warnings).toHaveLength(1); + expect(result.warnings[0]).toContain('external URL'); + }); + + it('returns an empty gallery when nothing is provided', () => { + const result = normalizeApplicationAssets(buildConfig({})); + + expect(result.logo).toBeUndefined(); + expect(result.galleryImages).toEqual([]); + expect(result.warnings).toEqual([]); + }); +}); diff --git a/packages/twenty-sdk/src/sdk/define/application/utils/normalize-application-assets.ts b/packages/twenty-sdk/src/sdk/define/application/utils/normalize-application-assets.ts new file mode 100644 index 0000000000..23fd7a8a00 --- /dev/null +++ b/packages/twenty-sdk/src/sdk/define/application/utils/normalize-application-assets.ts @@ -0,0 +1,62 @@ +import { type ApplicationConfig } from '@/sdk/define/application/application-config'; + +const isAbsoluteUrl = (value: string): boolean => + value.startsWith('http://') || value.startsWith('https://'); + +export const normalizeApplicationAssets = ( + application: ApplicationConfig, +): { + logo?: string; + galleryImages: string[]; + warnings: string[]; +} => { + const warnings: string[] = []; + + let logo = application.logo; + + if (logo && isAbsoluteUrl(logo)) { + warnings.push( + `Application logo "${logo}" is an external URL. External asset URLs are no longer supported and are ignored. Bundle the image in your public/ folder instead.`, + ); + logo = undefined; + } + + if (!logo && application.logoUrl) { + if (isAbsoluteUrl(application.logoUrl)) { + warnings.push( + `Application logoUrl "${application.logoUrl}" is an external URL. External asset URLs are no longer supported and are ignored. Bundle the image in your public/ folder and reference it via logo.`, + ); + } else { + warnings.push( + '`logoUrl` is deprecated. Use `logo` to reference an image bundled in your public/ folder.', + ); + logo = application.logoUrl; + } + } + + const usesDeprecatedScreenshots = + !application.galleryImages && (application.screenshots?.length ?? 0) > 0; + + if (usesDeprecatedScreenshots) { + warnings.push( + '`screenshots` is deprecated. Use `galleryImages` referencing images bundled in your public/ folder.', + ); + } + + const rawGalleryImages = + application.galleryImages ?? application.screenshots ?? []; + + const galleryImages = rawGalleryImages.filter((galleryImage) => { + if (isAbsoluteUrl(galleryImage)) { + warnings.push( + `Gallery image "${galleryImage}" is an external URL. External asset URLs are no longer supported and are ignored.`, + ); + + return false; + } + + return true; + }); + + return { logo, galleryImages, warnings }; +}; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1783615890055-add-gallery-images-to-application-registration.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1783615890055-add-gallery-images-to-application-registration.ts new file mode 100644 index 0000000000..c4353f61fb --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1783615890055-add-gallery-images-to-application-registration.ts @@ -0,0 +1,21 @@ +import { type QueryRunner } from 'typeorm'; + +import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator'; +import { type FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface'; + +@RegisteredInstanceCommand('2.20.0', 1783615890055) +export class AddGalleryImagesToApplicationRegistrationFastInstanceCommand + implements FastInstanceCommand +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + 'ALTER TABLE "core"."applicationRegistration" ADD COLUMN IF NOT EXISTS "galleryImages" jsonb', + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + 'ALTER TABLE "core"."applicationRegistration" DROP COLUMN IF EXISTS "galleryImages"', + ); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-slow-1783615890056-backfill-gallery-images-on-application-registration.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-slow-1783615890056-backfill-gallery-images-on-application-registration.ts new file mode 100644 index 0000000000..152d042a60 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-20/2-20-instance-command-slow-1783615890056-backfill-gallery-images-on-application-registration.ts @@ -0,0 +1,33 @@ +import { type DataSource, type QueryRunner } from 'typeorm'; + +import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator'; +import { type SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface'; + +@RegisteredInstanceCommand('2.20.0', 1783615890056, { type: 'slow' }) +export class BackfillGalleryImagesOnApplicationRegistrationSlowInstanceCommand + implements SlowInstanceCommand +{ + async runDataMigration(dataSource: DataSource): Promise { + await dataSource.query( + `UPDATE "core"."applicationRegistration" AS "registration" + SET "galleryImages" = "screenshotEntries"."galleryImages" + FROM ( + SELECT + "id", + jsonb_agg( + jsonb_build_object('path', "screenshot", 'fileId', NULL) + ORDER BY "ordinality" + ) AS "galleryImages" + FROM "core"."applicationRegistration", + unnest("screenshots") WITH ORDINALITY AS "unnested"("screenshot", "ordinality") + GROUP BY "id" + ) AS "screenshotEntries" + WHERE "registration"."id" = "screenshotEntries"."id" + AND "registration"."galleryImages" IS NULL`, + ); + } + + public async up(_queryRunner: QueryRunner): Promise {} + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts index d4ee164e97..b17594410d 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts @@ -105,6 +105,8 @@ import { BackfillNameFieldIsSystemSideEffectSlowInstanceCommand } from './2-20/2 import { RenameIsFeaturedToIsVettedOnApplicationRegistrationFastInstanceCommand } from './2-20/2-20-instance-command-fast-1783527064000-rename-is-featured-to-is-vetted-on-application-registration'; import { AddIsSystemSideEffectToSearchFieldMetadataFastInstanceCommand } from './2-20/2-20-instance-command-fast-1783580127637-add-is-system-side-effect-to-search-field-metadata'; import { CreateWorkflowCoreTableFastInstanceCommand } from './2-20/2-20-instance-command-fast-1783603454479-create-workflow-core-table'; +import { AddGalleryImagesToApplicationRegistrationFastInstanceCommand } from './2-20/2-20-instance-command-fast-1783615890055-add-gallery-images-to-application-registration'; +import { BackfillGalleryImagesOnApplicationRegistrationSlowInstanceCommand } from './2-20/2-20-instance-command-slow-1783615890056-backfill-gallery-images-on-application-registration'; import { AddWorkflowVersionSyncableColumnsFastInstanceCommand } from './2-20/2-20-instance-command-fast-1783603454480-add-workflow-version-syncable-columns'; export const INSTANCE_COMMANDS = [ @@ -213,5 +215,7 @@ export const INSTANCE_COMMANDS = [ RenameIsFeaturedToIsVettedOnApplicationRegistrationFastInstanceCommand, AddIsSystemSideEffectToSearchFieldMetadataFastInstanceCommand, CreateWorkflowCoreTableFastInstanceCommand, + AddGalleryImagesToApplicationRegistrationFastInstanceCommand, + BackfillGalleryImagesOnApplicationRegistrationSlowInstanceCommand, AddWorkflowVersionSyncableColumnsFastInstanceCommand, ]; diff --git a/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.service.ts b/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.service.ts index 8e64131b41..40459b7780 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-install/application-install.service.ts @@ -14,6 +14,7 @@ import { ApplicationException, ApplicationExceptionCode, } from 'src/engine/core-modules/application/application.exception'; +import { isImageFilePath } from 'src/engine/core-modules/application/application-registration/utils/is-image-file-path.util'; import { ApplicationRegistrationEntity } from 'src/engine/core-modules/application/application-registration/application-registration.entity'; import { ApplicationRegistrationService } from 'src/engine/core-modules/application/application-registration/application-registration.service'; import { ApplicationRegistrationSourceType } from 'src/engine/core-modules/application/application-registration/enums/application-registration-source-type.enum'; @@ -559,17 +560,25 @@ export class ApplicationInstallService { applicationUniversalIdentifier: string; workspaceId: string; }): Promise { - const logoUrl = manifest.application.logoUrl; + const logo = manifest.application.logo ?? manifest.application.logoUrl; if ( - !isDefined(logoUrl) || - logoUrl.startsWith('http://') || - logoUrl.startsWith('https://') + !isDefined(logo) || + logo.startsWith('http://') || + logo.startsWith('https://') ) { return null; } - const absolutePath = this.resolveWithinDirOrThrow(extractedDir, logoUrl); + if (!isImageFilePath(logo)) { + this.logger.warn( + `Logo "${logo}" is not a supported image type; skipping logo import for ${applicationUniversalIdentifier}`, + ); + + return null; + } + + const absolutePath = this.resolveWithinDirOrThrow(extractedDir, logo); let content: Buffer; @@ -577,7 +586,7 @@ export class ApplicationInstallService { content = await fs.readFile(absolutePath); } catch { this.logger.warn( - `Logo "${logoUrl}" declared in manifest but not found in package for ${applicationUniversalIdentifier}; skipping logo import`, + `Logo "${logo}" declared in manifest but not found in package for ${applicationUniversalIdentifier}; skipping logo import`, ); return null; @@ -588,7 +597,7 @@ export class ApplicationInstallService { fileFolder: FileFolder.PublicAsset, applicationUniversalIdentifier, workspaceId, - resourcePath: logoUrl, + resourcePath: logo, settings: { isTemporaryFile: false, toDelete: false }, }); diff --git a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/dtos/marketplace-app-detail.dto.ts b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/dtos/marketplace-app-detail.dto.ts index a2dd0eac43..cb2829b8d8 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/dtos/marketplace-app-detail.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/dtos/marketplace-app-detail.dto.ts @@ -90,9 +90,14 @@ export class MarketplaceAppDetailDTO { @Field({ nullable: true }) issueReportUrl?: string; - @Field(() => [String]) + @Field(() => [String], { + deprecationReason: 'Use galleryImages instead', + }) screenshots: string[]; + @Field(() => [String]) + galleryImages: string[]; + @IsOptional() @IsString() @Field({ nullable: true }) diff --git a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace-query.service.ts b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace-query.service.ts index 0af47344e8..0f68cc816e 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace-query.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/marketplace-query.service.ts @@ -13,6 +13,7 @@ import { ApplicationRegistrationException, ApplicationRegistrationExceptionCode, } from 'src/engine/core-modules/application/application-registration/application-registration.exception'; +import { toGalleryImagePaths } from 'src/engine/core-modules/application/application-registration/utils/to-gallery-image-paths.util'; import { ApplicationRegistrationService } from 'src/engine/core-modules/application/application-registration/application-registration.service'; @Injectable() @@ -81,6 +82,14 @@ export class MarketplaceQueryService { private toMarketplaceAppDetailDTO( registration: ApplicationRegistrationEntity, ): MarketplaceAppDetailDTO { + // TODO: simplify in a follow-up PR to read galleryImages only, once the + // deprecated screenshots column and manifest fallback are backfilled away. + const galleryImagePaths = isNonEmptyArray(registration.galleryImages) + ? registration.galleryImages.map((galleryImage) => galleryImage.path) + : isNonEmptyArray(registration.screenshots) + ? registration.screenshots + : toGalleryImagePaths(registration.manifest?.application); + return { id: registration.id, universalIdentifier: registration.universalIdentifier, @@ -123,9 +132,8 @@ export class MarketplaceQueryService { registration.issueReportUrl ?? registration.manifest?.application?.issueReportUrl ?? undefined, - screenshots: isNonEmptyArray(registration.screenshots) - ? registration.screenshots - : (registration.manifest?.application?.screenshots ?? []), + screenshots: galleryImagePaths, + galleryImages: galleryImagePaths, defaultRoleUniversalIdentifier: registration.manifest?.application?.defaultRoleUniversalIdentifier, roles: registration.manifest?.roles?.map((role) => diff --git a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/utils/__tests__/resolve-manifest-asset-urls.util.spec.ts b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/utils/__tests__/resolve-manifest-asset-urls.util.spec.ts index e7f09945fd..9f5952e1ae 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/utils/__tests__/resolve-manifest-asset-urls.util.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/utils/__tests__/resolve-manifest-asset-urls.util.spec.ts @@ -131,6 +131,47 @@ describe('resolveManifestAssetUrls', () => { expect(result.application.screenshots).toEqual([]); }); + it('should resolve a relative logo', () => { + const manifest = buildMinimalManifest({ logo: 'logo.png' }); + + const result = resolveManifestAssetUrls(manifest, urlBuilder); + + expect(result.application.logo).toBe( + 'https://cdn.example.com/pkg/logo.png', + ); + }); + + it('should not modify an absolute logo', () => { + const manifest = buildMinimalManifest({ + logo: 'https://example.com/logo.png', + }); + + const result = resolveManifestAssetUrls(manifest, urlBuilder); + + expect(result.application.logo).toBe('https://example.com/logo.png'); + }); + + it('should resolve galleryImages paths and leave absolute ones untouched', () => { + const manifest = buildMinimalManifest({ + galleryImages: ['a.png', 'https://example.com/b.png'], + }); + + const result = resolveManifestAssetUrls(manifest, urlBuilder); + + expect(result.application.galleryImages).toEqual([ + 'https://cdn.example.com/pkg/a.png', + 'https://example.com/b.png', + ]); + }); + + it('should handle undefined galleryImages', () => { + const manifest = buildMinimalManifest(); + + const result = resolveManifestAssetUrls(manifest, urlBuilder); + + expect(result.application.galleryImages).toEqual([]); + }); + it('should preserve all other manifest properties', () => { const manifest = buildMinimalManifest({ logoUrl: 'logo.png', diff --git a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/utils/resolve-manifest-asset-urls.util.ts b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/utils/resolve-manifest-asset-urls.util.ts index a9cf6a9759..7433841736 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-marketplace/utils/resolve-manifest-asset-urls.util.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-marketplace/utils/resolve-manifest-asset-urls.util.ts @@ -17,7 +17,11 @@ export const resolveManifestAssetUrls = ( logoUrl: manifest.application.logoUrl ? resolveUrl(manifest.application.logoUrl) : undefined, + logo: manifest.application.logo + ? resolveUrl(manifest.application.logo) + : undefined, screenshots: (manifest.application.screenshots ?? []).map(resolveUrl), + galleryImages: (manifest.application.galleryImages ?? []).map(resolveUrl), }, }; }; diff --git a/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.entity.ts b/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.entity.ts index e029145dec..3ff754e696 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.entity.ts @@ -20,6 +20,7 @@ import { import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { type Manifest } from 'twenty-shared/application'; import { ApplicationRegistrationVariableEntity } from 'src/engine/core-modules/application/application-registration-variable/application-registration-variable.entity'; +import { type ApplicationRegistrationGalleryImage } from 'src/engine/core-modules/application/application-registration/types/application-registration-gallery-image.type'; import { ApplicationRegistrationSourceType } from 'src/engine/core-modules/application/application-registration/enums/application-registration-source-type.enum'; import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity'; import { WasIntroducedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-introduced-in-upgrade.decorator'; @@ -214,7 +215,12 @@ export class ApplicationRegistrationEntity { @Field(() => String, { nullable: true }) get logoUrl(): string | null { - return this.logo ?? this.manifest?.application?.logoUrl ?? null; + return ( + this.logo ?? + this.manifest?.application?.logo ?? + this.manifest?.application?.logoUrl ?? + null + ); } @OneToMany( @@ -224,6 +230,13 @@ export class ApplicationRegistrationEntity { ) variables: Relation; + @Column({ type: 'jsonb', nullable: true }) + @WasIntroducedInUpgrade({ + upgradeCommandName: + '2.20.0_AddGalleryImagesToApplicationRegistrationFastInstanceCommand_1783615890055', + }) + galleryImages: ApplicationRegistrationGalleryImage[] | null; + @Field() @CreateDateColumn({ type: 'timestamptz' }) createdAt: Date; diff --git a/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.service.ts b/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.service.ts index 702787e699..4536eb656a 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-registration/application-registration.service.ts @@ -65,6 +65,7 @@ const APPLICATION_REGISTRATION_WITHOUT_MANIFEST_SELECT: (keyof ApplicationRegist 'emailSupport', 'issueReportUrl', 'screenshots', + 'galleryImages', 'createdAt', 'updatedAt', ]; diff --git a/packages/twenty-server/src/engine/core-modules/application/application-registration/application-tarball.service.ts b/packages/twenty-server/src/engine/core-modules/application/application-registration/application-tarball.service.ts index 2187bd3857..fb2125558e 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-registration/application-tarball.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-registration/application-tarball.service.ts @@ -3,10 +3,10 @@ import { InjectRepository } from '@nestjs/typeorm'; import { promises as fs } from 'fs'; import { tmpdir } from 'os'; -import { join } from 'path'; +import { isAbsolute, join, relative, resolve } from 'path'; import semver from 'semver'; -import { FileFolder } from 'twenty-shared/types'; +import { FileFolder, ServerFileFolder } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; import { type QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'; @@ -24,9 +24,14 @@ import { ApplicationRegistrationExceptionCode, } from 'src/engine/core-modules/application/application-registration/application-registration.exception'; import { ApplicationRegistrationSourceType } from 'src/engine/core-modules/application/application-registration/enums/application-registration-source-type.enum'; +import { type ApplicationRegistrationGalleryImage } from 'src/engine/core-modules/application/application-registration/types/application-registration-gallery-image.type'; import { fromManifestApplicationToDisplayFields } from 'src/engine/core-modules/application/application-registration/utils/from-manifest-application-to-display-fields.util'; +import { isImageFilePath } from 'src/engine/core-modules/application/application-registration/utils/is-image-file-path.util'; +import { toGalleryImagePaths } from 'src/engine/core-modules/application/application-registration/utils/to-gallery-image-paths.util'; import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { FileStorageService } from 'src/engine/core-modules/file-storage/services/file-storage.service'; +import { ServerFileStorageService } from 'src/engine/core-modules/file-storage/services/server-file-storage.service'; +import { prepareFileForStorageOrThrow } from 'src/engine/core-modules/file-storage/utils/prepare-file-for-storage-or-throw.util'; import type { ApplicationManifest } from 'twenty-shared/application'; @Injectable() @@ -37,6 +42,7 @@ export class ApplicationTarballService { @InjectRepository(ApplicationRegistrationEntity) private readonly appRegistrationRepository: Repository, private readonly fileStorageService: FileStorageService, + private readonly serverFileStorageService: ServerFileStorageService, private readonly applicationService: ApplicationService, private readonly applicationRegistrationVariableService: ApplicationRegistrationVariableService, private readonly applicationVersionValidationService: ApplicationVersionValidationService, @@ -202,6 +208,12 @@ export class ApplicationTarballService { ownerWorkspaceId: params.ownerWorkspaceId, } as QueryDeepPartialEntity); + await this.storeGalleryImageFiles({ + applicationRegistrationId: appRegistration.id, + manifestApplication: manifest.application, + contentDir, + }); + if (manifest.application?.serverVariables) { await this.applicationRegistrationVariableService.syncVariableSchemas( appRegistration.id, @@ -220,4 +232,92 @@ export class ApplicationTarballService { await fs.rm(tempDir, { recursive: true, force: true }); } } + + private async storeGalleryImageFiles({ + applicationRegistrationId, + manifestApplication, + contentDir, + }: { + applicationRegistrationId: string; + manifestApplication: ApplicationManifest | undefined; + contentDir: string; + }): Promise { + const galleryImages: ApplicationRegistrationGalleryImage[] = []; + + for (const path of toGalleryImagePaths(manifestApplication)) { + const fileId = await this.storeGalleryImageFile({ + applicationRegistrationId, + contentDir, + path, + }); + + if (isDefined(fileId)) { + galleryImages.push({ path, fileId }); + } + } + + await this.appRegistrationRepository.update(applicationRegistrationId, { + galleryImages, + } as QueryDeepPartialEntity); + } + + private async storeGalleryImageFile({ + applicationRegistrationId, + contentDir, + path, + }: { + applicationRegistrationId: string; + contentDir: string; + path: string; + }): Promise { + if ( + path.startsWith('http://') || + path.startsWith('https://') || + !isImageFilePath(path) + ) { + return null; + } + + const absolutePath = resolve(contentDir, path); + const relativeToContentDir = relative(contentDir, absolutePath); + + if ( + relativeToContentDir === '..' || + relativeToContentDir.startsWith('../') || + isAbsolute(relativeToContentDir) + ) { + this.logger.warn( + `Gallery image "${path}" escapes the package directory; skipping`, + ); + + return null; + } + + try { + const contents = await fs.readFile(absolutePath); + + const { sourceFile, mimeType } = await prepareFileForStorageOrThrow({ + sourceFile: contents, + resourcePath: path, + }); + + const savedFile = await this.serverFileStorageService.writeServerFile({ + fileFolder: ServerFileFolder.ApplicationRegistration, + applicationRegistrationId, + resourcePath: path, + contents: Buffer.isBuffer(sourceFile) + ? sourceFile + : Buffer.from(sourceFile), + mimeType, + }); + + return savedFile.id; + } catch (error) { + this.logger.warn( + `Failed to store gallery image "${path}" for registration ${applicationRegistrationId}: ${error.message}`, + ); + + return null; + } + } } diff --git a/packages/twenty-server/src/engine/core-modules/application/application-registration/types/application-registration-gallery-image.type.ts b/packages/twenty-server/src/engine/core-modules/application/application-registration/types/application-registration-gallery-image.type.ts new file mode 100644 index 0000000000..b0e697ed6c --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/application/application-registration/types/application-registration-gallery-image.type.ts @@ -0,0 +1,4 @@ +export type ApplicationRegistrationGalleryImage = { + path: string; + fileId: string | null; +}; diff --git a/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/__tests__/is-image-file-path.util.spec.ts b/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/__tests__/is-image-file-path.util.spec.ts new file mode 100644 index 0000000000..90d75f2cc7 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/__tests__/is-image-file-path.util.spec.ts @@ -0,0 +1,26 @@ +import { isImageFilePath } from 'src/engine/core-modules/application/application-registration/utils/is-image-file-path.util'; + +describe('isImageFilePath', () => { + it.each([ + 'public/logo.png', + 'public/shot.JPG', + 'a/b/c.jpeg', + 'image.webp', + 'anim.gif', + 'icon.svg', + 'photo.avif', + ])('returns true for image path %s', (filePath) => { + expect(isImageFilePath(filePath)).toBe(true); + }); + + it.each([ + 'public/data.json', + 'public/styles.css', + 'script.mjs', + 'README', + 'archive.tar.gz', + 'noextension', + ])('returns false for non-image path %s', (filePath) => { + expect(isImageFilePath(filePath)).toBe(false); + }); +}); diff --git a/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/from-manifest-application-to-display-fields.util.ts b/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/from-manifest-application-to-display-fields.util.ts index 31ff997b3d..5702660f9a 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/from-manifest-application-to-display-fields.util.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/from-manifest-application-to-display-fields.util.ts @@ -1,9 +1,12 @@ import { type ApplicationManifest } from 'twenty-shared/application'; +import { type ApplicationRegistrationGalleryImage } from 'src/engine/core-modules/application/application-registration/types/application-registration-gallery-image.type'; +import { toGalleryImagePaths } from 'src/engine/core-modules/application/application-registration/utils/to-gallery-image-paths.util'; + export const fromManifestApplicationToDisplayFields = ( application: ApplicationManifest | undefined, ) => ({ - logo: application?.logoUrl ?? null, + logo: application?.logo ?? application?.logoUrl ?? null, description: application?.description ?? null, author: application?.author ?? null, category: application?.category ?? null, @@ -12,5 +15,7 @@ export const fromManifestApplicationToDisplayFields = ( termsUrl: application?.termsUrl ?? null, emailSupport: application?.emailSupport ?? null, issueReportUrl: application?.issueReportUrl ?? null, - screenshots: application?.screenshots ?? [], + galleryImages: toGalleryImagePaths(application).map( + (path): ApplicationRegistrationGalleryImage => ({ path, fileId: null }), + ), }); diff --git a/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/is-image-file-path.util.ts b/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/is-image-file-path.util.ts new file mode 100644 index 0000000000..06d023a51e --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/is-image-file-path.util.ts @@ -0,0 +1,21 @@ +const ALLOWED_IMAGE_EXTENSIONS = new Set([ + '.png', + '.jpg', + '.jpeg', + '.webp', + '.gif', + '.svg', + '.avif', +]); + +export const isImageFilePath = (filePath: string): boolean => { + const lastDotIndex = filePath.lastIndexOf('.'); + + if (lastDotIndex === -1) { + return false; + } + + return ALLOWED_IMAGE_EXTENSIONS.has( + filePath.slice(lastDotIndex).toLowerCase(), + ); +}; diff --git a/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/to-gallery-image-paths.util.ts b/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/to-gallery-image-paths.util.ts new file mode 100644 index 0000000000..646a7ac343 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/application/application-registration/utils/to-gallery-image-paths.util.ts @@ -0,0 +1,13 @@ +import { type ApplicationManifest } from 'twenty-shared/application'; + +export const toGalleryImagePaths = ( + application: ApplicationManifest | undefined, +): string[] => { + const galleryImages = application?.galleryImages; + + if (galleryImages && galleryImages.length > 0) { + return galleryImages; + } + + return application?.screenshots ?? []; +}; diff --git a/packages/twenty-shared/src/application/applicationType.ts b/packages/twenty-shared/src/application/applicationType.ts index 40a9c83e80..e81a04c843 100644 --- a/packages/twenty-shared/src/application/applicationType.ts +++ b/packages/twenty-shared/src/application/applicationType.ts @@ -13,8 +13,16 @@ export type ApplicationManifest = SyncableEntityOptions & { serverVariables?: ServerVariables; author?: string; category?: ApplicationCategory; + /** + * @deprecated Use `logo` instead. + */ logoUrl?: string; + logo?: string; + /** + * @deprecated Use `galleryImages` instead. + */ screenshots?: string[]; + galleryImages?: string[]; aboutDescription?: string; websiteUrl?: string; termsUrl?: string;