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;