diff --git a/.storybook/global.scss b/.storybook/global.scss index 47271789..13a9520a 100644 --- a/.storybook/global.scss +++ b/.storybook/global.scss @@ -4,10 +4,7 @@ html { } .sb-show-main { - background: url(https://applescoop.org/image/wallpapers/mac/windows-10-stock-hd-cells-glowing-dark-22-09-2024-1727068449-hd-wallpaper.jpeg); - transition: background-color 0.3s; - background-position: center; - background-size: cover; + background: #030014 !important; } .sb-main-fullscreen { diff --git a/code0-tech-sagittarius-graphql-types.ts b/code0-tech-sagittarius-graphql-types.ts new file mode 100644 index 00000000..da2c19fe --- /dev/null +++ b/code0-tech-sagittarius-graphql-types.ts @@ -0,0 +1,2690 @@ +export type Maybe = T | null; +export type InputMaybe = Maybe; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +export type MakeEmpty = { [_ in K]?: never }; +export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; +/** All built-in and custom scalars, mapped to their actual values */ +export interface Scalars { + ID: { input: `gid://sagittarius//${number}`; output: `gid://sagittarius//${number}`; } + String: { input: string; output: string; } + Boolean: { input: boolean; output: boolean; } + Int: { input: number; output: number; } + Float: { input: number; output: number; } + /** A unique identifier for all DataType entities of the application */ + DataTypeID: { input: `gid://sagittarius/DataType/${number}`; output: `gid://sagittarius/DataType/${number}`; } + /** A unique identifier for all DataTypeIdentifier entities of the application */ + DataTypeIdentifierID: { input: `gid://sagittarius/DataTypeIdentifier/${number}`; output: `gid://sagittarius/DataTypeIdentifier/${number}`; } + /** A unique identifier for all DataTypeRule entities of the application */ + DataTypeRuleID: { input: `gid://sagittarius/DataTypeRule/${number}`; output: `gid://sagittarius/DataTypeRule/${number}`; } + /** A unique identifier for all Flow entities of the application */ + FlowID: { input: `gid://sagittarius/Flow/${number}`; output: `gid://sagittarius/Flow/${number}`; } + /** A unique identifier for all FlowSetting entities of the application */ + FlowSettingID: { input: `gid://sagittarius/FlowSetting/${number}`; output: `gid://sagittarius/FlowSetting/${number}`; } + /** A unique identifier for all FlowType entities of the application */ + FlowTypeID: { input: `gid://sagittarius/FlowType/${number}`; output: `gid://sagittarius/FlowType/${number}`; } + /** A unique identifier for all FlowTypeSetting entities of the application */ + FlowTypeSettingID: { input: `gid://sagittarius/FlowTypeSetting/${number}`; output: `gid://sagittarius/FlowTypeSetting/${number}`; } + /** A unique identifier for all FunctionDefinition entities of the application */ + FunctionDefinitionID: { input: `gid://sagittarius/FunctionDefinition/${number}`; output: `gid://sagittarius/FunctionDefinition/${number}`; } + /** A unique identifier for all GenericCombinationStrategy entities of the application */ + GenericCombinationStrategyID: { input: `gid://sagittarius/GenericCombinationStrategy/${number}`; output: `gid://sagittarius/GenericCombinationStrategy/${number}`; } + /** A unique identifier for all GenericMapper entities of the application */ + GenericMapperID: { input: `gid://sagittarius/GenericMapper/${number}`; output: `gid://sagittarius/GenericMapper/${number}`; } + /** A unique identifier for all GenericType entities of the application */ + GenericTypeID: { input: `gid://sagittarius/GenericType/${number}`; output: `gid://sagittarius/GenericType/${number}`; } + /** Represents untyped JSON */ + JSON: { input: any; output: any; } + /** A unique identifier for all Namespace entities of the application */ + NamespaceID: { input: `gid://sagittarius/Namespace/${number}`; output: `gid://sagittarius/Namespace/${number}`; } + /** A unique identifier for all NamespaceLicense entities of the application */ + NamespaceLicenseID: { input: `gid://sagittarius/NamespaceLicense/${number}`; output: `gid://sagittarius/NamespaceLicense/${number}`; } + /** A unique identifier for all NamespaceMember entities of the application */ + NamespaceMemberID: { input: `gid://sagittarius/NamespaceMember/${number}`; output: `gid://sagittarius/NamespaceMember/${number}`; } + /** A unique identifier for all NamespaceMemberRole entities of the application */ + NamespaceMemberRoleID: { input: `gid://sagittarius/NamespaceMemberRole/${number}`; output: `gid://sagittarius/NamespaceMemberRole/${number}`; } + /** A unique identifier for all NamespaceProject entities of the application */ + NamespaceProjectID: { input: `gid://sagittarius/NamespaceProject/${number}`; output: `gid://sagittarius/NamespaceProject/${number}`; } + /** A unique identifier for all NamespaceRole entities of the application */ + NamespaceRoleID: { input: `gid://sagittarius/NamespaceRole/${number}`; output: `gid://sagittarius/NamespaceRole/${number}`; } + /** A unique identifier for all NodeFunction entities of the application */ + NodeFunctionID: { input: `gid://sagittarius/NodeFunction/${number}`; output: `gid://sagittarius/NodeFunction/${number}`; } + /** A unique identifier for all NodeParameter entities of the application */ + NodeParameterID: { input: `gid://sagittarius/NodeParameter/${number}`; output: `gid://sagittarius/NodeParameter/${number}`; } + /** A unique identifier for all Organization entities of the application */ + OrganizationID: { input: `gid://sagittarius/Organization/${number}`; output: `gid://sagittarius/Organization/${number}`; } + /** A unique identifier for all ParameterDefinition entities of the application */ + ParameterDefinitionID: { input: `gid://sagittarius/ParameterDefinition/${number}`; output: `gid://sagittarius/ParameterDefinition/${number}`; } + /** A unique identifier for all ReferencePath entities of the application */ + ReferencePathID: { input: `gid://sagittarius/ReferencePath/${number}`; output: `gid://sagittarius/ReferencePath/${number}`; } + /** A unique identifier for all ReferenceValue entities of the application */ + ReferenceValueID: { input: `gid://sagittarius/ReferenceValue/${number}`; output: `gid://sagittarius/ReferenceValue/${number}`; } + /** A unique identifier for all RuntimeFunctionDefinition entities of the application */ + RuntimeFunctionDefinitionID: { input: `gid://sagittarius/RuntimeFunctionDefinition/${number}`; output: `gid://sagittarius/RuntimeFunctionDefinition/${number}`; } + /** A unique identifier for all Runtime entities of the application */ + RuntimeID: { input: `gid://sagittarius/Runtime/${number}`; output: `gid://sagittarius/Runtime/${number}`; } + /** A unique identifier for all RuntimeParameterDefinition entities of the application */ + RuntimeParameterDefinitionID: { input: `gid://sagittarius/RuntimeParameterDefinition/${number}`; output: `gid://sagittarius/RuntimeParameterDefinition/${number}`; } + /** + * Time represented in ISO 8601. + * + * For example: "2023-12-15T17:31:00Z". + */ + Time: { input: string; output: string; } + /** A unique identifier for all Types::FlowType entities of the application */ + TypesFlowTypeID: { input: `gid://sagittarius/TypesFlowType/${number}`; output: `gid://sagittarius/TypesFlowType/${number}`; } + /** A unique identifier for all User entities of the application */ + UserID: { input: `gid://sagittarius/User/${number}`; output: `gid://sagittarius/User/${number}`; } + /** A unique identifier for all UserIdentity entities of the application */ + UserIdentityID: { input: `gid://sagittarius/UserIdentity/${number}`; output: `gid://sagittarius/UserIdentity/${number}`; } + /** A unique identifier for all UserSession entities of the application */ + UserSessionID: { input: `gid://sagittarius/UserSession/${number}`; output: `gid://sagittarius/UserSession/${number}`; } +} + +/** Represents an active model error */ +export interface ActiveModelError { + __typename?: 'ActiveModelError'; + /** The affected attribute on the model */ + attribute: Scalars['String']['output']; + /** The validation type that failed for the attribute */ + type: Scalars['String']['output']; +} + +/** Represents the application settings */ +export interface ApplicationSettings { + __typename?: 'ApplicationSettings'; + /** Shows if organization creation is restricted to administrators */ + organizationCreationRestricted: Scalars['Boolean']['output']; + /** Shows if user registration is enabled */ + userRegistrationEnabled: Scalars['Boolean']['output']; +} + +/** Autogenerated input type of ApplicationSettingsUpdate */ +export interface ApplicationSettingsUpdateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** Set if organization creation is restricted to administrators. */ + organizationCreationRestricted?: InputMaybe; + /** Set if user registration is enabled. */ + userRegistrationEnabled?: InputMaybe; +} + +/** Autogenerated return type of ApplicationSettingsUpdate. */ +export interface ApplicationSettingsUpdatePayload { + __typename?: 'ApplicationSettingsUpdatePayload'; + /** The updated application settings. */ + applicationSettings?: Maybe; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; +} + +/** Objects that can present an authentication */ +export type Authentication = UserSession; + +/** Represents a DataType */ +export interface DataType { + __typename?: 'DataType'; + /** Time when this DataType was created */ + createdAt: Scalars['Time']['output']; + /** Generic keys of the datatype */ + genericKeys?: Maybe>; + /** Global ID of this DataType */ + id: Scalars['DataTypeID']['output']; + /** The identifier scoped to the namespace */ + identifier: Scalars['String']['output']; + /** Names of the flow type setting */ + name: TranslationConnection; + /** The namespace where this datatype belongs to */ + namespace?: Maybe; + /** The parent datatype */ + parent?: Maybe; + /** Rules of the datatype */ + rules: DataTypeRuleConnection; + /** Time when this DataType was last updated */ + updatedAt: Scalars['Time']['output']; + /** The type of the datatype */ + variant: DataTypeVariant; +} + + +/** Represents a DataType */ +export interface DataTypeNameArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a DataType */ +export interface DataTypeRulesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** The connection type for DataType. */ +export interface DataTypeConnection { + __typename?: 'DataTypeConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface DataTypeEdge { + __typename?: 'DataTypeEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Represents a data type identifier. */ +export interface DataTypeIdentifier { + __typename?: 'DataTypeIdentifier'; + /** Time when this DataTypeIdentifier was created */ + createdAt: Scalars['Time']['output']; + /** The data type of the data type identifier. */ + dataType?: Maybe; + /** The generic key of the data type identifier. */ + genericKey?: Maybe; + /** The generic type of the data type identifier. */ + genericType?: Maybe; + /** Global ID of this DataTypeIdentifier */ + id: Scalars['DataTypeIdentifierID']['output']; + /** Time when this DataTypeIdentifier was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** Input type for data type identifier */ +export interface DataTypeIdentifierInput { + /** Data type ID */ + dataTypeId?: InputMaybe; + /** Generic key value */ + genericKey?: InputMaybe; + /** Generic type information */ + genericType?: InputMaybe; +} + +/** Represents a rule that can be applied to a data type. */ +export interface DataTypeRule { + __typename?: 'DataTypeRule'; + /** The configuration of the rule */ + config: DataTypeRulesConfig; + /** Time when this DataTypeRule was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this DataTypeRule */ + id: Scalars['DataTypeRuleID']['output']; + /** Time when this DataTypeRule was last updated */ + updatedAt: Scalars['Time']['output']; + /** The type of the rule */ + variant: DataTypeRulesVariant; +} + +/** The connection type for DataTypeRule. */ +export interface DataTypeRuleConnection { + __typename?: 'DataTypeRuleConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface DataTypeRuleEdge { + __typename?: 'DataTypeRuleEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Represents a rule that can be applied to a data type. */ +export type DataTypeRulesConfig = DataTypeRulesContainsKeyConfig | DataTypeRulesContainsTypeConfig | DataTypeRulesInputTypesConfig | DataTypeRulesItemOfCollectionConfig | DataTypeRulesNumberRangeConfig | DataTypeRulesRegexConfig | DataTypeRulesReturnTypeConfig; + +/** Represents a rule that can be applied to a data type. */ +export interface DataTypeRulesContainsKeyConfig { + __typename?: 'DataTypeRulesContainsKeyConfig'; + /** The identifier of the data type this rule belongs to */ + dataTypeIdentifier: DataTypeIdentifier; + /** The key of the rule */ + key: Scalars['String']['output']; +} + +/** Represents a rule that can be applied to a data type. */ +export interface DataTypeRulesContainsTypeConfig { + __typename?: 'DataTypeRulesContainsTypeConfig'; + /** The identifier of the data type this rule belongs to */ + dataTypeIdentifier: DataTypeIdentifier; +} + +/** Represents a subtype of input type configuration for a input data type. */ +export interface DataTypeRulesInputTypeConfig { + __typename?: 'DataTypeRulesInputTypeConfig'; + /** The identifier of the data type this input type belongs to */ + dataTypeIdentifier: DataTypeIdentifier; + /** The input data type that this configuration applies to */ + inputType: DataType; +} + +/** Represents a rule that can be applied to a data type. */ +export interface DataTypeRulesInputTypesConfig { + __typename?: 'DataTypeRulesInputTypesConfig'; + /** The input types that can be used in this data type rule */ + inputTypes: Array; +} + +/** Represents a rule that can be applied to a data type. */ +export interface DataTypeRulesItemOfCollectionConfig { + __typename?: 'DataTypeRulesItemOfCollectionConfig'; + /** The items that can be configured for this rule. */ + items?: Maybe>; +} + +/** Represents a rule that can be applied to a data type. */ +export interface DataTypeRulesNumberRangeConfig { + __typename?: 'DataTypeRulesNumberRangeConfig'; + /** The minimum value of the range */ + from: Scalars['Int']['output']; + /** The step value for the range, if applicable */ + steps?: Maybe; + /** The maximum value of the range */ + to: Scalars['Int']['output']; +} + +/** Represents a rule that can be applied to a data type. */ +export interface DataTypeRulesRegexConfig { + __typename?: 'DataTypeRulesRegexConfig'; + /** The regex pattern to match against the data type value. */ + pattern: Scalars['String']['output']; +} + +/** Represents a rule that can be applied to a data type. */ +export interface DataTypeRulesReturnTypeConfig { + __typename?: 'DataTypeRulesReturnTypeConfig'; + /** The data type identifier for the return type. */ + dataTypeIdentifier: DataTypeIdentifier; +} + +/** The type of rule that can be applied to a data type. */ +export enum DataTypeRulesVariant { + /** The rule checks if a key is present in the data type. */ + ContainsKey = 'CONTAINS_KEY', + /** The rule checks if a specific type is present in the data type. */ + ContainsType = 'CONTAINS_TYPE', + /** The rule checks if the data type matches a specific input type. */ + InputType = 'INPUT_TYPE', + /** The rule checks if an item is part of a collection in the data type. */ + ItemOfCollection = 'ITEM_OF_COLLECTION', + /** The rule checks if a number falls within a specified range. */ + NumberRange = 'NUMBER_RANGE', + /** The rule checks if a string matches a specified regular expression. */ + Regex = 'REGEX', + /** The rule checks if the data type matches a specific return type. */ + ReturnType = 'RETURN_TYPE' +} + +/** Represent all available types of a datatype */ +export enum DataTypeVariant { + /** Represents an array */ + Array = 'ARRAY', + /** Represents an data type containing a data type */ + DataType = 'DATA_TYPE', + /** Represents a error */ + Error = 'ERROR', + /** Represents a node */ + Node = 'NODE', + /** Represents an object */ + Object = 'OBJECT', + /** Represents a primitive datatype */ + Primitive = 'PRIMITIVE', + /** Represents a type */ + Type = 'TYPE' +} + +/** Autogenerated input type of Echo */ +export interface EchoInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** Message to return to the user. */ + message?: InputMaybe; +} + +/** Autogenerated return type of Echo. */ +export interface EchoPayload { + __typename?: 'EchoPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** Message returned to the user. */ + message?: Maybe; +} + +/** Objects that can present an error */ +export type Error = ActiveModelError | MessageError; + +/** Represents a flow */ +export interface Flow { + __typename?: 'Flow'; + /** Time when this Flow was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this Flow */ + id: Scalars['FlowID']['output']; + /** The input data type of the flow */ + inputType?: Maybe; + /** The return data type of the flow */ + returnType?: Maybe; + /** The settings of the flow */ + settings?: Maybe>; + /** The starting node of the flow */ + startingNode: NodeFunction; + /** The flow type of the flow */ + type: FlowType; + /** Time when this Flow was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** The connection type for Flow. */ +export interface FlowConnection { + __typename?: 'FlowConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface FlowEdge { + __typename?: 'FlowEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Input type for creating or updating a flow */ +export interface FlowInput { + /** The settings of the flow */ + settings?: InputMaybe>; + /** The starting node of the flow */ + startingNode: NodeFunctionInput; + /** The identifier of the flow type */ + type: Scalars['FlowTypeID']['input']; +} + +/** Represents a flow setting */ +export interface FlowSetting { + __typename?: 'FlowSetting'; + /** Time when this FlowSetting was created */ + createdAt: Scalars['Time']['output']; + /** The identifier of the flow setting */ + flowSettingId: Scalars['String']['output']; + /** Global ID of this FlowSetting */ + id: Scalars['FlowSettingID']['output']; + /** Time when this FlowSetting was last updated */ + updatedAt: Scalars['Time']['output']; + /** The value of the flow setting */ + value: Scalars['JSON']['output']; +} + +/** Input type for flow settings */ +export interface FlowSettingInput { + /** The identifier (not database id) of the flow setting */ + flowSettingId: Scalars['String']['input']; + /** The value of the flow setting */ + object: Scalars['JSON']['input']; +} + +/** Represents a flow type */ +export interface FlowType { + __typename?: 'FlowType'; + /** Time when this FlowType was created */ + createdAt: Scalars['Time']['output']; + /** Descriptions of the flow type */ + descriptions?: Maybe; + /** Editable status of the flow type */ + editable: Scalars['Boolean']['output']; + /** Flow type settings of the flow type */ + flowTypeSettings: Array; + /** Global ID of this FlowType */ + id: Scalars['TypesFlowTypeID']['output']; + /** Identifier of the flow type */ + identifier: Scalars['String']['output']; + /** Input type of the flow type */ + inputType?: Maybe; + /** Names of the flow type */ + names?: Maybe; + /** Return type of the flow type */ + returnType?: Maybe; + /** Time when this FlowType was last updated */ + updatedAt: Scalars['Time']['output']; +} + + +/** Represents a flow type */ +export interface FlowTypeDescriptionsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a flow type */ +export interface FlowTypeNamesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** The connection type for FlowType. */ +export interface FlowTypeConnection { + __typename?: 'FlowTypeConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface FlowTypeEdge { + __typename?: 'FlowTypeEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Represents a flow type setting */ +export interface FlowTypeSetting { + __typename?: 'FlowTypeSetting'; + /** Time when this FlowTypeSetting was created */ + createdAt: Scalars['Time']['output']; + /** Data type of the flow type setting */ + dataType?: Maybe; + /** Descriptions of the flow type setting */ + descriptions: TranslationConnection; + /** Flow type of the flow type setting */ + flowType?: Maybe; + /** Global ID of this FlowTypeSetting */ + id: Scalars['FlowTypeSettingID']['output']; + /** Identifier of the flow type setting */ + identifier: Scalars['String']['output']; + /** Names of the flow type setting */ + names: TranslationConnection; + /** Unique status of the flow type setting */ + unique: Scalars['Boolean']['output']; + /** Time when this FlowTypeSetting was last updated */ + updatedAt: Scalars['Time']['output']; +} + + +/** Represents a flow type setting */ +export interface FlowTypeSettingDescriptionsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a flow type setting */ +export interface FlowTypeSettingNamesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** Represents a function definition */ +export interface FunctionDefinition { + __typename?: 'FunctionDefinition'; + /** Time when this FunctionDefinition was created */ + createdAt: Scalars['Time']['output']; + /** Deprecation message of the function */ + deprecationMessages?: Maybe; + /** Description of the function */ + descriptions?: Maybe; + /** Documentation of the function */ + documentations?: Maybe; + /** Generic keys of the function */ + genericKeys?: Maybe>; + /** Global ID of this FunctionDefinition */ + id: Scalars['FunctionDefinitionID']['output']; + /** Name of the function */ + names?: Maybe; + /** Parameters of the function */ + parameterDefinitions?: Maybe; + /** Return type of the function */ + returnType?: Maybe; + /** Runtime function definition */ + runtimeFunctionDefinition?: Maybe; + /** Indicates if the function can throw an error */ + throwsError: Scalars['Boolean']['output']; + /** Time when this FunctionDefinition was last updated */ + updatedAt: Scalars['Time']['output']; +} + + +/** Represents a function definition */ +export interface FunctionDefinitionDeprecationMessagesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a function definition */ +export interface FunctionDefinitionDescriptionsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a function definition */ +export interface FunctionDefinitionDocumentationsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a function definition */ +export interface FunctionDefinitionNamesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a function definition */ +export interface FunctionDefinitionParameterDefinitionsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** The connection type for FunctionDefinition. */ +export interface FunctionDefinitionConnection { + __typename?: 'FunctionDefinitionConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface FunctionDefinitionEdge { + __typename?: 'FunctionDefinitionEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Represents a combination strategy with AND/OR logic used by a generic mapper. */ +export interface GenericCombinationStrategy { + __typename?: 'GenericCombinationStrategy'; + /** Time when this GenericCombinationStrategy was created */ + createdAt: Scalars['Time']['output']; + /** The associated generic mapper, if any. */ + genericMapper?: Maybe; + /** Global ID of this GenericCombinationStrategy */ + id: Scalars['GenericCombinationStrategyID']['output']; + /** The combination type ('AND' or 'OR'). */ + type: GenericCombinationStrategyType; + /** Time when this GenericCombinationStrategy was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** The available combination strategy types. */ +export enum GenericCombinationStrategyType { + /** Represents a logical AND combination. */ + And = 'AND', + /** Represents a logical OR combination. */ + Or = 'OR' +} + +/** Represents a mapping between a source data type and a target key for generic values. */ +export interface GenericMapper { + __typename?: 'GenericMapper'; + /** Time when this GenericMapper was created */ + createdAt: Scalars['Time']['output']; + /** Combination strategies associated with this generic mapper. */ + genericCombinationStrategies?: Maybe>; + /** Global ID of this GenericMapper */ + id: Scalars['GenericMapperID']['output']; + /** The source data type identifier. */ + sources: Array; + /** The target key for the generic value. */ + target: Scalars['String']['output']; + /** Time when this GenericMapper was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** Input type for generic mappers */ +export interface GenericMapperInput { + /** The source data type identifier for the mapper */ + sources: Array; + /** The target data type identifier for the mapper */ + target: Scalars['String']['input']; +} + +/** Represents a generic type that can be used in various contexts. */ +export interface GenericType { + __typename?: 'GenericType'; + /** Time when this GenericType was created */ + createdAt: Scalars['Time']['output']; + /** The data type associated with this generic type. */ + dataType: DataType; + /** The mappers associated with this generic type. */ + genericMappers: Array; + /** Global ID of this GenericType */ + id: Scalars['GenericTypeID']['output']; + /** Time when this GenericType was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** Input type for generic type operations. */ +export interface GenericTypeInput { + /** The data type associated with this generic type. */ + dataTypeId: Scalars['DataTypeID']['input']; + /** The mappers associated with this generic type. */ + genericMappers: Array; +} + +/** Represents the input for external user identity validation */ +export interface IdentityInput { + /** This validation code will be used for the oAuth validation process */ + code?: InputMaybe; +} + +/** Represents a literal value, such as a string or number. */ +export interface LiteralValue { + __typename?: 'LiteralValue'; + /** Time when this LiteralValue was created */ + createdAt: Scalars['Time']['output']; + /** Time when this LiteralValue was last updated */ + updatedAt: Scalars['Time']['output']; + /** The literal value itself as JSON. */ + value: Scalars['JSON']['output']; +} + +/** Represents an error message */ +export interface MessageError { + __typename?: 'MessageError'; + /** The message provided from the error */ + message: Scalars['String']['output']; +} + +/** Represents the input for mfa authentication */ +export interface MfaInput { + /** The type of the mfa authentication */ + type: MfaType; + /** The value of the authentication */ + value: Scalars['String']['input']; +} + +/** Represent all available types to authenticate with mfa */ +export enum MfaType { + /** Single use backup code */ + BackupCode = 'BACKUP_CODE', + /** Time based onetime password */ + Totp = 'TOTP' +} + +/** Root Mutation type */ +export interface Mutation { + __typename?: 'Mutation'; + /** Update application settings. */ + applicationSettingsUpdate?: Maybe; + /** + * A mutation that does not perform any changes. + * + * This is expected to be used for testing of endpoints, to verify + * that a user has mutation access. + */ + echo?: Maybe; + /** Create a new namespace license. */ + namespacesLicensesCreate?: Maybe; + /** Deletes an namespace license. */ + namespacesLicensesDelete?: Maybe; + /** Update the roles a member is assigned to. */ + namespacesMembersAssignRoles?: Maybe; + /** Remove a member from a namespace. */ + namespacesMembersDelete?: Maybe; + /** Invite a new member to a namespace. */ + namespacesMembersInvite?: Maybe; + /** Assign runtimes to a project */ + namespacesProjectsAssignRuntimes?: Maybe; + /** Creates a new namespace project. */ + namespacesProjectsCreate?: Maybe; + /** Deletes a namespace project. */ + namespacesProjectsDelete?: Maybe; + /** Creates a new flow. */ + namespacesProjectsFlowsCreate?: Maybe; + /** Deletes a namespace project. */ + namespacesProjectsFlowsDelete?: Maybe; + /** Updates a namespace project. */ + namespacesProjectsUpdate?: Maybe; + /** Update the abilities a role is granted. */ + namespacesRolesAssignAbilities?: Maybe; + /** Update the project a role is assigned to. */ + namespacesRolesAssignProjects?: Maybe; + /** Create a new role in a namespace. */ + namespacesRolesCreate?: Maybe; + /** Delete an existing role in a namespace. */ + namespacesRolesDelete?: Maybe; + /** Update an existing namespace role. */ + namespacesRolesUpdate?: Maybe; + /** Create a new organization. */ + organizationsCreate?: Maybe; + /** Delete an existing organization. */ + organizationsDelete?: Maybe; + /** Update an existing organization. */ + organizationsUpdate?: Maybe; + /** Create a new runtime. */ + runtimesCreate?: Maybe; + /** Delete an existing runtime. */ + runtimesDelete?: Maybe; + /** reloads the token of an existing runtime. */ + runtimesRotateToken?: Maybe; + /** Update an existing runtime. */ + runtimesUpdate?: Maybe; + /** Links an external identity to an existing user */ + usersIdentityLink?: Maybe; + /** Login to an existing user via an external identity */ + usersIdentityLogin?: Maybe; + /** Register a new user via an external identity */ + usersIdentityRegister?: Maybe; + /** Unlinks an external identity from an user */ + usersIdentityUnlink?: Maybe; + /** Login to an existing user */ + usersLogin?: Maybe; + /** Logout an existing user session */ + usersLogout?: Maybe; + /** Rotates the backup codes of a user. */ + usersMfaBackupCodesRotate?: Maybe; + /** Generates an encrypted totp secret */ + usersMfaTotpGenerateSecret?: Maybe; + /** Validates a TOTP value for the given secret and enables TOTP MFA for the user */ + usersMfaTotpValidateSecret?: Maybe; + /** Register a new user */ + usersRegister?: Maybe; + /** Update an existing user. */ + usersUpdate?: Maybe; +} + + +/** Root Mutation type */ +export interface MutationApplicationSettingsUpdateArgs { + input: ApplicationSettingsUpdateInput; +} + + +/** Root Mutation type */ +export interface MutationEchoArgs { + input: EchoInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesLicensesCreateArgs { + input: NamespacesLicensesCreateInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesLicensesDeleteArgs { + input: NamespacesLicensesDeleteInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesMembersAssignRolesArgs { + input: NamespacesMembersAssignRolesInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesMembersDeleteArgs { + input: NamespacesMembersDeleteInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesMembersInviteArgs { + input: NamespacesMembersInviteInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesProjectsAssignRuntimesArgs { + input: NamespacesProjectsAssignRuntimesInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesProjectsCreateArgs { + input: NamespacesProjectsCreateInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesProjectsDeleteArgs { + input: NamespacesProjectsDeleteInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesProjectsFlowsCreateArgs { + input: NamespacesProjectsFlowsCreateInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesProjectsFlowsDeleteArgs { + input: NamespacesProjectsFlowsDeleteInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesProjectsUpdateArgs { + input: NamespacesProjectsUpdateInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesRolesAssignAbilitiesArgs { + input: NamespacesRolesAssignAbilitiesInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesRolesAssignProjectsArgs { + input: NamespacesRolesAssignProjectsInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesRolesCreateArgs { + input: NamespacesRolesCreateInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesRolesDeleteArgs { + input: NamespacesRolesDeleteInput; +} + + +/** Root Mutation type */ +export interface MutationNamespacesRolesUpdateArgs { + input: NamespacesRolesUpdateInput; +} + + +/** Root Mutation type */ +export interface MutationOrganizationsCreateArgs { + input: OrganizationsCreateInput; +} + + +/** Root Mutation type */ +export interface MutationOrganizationsDeleteArgs { + input: OrganizationsDeleteInput; +} + + +/** Root Mutation type */ +export interface MutationOrganizationsUpdateArgs { + input: OrganizationsUpdateInput; +} + + +/** Root Mutation type */ +export interface MutationRuntimesCreateArgs { + input: RuntimesCreateInput; +} + + +/** Root Mutation type */ +export interface MutationRuntimesDeleteArgs { + input: RuntimesDeleteInput; +} + + +/** Root Mutation type */ +export interface MutationRuntimesRotateTokenArgs { + input: RuntimesRotateTokenInput; +} + + +/** Root Mutation type */ +export interface MutationRuntimesUpdateArgs { + input: RuntimesUpdateInput; +} + + +/** Root Mutation type */ +export interface MutationUsersIdentityLinkArgs { + input: UsersIdentityLinkInput; +} + + +/** Root Mutation type */ +export interface MutationUsersIdentityLoginArgs { + input: UsersIdentityLoginInput; +} + + +/** Root Mutation type */ +export interface MutationUsersIdentityRegisterArgs { + input: UsersIdentityRegisterInput; +} + + +/** Root Mutation type */ +export interface MutationUsersIdentityUnlinkArgs { + input: UsersIdentityUnlinkInput; +} + + +/** Root Mutation type */ +export interface MutationUsersLoginArgs { + input: UsersLoginInput; +} + + +/** Root Mutation type */ +export interface MutationUsersLogoutArgs { + input: UsersLogoutInput; +} + + +/** Root Mutation type */ +export interface MutationUsersMfaBackupCodesRotateArgs { + input: UsersMfaBackupCodesRotateInput; +} + + +/** Root Mutation type */ +export interface MutationUsersMfaTotpGenerateSecretArgs { + input: UsersMfaTotpGenerateSecretInput; +} + + +/** Root Mutation type */ +export interface MutationUsersMfaTotpValidateSecretArgs { + input: UsersMfaTotpValidateSecretInput; +} + + +/** Root Mutation type */ +export interface MutationUsersRegisterArgs { + input: UsersRegisterInput; +} + + +/** Root Mutation type */ +export interface MutationUsersUpdateArgs { + input: UsersUpdateInput; +} + +/** Represents a Namespace */ +export interface Namespace { + __typename?: 'Namespace'; + /** Time when this Namespace was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this Namespace */ + id: Scalars['NamespaceID']['output']; + /** Members of the namespace */ + members: NamespaceMemberConnection; + /** Licenses of the namespace */ + namespaceLicenses: NamespaceLicenseConnection; + /** Parent of this namespace */ + parent: NamespaceParent; + /** Projects of the namespace */ + projects: NamespaceProjectConnection; + /** Roles of the namespace */ + roles: NamespaceRoleConnection; + /** Runtime of the namespace */ + runtimes: RuntimeConnection; + /** Time when this Namespace was last updated */ + updatedAt: Scalars['Time']['output']; +} + + +/** Represents a Namespace */ +export interface NamespaceMembersArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a Namespace */ +export interface NamespaceNamespaceLicensesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a Namespace */ +export interface NamespaceProjectsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a Namespace */ +export interface NamespaceRolesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a Namespace */ +export interface NamespaceRuntimesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** Represents a Namespace License */ +export interface NamespaceLicense { + __typename?: 'NamespaceLicense'; + /** Time when this NamespaceLicense was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this NamespaceLicense */ + id: Scalars['NamespaceLicenseID']['output']; + /** The namespace the license belongs to */ + namespace: Namespace; + /** Time when this NamespaceLicense was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** The connection type for NamespaceLicense. */ +export interface NamespaceLicenseConnection { + __typename?: 'NamespaceLicenseConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface NamespaceLicenseEdge { + __typename?: 'NamespaceLicenseEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Represents a namespace member */ +export interface NamespaceMember { + __typename?: 'NamespaceMember'; + /** Time when this NamespaceMember was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this NamespaceMember */ + id: Scalars['NamespaceMemberID']['output']; + /** Namespace this member belongs to */ + namespace: Namespace; + /** Time when this NamespaceMember was last updated */ + updatedAt: Scalars['Time']['output']; + /** User this member belongs to */ + user: User; +} + +/** The connection type for NamespaceMember. */ +export interface NamespaceMemberConnection { + __typename?: 'NamespaceMemberConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface NamespaceMemberEdge { + __typename?: 'NamespaceMemberEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Represents an assigned role to a member */ +export interface NamespaceMemberRole { + __typename?: 'NamespaceMemberRole'; + /** Time when this NamespaceMemberRole was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this NamespaceMemberRole */ + id: Scalars['NamespaceMemberRoleID']['output']; + /** The member the role is assigned to */ + member?: Maybe; + /** The assigned role */ + role?: Maybe; + /** Time when this NamespaceMemberRole was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** Objects that can present a namespace */ +export type NamespaceParent = Organization; + +/** Represents a namespace project */ +export interface NamespaceProject { + __typename?: 'NamespaceProject'; + /** Time when this NamespaceProject was created */ + createdAt: Scalars['Time']['output']; + /** Description of the project */ + description: Scalars['String']['output']; + /** Fetches an flow given by its ID */ + flow?: Maybe; + /** Fetches all flows in this project */ + flows?: Maybe; + /** Global ID of this NamespaceProject */ + id: Scalars['NamespaceProjectID']['output']; + /** Name of the project */ + name: Scalars['String']['output']; + /** The namespace where this project belongs to */ + namespace: Namespace; + /** The primary runtime for the project */ + primaryRuntime?: Maybe; + /** Runtimes assigned to this project */ + runtimes: RuntimeConnection; + /** Time when this NamespaceProject was last updated */ + updatedAt: Scalars['Time']['output']; +} + + +/** Represents a namespace project */ +export interface NamespaceProjectFlowArgs { + id: Scalars['FlowID']['input']; +} + + +/** Represents a namespace project */ +export interface NamespaceProjectFlowsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a namespace project */ +export interface NamespaceProjectRuntimesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** The connection type for NamespaceProject. */ +export interface NamespaceProjectConnection { + __typename?: 'NamespaceProjectConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface NamespaceProjectEdge { + __typename?: 'NamespaceProjectEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Represents a namespace role. */ +export interface NamespaceRole { + __typename?: 'NamespaceRole'; + /** The abilities the role is granted */ + abilities: Array; + /** The projects this role is assigned to */ + assignedProjects?: Maybe; + /** Time when this NamespaceRole was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this NamespaceRole */ + id: Scalars['NamespaceRoleID']['output']; + /** The name of this role */ + name: Scalars['String']['output']; + /** The namespace where this role belongs to */ + namespace?: Maybe; + /** Time when this NamespaceRole was last updated */ + updatedAt: Scalars['Time']['output']; +} + + +/** Represents a namespace role. */ +export interface NamespaceRoleAssignedProjectsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** Represents abilities that can be granted to roles in namespaces. */ +export enum NamespaceRoleAbility { + /** Allows to change the roles of a namespace member */ + AssignMemberRoles = 'ASSIGN_MEMBER_ROLES', + /** Allows to assign runtimes to a project in the namespace */ + AssignProjectRuntimes = 'ASSIGN_PROJECT_RUNTIMES', + /** Allows to change the abilities of a namespace role */ + AssignRoleAbilities = 'ASSIGN_ROLE_ABILITIES', + /** Allows to change the assigned projects of a namespace role */ + AssignRoleProjects = 'ASSIGN_ROLE_PROJECTS', + /** Allows to create flows in a namespace project */ + CreateFlows = 'CREATE_FLOWS', + /** Allows to create a license for the namespace */ + CreateNamespaceLicense = 'CREATE_NAMESPACE_LICENSE', + /** Allows to create a project in the namespace */ + CreateNamespaceProject = 'CREATE_NAMESPACE_PROJECT', + /** Allows the creation of roles in a namespace */ + CreateNamespaceRole = 'CREATE_NAMESPACE_ROLE', + /** Allows to create a runtime globally or for the namespace */ + CreateRuntime = 'CREATE_RUNTIME', + /** Allows to delete flows in a namespace project */ + DeleteFlows = 'DELETE_FLOWS', + /** Allows to remove members of a namespace */ + DeleteMember = 'DELETE_MEMBER', + /** Allows to delete the license of the namespace */ + DeleteNamespaceLicense = 'DELETE_NAMESPACE_LICENSE', + /** Allows to delete the project of the namespace */ + DeleteNamespaceProject = 'DELETE_NAMESPACE_PROJECT', + /** Allows the deletion of roles in a namespace */ + DeleteNamespaceRole = 'DELETE_NAMESPACE_ROLE', + /** Allows to delete the organization */ + DeleteOrganization = 'DELETE_ORGANIZATION', + /** Allows to delete a runtime */ + DeleteRuntime = 'DELETE_RUNTIME', + /** Allows to invite new members to a namespace */ + InviteMember = 'INVITE_MEMBER', + /** Allows to perform any action in the namespace */ + NamespaceAdministrator = 'NAMESPACE_ADMINISTRATOR', + /** Allows to read the license of the namespace */ + ReadNamespaceLicense = 'READ_NAMESPACE_LICENSE', + /** Allows to read the project of the namespace */ + ReadNamespaceProject = 'READ_NAMESPACE_PROJECT', + /** Allows to regenerate a runtime token */ + RotateRuntimeToken = 'ROTATE_RUNTIME_TOKEN', + /** Allows to update flows in the project */ + UpdateFlows = 'UPDATE_FLOWS', + /** Allows to update the project of the namespace */ + UpdateNamespaceProject = 'UPDATE_NAMESPACE_PROJECT', + /** Allows to update the namespace role */ + UpdateNamespaceRole = 'UPDATE_NAMESPACE_ROLE', + /** Allows to update the organization */ + UpdateOrganization = 'UPDATE_ORGANIZATION', + /** Allows to update a runtime globally or for the namespace */ + UpdateRuntime = 'UPDATE_RUNTIME' +} + +/** The connection type for NamespaceRole. */ +export interface NamespaceRoleConnection { + __typename?: 'NamespaceRoleConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface NamespaceRoleEdge { + __typename?: 'NamespaceRoleEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Autogenerated input type of NamespacesLicensesCreate */ +export interface NamespacesLicensesCreateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The license data. */ + data: Scalars['String']['input']; + /** The namespace ID. */ + namespaceId: Scalars['NamespaceID']['input']; +} + +/** Autogenerated return type of NamespacesLicensesCreate. */ +export interface NamespacesLicensesCreatePayload { + __typename?: 'NamespacesLicensesCreatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The newly created license. */ + namespaceLicense?: Maybe; +} + +/** Autogenerated input type of NamespacesLicensesDelete */ +export interface NamespacesLicensesDeleteInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The license id to delete. */ + namespaceLicenseId: Scalars['NamespaceLicenseID']['input']; +} + +/** Autogenerated return type of NamespacesLicensesDelete. */ +export interface NamespacesLicensesDeletePayload { + __typename?: 'NamespacesLicensesDeletePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The deleted namespace license. */ + namespaceLicense?: Maybe; +} + +/** Autogenerated input type of NamespacesMembersAssignRoles */ +export interface NamespacesMembersAssignRolesInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The id of the member which should be assigned the roles */ + memberId: Scalars['NamespaceMemberID']['input']; + /** The roles the member should be assigned to the member */ + roleIds: Array; +} + +/** Autogenerated return type of NamespacesMembersAssignRoles. */ +export interface NamespacesMembersAssignRolesPayload { + __typename?: 'NamespacesMembersAssignRolesPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The roles the member is now assigned to */ + namespaceMemberRoles?: Maybe>; +} + +/** Autogenerated input type of NamespacesMembersDelete */ +export interface NamespacesMembersDeleteInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The id of the namespace member to remove */ + namespaceMemberId: Scalars['NamespaceMemberID']['input']; +} + +/** Autogenerated return type of NamespacesMembersDelete. */ +export interface NamespacesMembersDeletePayload { + __typename?: 'NamespacesMembersDeletePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The removed namespace member */ + namespaceMember?: Maybe; +} + +/** Autogenerated input type of NamespacesMembersInvite */ +export interface NamespacesMembersInviteInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The id of the namespace which this member will belong to */ + namespaceId: Scalars['NamespaceID']['input']; + /** The id of the user to invite */ + userId: Scalars['UserID']['input']; +} + +/** Autogenerated return type of NamespacesMembersInvite. */ +export interface NamespacesMembersInvitePayload { + __typename?: 'NamespacesMembersInvitePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The newly created namespace member */ + namespaceMember?: Maybe; +} + +/** Autogenerated input type of NamespacesProjectsAssignRuntimes */ +export interface NamespacesProjectsAssignRuntimesInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** ID of the project to assign runtimes to */ + namespaceProjectId: Scalars['NamespaceProjectID']['input']; + /** The new runtimes assigned to the project */ + runtimeIds: Array; +} + +/** Autogenerated return type of NamespacesProjectsAssignRuntimes. */ +export interface NamespacesProjectsAssignRuntimesPayload { + __typename?: 'NamespacesProjectsAssignRuntimesPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The updated project with assigned runtimes */ + namespaceProject?: Maybe; +} + +/** Autogenerated input type of NamespacesProjectsCreate */ +export interface NamespacesProjectsCreateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** Description for the new project. */ + description?: InputMaybe; + /** Name for the new project. */ + name: Scalars['String']['input']; + /** The id of the namespace which this project will belong to */ + namespaceId: Scalars['NamespaceID']['input']; +} + +/** Autogenerated return type of NamespacesProjectsCreate. */ +export interface NamespacesProjectsCreatePayload { + __typename?: 'NamespacesProjectsCreatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The newly created project. */ + namespaceProject?: Maybe; +} + +/** Autogenerated input type of NamespacesProjectsDelete */ +export interface NamespacesProjectsDeleteInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The id of the namespace project which will be deleted */ + namespaceProjectId: Scalars['NamespaceProjectID']['input']; +} + +/** Autogenerated return type of NamespacesProjectsDelete. */ +export interface NamespacesProjectsDeletePayload { + __typename?: 'NamespacesProjectsDeletePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The deleted project. */ + namespaceProject?: Maybe; +} + +/** Autogenerated input type of NamespacesProjectsFlowsCreate */ +export interface NamespacesProjectsFlowsCreateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The flow to create */ + flow: FlowInput; + /** The ID of the project to which the flow belongs to */ + projectId: Scalars['NamespaceProjectID']['input']; +} + +/** Autogenerated return type of NamespacesProjectsFlowsCreate. */ +export interface NamespacesProjectsFlowsCreatePayload { + __typename?: 'NamespacesProjectsFlowsCreatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The newly created flow. */ + flow?: Maybe; +} + +/** Autogenerated input type of NamespacesProjectsFlowsDelete */ +export interface NamespacesProjectsFlowsDeleteInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The id of the flow which will be deleted */ + flowId: Scalars['FlowID']['input']; +} + +/** Autogenerated return type of NamespacesProjectsFlowsDelete. */ +export interface NamespacesProjectsFlowsDeletePayload { + __typename?: 'NamespacesProjectsFlowsDeletePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The deleted flow. */ + flow?: Maybe; +} + +/** Autogenerated input type of NamespacesProjectsUpdate */ +export interface NamespacesProjectsUpdateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** Description for the updated project. */ + description?: InputMaybe; + /** Name for the updated project. */ + name?: InputMaybe; + /** The id of the namespace project which will be updated */ + namespaceProjectId: Scalars['NamespaceProjectID']['input']; + /** The primary runtime for the updated project. */ + primaryRuntimeId?: InputMaybe; +} + +/** Autogenerated return type of NamespacesProjectsUpdate. */ +export interface NamespacesProjectsUpdatePayload { + __typename?: 'NamespacesProjectsUpdatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The updated project. */ + namespaceProject?: Maybe; +} + +/** Autogenerated input type of NamespacesRolesAssignAbilities */ +export interface NamespacesRolesAssignAbilitiesInput { + /** The abilities that should be granted to the ability */ + abilities: Array; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The id of the role which should be granted the abilities */ + roleId: Scalars['NamespaceRoleID']['input']; +} + +/** Autogenerated return type of NamespacesRolesAssignAbilities. */ +export interface NamespacesRolesAssignAbilitiesPayload { + __typename?: 'NamespacesRolesAssignAbilitiesPayload'; + /** The now granted abilities */ + abilities?: Maybe>; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; +} + +/** Autogenerated input type of NamespacesRolesAssignProjects */ +export interface NamespacesRolesAssignProjectsInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The projects that should be assigned to the role */ + projectIds: Array; + /** The id of the role which should be assigned to projects */ + roleId: Scalars['NamespaceRoleID']['input']; +} + +/** Autogenerated return type of NamespacesRolesAssignProjects. */ +export interface NamespacesRolesAssignProjectsPayload { + __typename?: 'NamespacesRolesAssignProjectsPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The now assigned projects */ + projects?: Maybe>; +} + +/** Autogenerated input type of NamespacesRolesCreate */ +export interface NamespacesRolesCreateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The name for the new role */ + name: Scalars['String']['input']; + /** The id of the namespace which this role will belong to */ + namespaceId: Scalars['NamespaceID']['input']; +} + +/** Autogenerated return type of NamespacesRolesCreate. */ +export interface NamespacesRolesCreatePayload { + __typename?: 'NamespacesRolesCreatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The newly created namespace role */ + namespaceRole?: Maybe; +} + +/** Autogenerated input type of NamespacesRolesDelete */ +export interface NamespacesRolesDeleteInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The id of the namespace role which will be deleted */ + namespaceRoleId: Scalars['NamespaceRoleID']['input']; +} + +/** Autogenerated return type of NamespacesRolesDelete. */ +export interface NamespacesRolesDeletePayload { + __typename?: 'NamespacesRolesDeletePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The deleted namespace role */ + namespaceRole?: Maybe; +} + +/** Autogenerated input type of NamespacesRolesUpdate */ +export interface NamespacesRolesUpdateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** Name for the namespace role. */ + name: Scalars['String']['input']; + /** ID of the namespace role to update. */ + namespaceRoleId: Scalars['NamespaceRoleID']['input']; +} + +/** Autogenerated return type of NamespacesRolesUpdate. */ +export interface NamespacesRolesUpdatePayload { + __typename?: 'NamespacesRolesUpdatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The updated namespace role. */ + namespaceRole?: Maybe; +} + +/** An object with an ID. */ +export interface Node { + /** ID of the object. */ + id: Scalars['ID']['output']; +} + +/** Represents a Node Function */ +export interface NodeFunction { + __typename?: 'NodeFunction'; + /** Time when this NodeFunction was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this NodeFunction */ + id: Scalars['NodeFunctionID']['output']; + /** The next Node Function in the flow */ + nextNode?: Maybe; + /** The parameters of the Node Function */ + parameters: NodeParameterConnection; + /** The definition of the Node Function */ + runtimeFunction: RuntimeFunctionDefinition; + /** Time when this NodeFunction was last updated */ + updatedAt: Scalars['Time']['output']; +} + + +/** Represents a Node Function */ +export interface NodeFunctionParametersArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** Input type for a Node Function */ +export interface NodeFunctionInput { + /** The next Node Function in the flow */ + nextNode?: InputMaybe; + /** The parameters of the Node Function */ + parameters: Array; + /** The identifier of the Runtime Function Definition */ + runtimeFunctionId: Scalars['RuntimeFunctionDefinitionID']['input']; +} + +/** Represents a Node parameter */ +export interface NodeParameter { + __typename?: 'NodeParameter'; + /** Time when this NodeParameter was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this NodeParameter */ + id: Scalars['NodeParameterID']['output']; + /** The definition of the parameter */ + runtimeParameter: RuntimeParameterDefinition; + /** Time when this NodeParameter was last updated */ + updatedAt: Scalars['Time']['output']; + /** The value of the parameter */ + value?: Maybe; +} + +/** The connection type for NodeParameter. */ +export interface NodeParameterConnection { + __typename?: 'NodeParameterConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface NodeParameterEdge { + __typename?: 'NodeParameterEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Input type for Node parameter */ +export interface NodeParameterInput { + /** The identifier of the Runtime Parameter Definition */ + runtimeParameterDefinitionId: Scalars['RuntimeParameterDefinitionID']['input']; + /** The value of the parameter */ + value?: InputMaybe; +} + +/** Represents a parameter value for a node. */ +export type NodeParameterValue = LiteralValue | NodeFunction | ReferenceValue; + +/** Input type for parameter value */ +export interface NodeParameterValueInput { + /** The function value of the parameter */ + functionValue?: InputMaybe; + /** The literal value of the parameter */ + literalValue?: InputMaybe; + /** The reference value of the parameter */ + referenceValue?: InputMaybe; +} + +/** Represents a Organization */ +export interface Organization { + __typename?: 'Organization'; + /** Time when this Organization was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this Organization */ + id: Scalars['OrganizationID']['output']; + /** Name of the organization */ + name: Scalars['String']['output']; + /** Namespace of this organization */ + namespace: Namespace; + /** Time when this Organization was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** Autogenerated input type of OrganizationsCreate */ +export interface OrganizationsCreateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** Name for the new organization. */ + name: Scalars['String']['input']; +} + +/** Autogenerated return type of OrganizationsCreate. */ +export interface OrganizationsCreatePayload { + __typename?: 'OrganizationsCreatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The newly created organization. */ + organization?: Maybe; +} + +/** Autogenerated input type of OrganizationsDelete */ +export interface OrganizationsDeleteInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The organization to delete. */ + organizationId: Scalars['OrganizationID']['input']; +} + +/** Autogenerated return type of OrganizationsDelete. */ +export interface OrganizationsDeletePayload { + __typename?: 'OrganizationsDeletePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The deleted organization. */ + organization?: Maybe; +} + +/** Autogenerated input type of OrganizationsUpdate */ +export interface OrganizationsUpdateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** Name for the new organization. */ + name: Scalars['String']['input']; + /** ID of the organization to update. */ + organizationId: Scalars['OrganizationID']['input']; +} + +/** Autogenerated return type of OrganizationsUpdate. */ +export interface OrganizationsUpdatePayload { + __typename?: 'OrganizationsUpdatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The updated organization. */ + organization?: Maybe; +} + +/** Information about pagination in a connection. */ +export interface PageInfo { + __typename?: 'PageInfo'; + /** When paginating forwards, the cursor to continue. */ + endCursor?: Maybe; + /** When paginating forwards, are there more items? */ + hasNextPage: Scalars['Boolean']['output']; + /** When paginating backwards, are there more items? */ + hasPreviousPage: Scalars['Boolean']['output']; + /** When paginating backwards, the cursor to continue. */ + startCursor?: Maybe; +} + +/** Represents a parameter definition */ +export interface ParameterDefinition { + __typename?: 'ParameterDefinition'; + /** Time when this ParameterDefinition was created */ + createdAt: Scalars['Time']['output']; + /** Data type of the parameter */ + dataType?: Maybe; + /** Description of the parameter */ + descriptions?: Maybe; + /** Documentation of the parameter */ + documentations?: Maybe; + /** Global ID of this ParameterDefinition */ + id: Scalars['ParameterDefinitionID']['output']; + /** Name of the parameter */ + names?: Maybe; + /** Time when this ParameterDefinition was last updated */ + updatedAt: Scalars['Time']['output']; +} + + +/** Represents a parameter definition */ +export interface ParameterDefinitionDescriptionsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a parameter definition */ +export interface ParameterDefinitionDocumentationsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a parameter definition */ +export interface ParameterDefinitionNamesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** The connection type for ParameterDefinition. */ +export interface ParameterDefinitionConnection { + __typename?: 'ParameterDefinitionConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface ParameterDefinitionEdge { + __typename?: 'ParameterDefinitionEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Root Query type */ +export interface Query { + __typename?: 'Query'; + /** Get global application settings */ + applicationSettings?: Maybe; + /** Get the currently logged in authentication */ + currentAuthentication?: Maybe; + /** Get the currently logged in user */ + currentUser?: Maybe; + /** Field available for use to test API access */ + echo: Scalars['String']['output']; + /** Find runtimes */ + globalRuntimes: RuntimeConnection; + /** Find a namespace */ + namespace?: Maybe; + /** Fetches an object given its ID */ + node?: Maybe; + /** Fetches a list of objects given a list of IDs */ + nodes?: Maybe>>; + /** Find a organization */ + organization?: Maybe; + /** Find users */ + users: UserConnection; +} + + +/** Root Query type */ +export interface QueryEchoArgs { + message: Scalars['String']['input']; +} + + +/** Root Query type */ +export interface QueryGlobalRuntimesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Root Query type */ +export interface QueryNamespaceArgs { + id: Scalars['NamespaceID']['input']; +} + + +/** Root Query type */ +export interface QueryNodeArgs { + id: Scalars['ID']['input']; +} + + +/** Root Query type */ +export interface QueryNodesArgs { + ids: Array; +} + + +/** Root Query type */ +export interface QueryOrganizationArgs { + id?: InputMaybe; + name?: InputMaybe; +} + + +/** Root Query type */ +export interface QueryUsersArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** Represents a reference path in a flow */ +export interface ReferencePath { + __typename?: 'ReferencePath'; + /** The array index of the referenced data by the path */ + arrayIndex?: Maybe; + /** Time when this ReferencePath was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this ReferencePath */ + id: Scalars['ReferencePathID']['output']; + /** The path to the reference in the flow */ + path?: Maybe; + /** Time when this ReferencePath was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** Input type for reference path */ +export interface ReferencePathInput { + /** Array index if applicable */ + arrayIndex?: InputMaybe; + /** The path to the reference in the flow */ + path?: InputMaybe; +} + +/** Represents a reference value in the system. */ +export interface ReferenceValue { + __typename?: 'ReferenceValue'; + /** Time when this ReferenceValue was created */ + createdAt: Scalars['Time']['output']; + /** The identifier of the data type this reference value belongs to. */ + dataTypeIdentifier: DataTypeIdentifier; + /** Global ID of this ReferenceValue */ + id: Scalars['ReferenceValueID']['output']; + /** The primary level of the reference value. */ + primaryLevel: Scalars['Int']['output']; + /** The paths associated with this reference value. */ + referencePath: Array; + /** The secondary level of the reference value. */ + secondaryLevel: Scalars['Int']['output']; + /** The tertiary level of the reference value, if applicable. */ + tertiaryLevel?: Maybe; + /** Time when this ReferenceValue was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** Input type for reference value */ +export interface ReferenceValueInput { + /** The identifier of the data type this reference value belongs to */ + dataTypeIdentifier: DataTypeIdentifierInput; + /** The primary level of the reference value */ + primaryLevel: Scalars['Int']['input']; + /** The paths associated with this reference value */ + referencePath: Array; + /** The secondary level of the reference value */ + secondaryLevel: Scalars['Int']['input']; + /** The tertiary level of the reference value */ + tertiaryLevel?: InputMaybe; +} + +/** Represents a runtime */ +export interface Runtime { + __typename?: 'Runtime'; + /** Time when this Runtime was created */ + createdAt: Scalars['Time']['output']; + /** DataTypes of the runtime */ + dataTypes: DataTypeConnection; + /** The description for the runtime if present */ + description: Scalars['String']['output']; + /** FlowTypes of the runtime */ + flowTypes: FlowTypeConnection; + /** Global ID of this Runtime */ + id: Scalars['RuntimeID']['output']; + /** The name for the runtime */ + name: Scalars['String']['output']; + /** The parent namespace for the runtime */ + namespace?: Maybe; + /** Projects associated with the runtime */ + projects: NamespaceProjectConnection; + /** The status of the runtime */ + status: RuntimeStatusType; + /** Token belonging to the runtime, only present on creation */ + token?: Maybe; + /** Time when this Runtime was last updated */ + updatedAt: Scalars['Time']['output']; +} + + +/** Represents a runtime */ +export interface RuntimeDataTypesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a runtime */ +export interface RuntimeFlowTypesArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + + +/** Represents a runtime */ +export interface RuntimeProjectsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** The connection type for Runtime. */ +export interface RuntimeConnection { + __typename?: 'RuntimeConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface RuntimeEdge { + __typename?: 'RuntimeEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Represents a Node Function definition */ +export interface RuntimeFunctionDefinition { + __typename?: 'RuntimeFunctionDefinition'; + /** Time when this RuntimeFunctionDefinition was created */ + createdAt: Scalars['Time']['output']; + /** Function definitions of the Node Function */ + functionDefinitions?: Maybe; + /** Global ID of this RuntimeFunctionDefinition */ + id: Scalars['RuntimeParameterDefinitionID']['output']; + /** The runtime this Node Function belongs to */ + runtime: Runtime; + /** Time when this RuntimeFunctionDefinition was last updated */ + updatedAt: Scalars['Time']['output']; +} + + +/** Represents a Node Function definition */ +export interface RuntimeFunctionDefinitionFunctionDefinitionsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** Represents a Node parameter definition */ +export interface RuntimeParameterDefinition { + __typename?: 'RuntimeParameterDefinition'; + /** Time when this RuntimeParameterDefinition was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this RuntimeParameterDefinition */ + id: Scalars['RuntimeParameterDefinitionID']['output']; + /** Time when this RuntimeParameterDefinition was last updated */ + updatedAt: Scalars['Time']['output']; +} + +/** Represent all available types of statuses of a runtime */ +export enum RuntimeStatusType { + /** No problem with connection, everything works as expected */ + Connected = 'CONNECTED', + /** The runtime is disconnected, cause unknown */ + Disconnected = 'DISCONNECTED' +} + +/** Autogenerated input type of RuntimesCreate */ +export interface RuntimesCreateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The description for the new runtime. */ + description?: InputMaybe; + /** Name for the new runtime. */ + name: Scalars['String']['input']; + /** The Parent Id for the runtime. */ + namespaceId?: InputMaybe; +} + +/** Autogenerated return type of RuntimesCreate. */ +export interface RuntimesCreatePayload { + __typename?: 'RuntimesCreatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The newly created runtime. */ + runtime?: Maybe; +} + +/** Autogenerated input type of RuntimesDelete */ +export interface RuntimesDeleteInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The runtime to delete. */ + runtimeId: Scalars['RuntimeID']['input']; +} + +/** Autogenerated return type of RuntimesDelete. */ +export interface RuntimesDeletePayload { + __typename?: 'RuntimesDeletePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The updated organization. */ + runtime?: Maybe; +} + +/** Autogenerated input type of RuntimesRotateToken */ +export interface RuntimesRotateTokenInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The runtime to rotate the token. */ + runtimeId: Scalars['RuntimeID']['input']; +} + +/** Autogenerated return type of RuntimesRotateToken. */ +export interface RuntimesRotateTokenPayload { + __typename?: 'RuntimesRotateTokenPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The updated runtime. */ + runtime?: Maybe; +} + +/** Autogenerated input type of RuntimesUpdate */ +export interface RuntimesUpdateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** Description for the new runtime. */ + description?: InputMaybe; + /** Name for the new runtime. */ + name?: InputMaybe; + /** ID of the runtime to update. */ + runtimeId: Scalars['RuntimeID']['input']; +} + +/** Autogenerated return type of RuntimesUpdate. */ +export interface RuntimesUpdatePayload { + __typename?: 'RuntimesUpdatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The updated runtime. */ + runtime?: Maybe; +} + +/** Represents a translation */ +export interface Translation { + __typename?: 'Translation'; + /** Code of the translation */ + code: Scalars['String']['output']; + /** Content of the translation */ + content: Scalars['String']['output']; +} + +/** The connection type for Translation. */ +export interface TranslationConnection { + __typename?: 'TranslationConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface TranslationEdge { + __typename?: 'TranslationEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Represents a user */ +export interface User { + __typename?: 'User'; + /** Global admin status of the user */ + admin: Scalars['Boolean']['output']; + /** The avatar if present of the user */ + avatarPath?: Maybe; + /** Time when this User was created */ + createdAt: Scalars['Time']['output']; + /** Email of the user */ + email: Scalars['String']['output']; + /** Firstname of the user */ + firstname: Scalars['String']['output']; + /** Global ID of this User */ + id: Scalars['UserID']['output']; + /** Lastname of the user */ + lastname: Scalars['String']['output']; + /** Namespace of this user */ + namespace?: Maybe; + /** Namespace Memberships of this user */ + namespaceMemberships: NamespaceMemberConnection; + /** Time when this User was last updated */ + updatedAt: Scalars['Time']['output']; + /** Username of the user */ + username: Scalars['String']['output']; +} + + +/** Represents a user */ +export interface UserNamespaceMembershipsArgs { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; +} + +/** The connection type for User. */ +export interface UserConnection { + __typename?: 'UserConnection'; + /** Total count of collection. */ + count: Scalars['Int']['output']; + /** A list of edges. */ + edges?: Maybe>>; + /** A list of nodes. */ + nodes?: Maybe>>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +} + +/** An edge in a connection. */ +export interface UserEdge { + __typename?: 'UserEdge'; + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node?: Maybe; +} + +/** Represents an external user identity */ +export interface UserIdentity { + __typename?: 'UserIdentity'; + /** Time when this UserIdentity was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this UserIdentity */ + id: Scalars['UserIdentityID']['output']; + /** The description for the runtime if present */ + identifier: Scalars['String']['output']; + /** The name for the runtime */ + providerId: Scalars['String']['output']; + /** Time when this UserIdentity was last updated */ + updatedAt: Scalars['Time']['output']; + /** The correlating user of the identity */ + user: User; +} + +/** Represents a user session */ +export interface UserSession { + __typename?: 'UserSession'; + /** Whether or not the session is active and can be used */ + active: Scalars['Boolean']['output']; + /** Time when this UserSession was created */ + createdAt: Scalars['Time']['output']; + /** Global ID of this UserSession */ + id: Scalars['UserSessionID']['output']; + /** Token belonging to the session, only present on creation */ + token?: Maybe; + /** Time when this UserSession was last updated */ + updatedAt: Scalars['Time']['output']; + /** User that belongs to the session */ + user: User; +} + +/** Autogenerated input type of UsersIdentityLink */ +export interface UsersIdentityLinkInput { + /** The validation object */ + args: IdentityInput; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The ID of the external provider (e.g. google, discord, gitlab...) */ + providerId: Scalars['String']['input']; +} + +/** Autogenerated return type of UsersIdentityLink. */ +export interface UsersIdentityLinkPayload { + __typename?: 'UsersIdentityLinkPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The created user identity */ + userIdentity?: Maybe; +} + +/** Autogenerated input type of UsersIdentityLogin */ +export interface UsersIdentityLoginInput { + /** The validation object */ + args: IdentityInput; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The ID of the external provider (e.g. google, discord, gitlab...) */ + providerId: Scalars['String']['input']; +} + +/** Autogenerated return type of UsersIdentityLogin. */ +export interface UsersIdentityLoginPayload { + __typename?: 'UsersIdentityLoginPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The created user session */ + userSession?: Maybe; +} + +/** Autogenerated input type of UsersIdentityRegister */ +export interface UsersIdentityRegisterInput { + /** The validation object */ + args: IdentityInput; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The ID of the external provider (e.g. google, discord, gitlab...) */ + providerId: Scalars['String']['input']; +} + +/** Autogenerated return type of UsersIdentityRegister. */ +export interface UsersIdentityRegisterPayload { + __typename?: 'UsersIdentityRegisterPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The created users session */ + userSession?: Maybe; +} + +/** Autogenerated input type of UsersIdentityUnlink */ +export interface UsersIdentityUnlinkInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** The ID of the identity to remove */ + identityId: Scalars['UserIdentityID']['input']; +} + +/** Autogenerated return type of UsersIdentityUnlink. */ +export interface UsersIdentityUnlinkPayload { + __typename?: 'UsersIdentityUnlinkPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The removed identity */ + userIdentity?: Maybe; +} + +/** Autogenerated input type of UsersLogin */ +export interface UsersLoginInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** Email of the user */ + email?: InputMaybe; + /** The data of the mfa login */ + mfa?: InputMaybe; + /** Password of the user */ + password: Scalars['String']['input']; + /** Username of the user */ + username?: InputMaybe; +} + +/** Autogenerated return type of UsersLogin. */ +export interface UsersLoginPayload { + __typename?: 'UsersLoginPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The created user session */ + userSession?: Maybe; +} + +/** Autogenerated input type of UsersLogout */ +export interface UsersLogoutInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** ID of the session to logout */ + userSessionId: Scalars['UserSessionID']['input']; +} + +/** Autogenerated return type of UsersLogout. */ +export interface UsersLogoutPayload { + __typename?: 'UsersLogoutPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The logged out user session */ + userSession?: Maybe; +} + +/** Autogenerated input type of UsersMfaBackupCodesRotate */ +export interface UsersMfaBackupCodesRotateInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; +} + +/** Autogenerated return type of UsersMfaBackupCodesRotate. */ +export interface UsersMfaBackupCodesRotatePayload { + __typename?: 'UsersMfaBackupCodesRotatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** The newly rotated backup codes. */ + codes?: Maybe>; + /** Errors encountered during execution of the mutation. */ + errors: Array; +} + +/** Autogenerated input type of UsersMfaTotpGenerateSecret */ +export interface UsersMfaTotpGenerateSecretInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; +} + +/** Autogenerated return type of UsersMfaTotpGenerateSecret. */ +export interface UsersMfaTotpGenerateSecretPayload { + __typename?: 'UsersMfaTotpGenerateSecretPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The created and signed secret */ + secret?: Maybe; +} + +/** Autogenerated input type of UsersMfaTotpValidateSecret */ +export interface UsersMfaTotpValidateSecretInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** + * The current totp at the time to verify the mfa + * authentication device + */ + currentTotp: Scalars['String']['input']; + /** The signed secret from the generation */ + secret: Scalars['String']['input']; +} + +/** Autogenerated return type of UsersMfaTotpValidateSecret. */ +export interface UsersMfaTotpValidateSecretPayload { + __typename?: 'UsersMfaTotpValidateSecretPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The modified user */ + user?: Maybe; +} + +/** Autogenerated input type of UsersRegister */ +export interface UsersRegisterInput { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** Email of the user */ + email: Scalars['String']['input']; + /** Password of the user */ + password: Scalars['String']['input']; + /** The repeated password of the user to check for typos */ + passwordRepeat: Scalars['String']['input']; + /** Username of the user */ + username: Scalars['String']['input']; +} + +/** Autogenerated return type of UsersRegister. */ +export interface UsersRegisterPayload { + __typename?: 'UsersRegisterPayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The created users session */ + userSession?: Maybe; +} + +/** Autogenerated input type of UsersUpdate */ +export interface UsersUpdateInput { + /** New global admin status for the user. */ + admin?: InputMaybe; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + /** New email for the user. */ + email?: InputMaybe; + /** New firstname for the user. */ + firstname?: InputMaybe; + /** New lastname for the user. */ + lastname?: InputMaybe; + /** The data of the mfa validation */ + mfa?: InputMaybe; + /** New password for the user. */ + password?: InputMaybe; + /** New password repeat for the user to check for typos, required if password is set. */ + passwordRepeat?: InputMaybe; + /** ID of the user to update. */ + userId: Scalars['UserID']['input']; + /** New username for the user. */ + username?: InputMaybe; +} + +/** Autogenerated return type of UsersUpdate. */ +export interface UsersUpdatePayload { + __typename?: 'UsersUpdatePayload'; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: Maybe; + /** Errors encountered during execution of the mutation. */ + errors: Array; + /** The updated user. */ + user?: Maybe; +} diff --git a/package-lock.json b/package-lock.json index 7bde9038..bc01a490 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,8 @@ "@ariakit/react": "^0.4.17", "@babel/plugin-proposal-decorators": "^7.28.0", "@babel/plugin-transform-class-properties": "^7.27.1", + "@code0-tech/sagittarius-graphql-types": "^0.0.0-00f33663039d78ad59e95306730878c687de5c48", + "@dagrejs/dagre": "^1.1.5", "@mdx-js/react": "^3.1.0", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-dialog": "^1.1.14", @@ -42,6 +44,7 @@ "@tabler/icons-react": "3.34.1", "@types/react": "^19.1.8", "@vitejs/plugin-react": "^4.6.0", + "@xyflow/react": "^12.8.2", "axe-playwright": "^2.1.0", "babel-loader": "^9.2.1", "babel-plugin-react-compiler": "^19.1.0-rc.2", @@ -73,14 +76,18 @@ }, "peerDependencies": { "@ariakit/react": "^0.4.5", + "@code0-tech/sagittarius-graphql-types": "^0.0.0-4b2e73eae302fe499001bf42fdb3a6bcc5be78aa", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-one-time-password-field": "^0.1.7", "@radix-ui/react-radio-group": "^1.3.7", + "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-toggle-group": "^1.1.10", + "@radix-ui/react-tooltip": "^1.2.8", "@tabler/icons-react": "^3.5.0", + "@xyflow/react": "^12.8.2", "cmdk": "^1.1.1", "js-md5": "^0.8.3", "merge-props": "^6.0.0", @@ -2279,6 +2286,12 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@code0-tech/sagittarius-graphql-types": { + "version": "0.0.0-00f33663039d78ad59e95306730878c687de5c48", + "resolved": "https://registry.npmjs.org/@code0-tech/sagittarius-graphql-types/-/sagittarius-graphql-types-0.0.0-00f33663039d78ad59e95306730878c687de5c48.tgz", + "integrity": "sha512-g8bv5KWq+c8jd9Gb+86O3TT/Dcvt2injJcZ5aFJkN8ZZz/4VsA0EcC3h/hCsoiBIEquvAdQQVFriNAbC0JrKRA==", + "dev": true + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -2303,6 +2316,26 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@dagrejs/dagre": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.1.5.tgz", + "integrity": "sha512-Ghgrh08s12DCL5SeiR6AoyE80mQELTWhJBRmXfFoqDiFkR458vPEdgTbbjA0T+9ETNxUblnD0QW55tfdvi5pjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dagrejs/graphlib": "2.2.4" + } + }, + "node_modules/@dagrejs/graphlib": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.2.4.tgz", + "integrity": "sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">17.0.0" + } + }, "node_modules/@daybrush/utils": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/@daybrush/utils/-/utils-1.13.0.tgz", @@ -6590,6 +6623,61 @@ "@types/node": "*" } }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/doctrine": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", @@ -7146,6 +7234,40 @@ "dev": true, "peer": true }, + "node_modules/@xyflow/react": { + "version": "12.9.0", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.9.0.tgz", + "integrity": "sha512-bt37E8Wf2HQ7hHQaMSnOw4UEWQqWlNwzfgF9tjix5Fu9Pn/ph3wbexSS/wbWnTkv0vhgMVyphQLfFWIuCe59hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xyflow/system": "0.0.71", + "classcat": "^5.0.3", + "zustand": "^4.4.0" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@xyflow/system": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.71.tgz", + "integrity": "sha512-O2xIK84Uv1hH8qzeY94SKsj0R1n2jXHLsX6RZnM4x1Uc4oWiVbXDFucnkbFwhnQm3IIdAxkbgd2rEDp5oTRhhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-drag": "^3.0.7", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-selection": "^3.0.10", + "@types/d3-transition": "^3.0.8", + "@types/d3-zoom": "^3.0.8", + "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + } + }, "node_modules/acorn": { "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", @@ -8062,6 +8184,13 @@ "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", + "dev": true, + "license": "MIT" + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -8579,6 +8708,120 @@ "node": ">=0.8" } }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -17305,6 +17548,35 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 37b472fc..38d771ec 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "A simple template for a custom React component library", "scripts": { "dev": "npm run storybook:dev", - "test": "jest", + "test": "jest --passWithNoTests", "build": "npm run clear && vite build", "clear": "concurrently \"rimraf ./dist\" \"rimraf ./storybook-static\"", "rollup": "rollup -c", @@ -21,6 +21,8 @@ "@ariakit/react": "^0.4.17", "@babel/plugin-proposal-decorators": "^7.28.0", "@babel/plugin-transform-class-properties": "^7.27.1", + "@code0-tech/sagittarius-graphql-types": "^0.0.0-00f33663039d78ad59e95306730878c687de5c48", + "@dagrejs/dagre": "^1.1.5", "@mdx-js/react": "^3.1.0", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-dialog": "^1.1.14", @@ -52,6 +54,7 @@ "@tabler/icons-react": "3.34.1", "@types/react": "^19.1.8", "@vitejs/plugin-react": "^4.6.0", + "@xyflow/react": "^12.8.2", "axe-playwright": "^2.1.0", "babel-loader": "^9.2.1", "babel-plugin-react-compiler": "^19.1.0-rc.2", @@ -88,14 +91,18 @@ "types": "dist/index.d.ts", "peerDependencies": { "@ariakit/react": "^0.4.5", + "@code0-tech/sagittarius-graphql-types": "^0.0.0-4b2e73eae302fe499001bf42fdb3a6bcc5be78aa", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-one-time-password-field": "^0.1.7", "@radix-ui/react-radio-group": "^1.3.7", + "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-toggle-group": "^1.1.10", + "@radix-ui/react-tooltip": "^1.2.8", "@tabler/icons-react": "^3.5.0", + "@xyflow/react": "^12.8.2", "cmdk": "^1.1.1", "js-md5": "^0.8.3", "merge-props": "^6.0.0", diff --git a/src/components/badge/Badge.style.scss b/src/components/badge/Badge.style.scss index 207fc066..233a9d90 100644 --- a/src/components/badge/Badge.style.scss +++ b/src/components/badge/Badge.style.scss @@ -7,6 +7,7 @@ display: inline-flex; font-size: variables.$xs; width: fit-content; + align-items: center; & { @include helpers.fontStyle(); diff --git a/src/components/button/Button.style.scss b/src/components/button/Button.style.scss index 0f285887..c64607ff 100644 --- a/src/components/button/Button.style.scss +++ b/src/components/button/Button.style.scss @@ -25,6 +25,7 @@ @include box.boxActive($color, variables.$white, variables.$white); &.button--outlined { + background: transparent; //@include helpers.glassBox($color, variables.$white, $color); } diff --git a/src/components/button/Button.tsx b/src/components/button/Button.tsx index 298ea00d..947475cc 100644 --- a/src/components/button/Button.tsx +++ b/src/components/button/Button.tsx @@ -19,7 +19,7 @@ export interface ButtonProps extends Code0Component { const Button: React.FC = React.forwardRef((props, ref) => { - const {children, variant = "outlined", color = "secondary", active = false, disabled = false, ...args} = props + const {children, variant = "normal", color = "secondary", active = false, disabled = false, ...args} = props return ) + + + return +}) \ No newline at end of file diff --git a/src/components/d-flow/type/DFlowType.data.ts b/src/components/d-flow/type/DFlowType.data.ts deleted file mode 100644 index b40b5ee4..00000000 --- a/src/components/d-flow/type/DFlowType.data.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {FlowType} from "./DFlowType.view"; -import {EDataType, EDataTypeRuleType} from "../data-type/DFlowDataType.view"; - -export const REST_FLOW_TYPE: FlowType = { - flow_type_id: "REST", - name: [{ - text: "REST", - code: "de_de" - }], - description: [{ - text: "REST", - code: "de_de" - }], - settings: [{ - flow_definition_settings_id: "rest_setting_1", - key: "HTTP_METHOD", - type: "HTTP_METHOD", - name: [{ - text: "Http Methode", - code: "de_de" - }], - description: [{ - text: "Wähle eine Http Methode aus", - code: "de_de" - }], - unique: false, - }, { - flow_definition_settings_id: "rest_setting_2", - key: "URL", - type: "URL_ENDPOINT", - name: [{ - text: "Endpoint", - code: "de_de" - }], - description: [{ - text: "Gebe einen Endpoint an", - code: "de_de" - }], - unique: true - }], - input_type: { - data_type_id: "REST_FLOW_INPUT_TYPE", - name: [{ - text: "Anfrage Objekt", - code: "de_de" - }], - type: EDataType.OBJECT, - rules: [{ - type: EDataTypeRuleType.LOCK_KEY, - config: {key: "req", type: "REQ_OBJECT"} - }] - }, - return_type: { - data_type_id: "REST_FLOW_RETURN_TYPE", - name: [{ - text: "Rückgabe Objekt", - code: "de_de" - }], - type: EDataType.OBJECT - } -} \ No newline at end of file diff --git a/src/components/d-flow/type/DFlowType.service.ts b/src/components/d-flow/type/DFlowType.service.ts index e69de29b..95bf3f38 100644 --- a/src/components/d-flow/type/DFlowType.service.ts +++ b/src/components/d-flow/type/DFlowType.service.ts @@ -0,0 +1,19 @@ +import {ReactiveArrayService, ReactiveArrayStore} from "../../../utils/reactiveArrayService"; +import {FlowTypeView} from "./DFlowType.view"; +import {Scalars} from "@code0-tech/sagittarius-graphql-types"; + +export interface DFlowTypeService { + getById(id: string): FlowTypeView | undefined +} + +export class DFlowTypeReactiveService extends ReactiveArrayService implements DFlowTypeService { + + constructor(store: ReactiveArrayStore) { + super(store); + } + + getById(id: Scalars['TypesFlowTypeID']['output']): FlowTypeView | undefined { + return this.values().find(value => value.id === id); + } + +} \ No newline at end of file diff --git a/src/components/d-flow/type/DFlowType.view.ts b/src/components/d-flow/type/DFlowType.view.ts index 0eac4235..d2d6caeb 100644 --- a/src/components/d-flow/type/DFlowType.view.ts +++ b/src/components/d-flow/type/DFlowType.view.ts @@ -1,22 +1,92 @@ -import {Translation} from "../../../utils/translation"; -import {DataTypeObject} from "../data-type/DFlowDataType.view"; - -export interface FlowType { - flow_type_id: string - name: Translation[] - description: Translation[] - documentation?: Translation[] //as markdown - settings: FlowTypeSetting[] - input_type?: DataTypeObject // data type - return_type?: DataTypeObject -} - -export interface FlowTypeSetting { - flow_definition_settings_id: string - key: string - name: Translation[] - unique: boolean - description: Translation[] - type: string // data type id - default_value?: object +import { + DataType, + FlowType, + FlowTypeSetting, + Maybe, + Scalars, + TranslationConnection +} from "@code0-tech/sagittarius-graphql-types"; + + +export class FlowTypeView { + + /** Time when this FlowType was created */ + private readonly _createdAt?: Maybe; + /** Descriptions of the flow type */ + private readonly _descriptions?: Maybe; + /** Editable status of the flow type */ + private readonly _editable?: Maybe; + /** Flow type settings of the flow type */ + private readonly _flowTypeSettings?: Maybe>; + /** Global ID of this FlowType */ + private readonly _id?: Maybe; + /** Identifier of the flow type */ + private readonly _identifier?: Maybe; + /** Input type of the flow type */ + private readonly _inputType?: Maybe; + /** Names of the flow type */ + private readonly _names?: Maybe; + /** Return type of the flow type */ + private readonly _returnType?: Maybe; + /** Time when this FlowType was last updated */ + private readonly _updatedAt?: Maybe; + + + constructor(flowType: FlowType) { + this._createdAt = flowType.createdAt; + this._descriptions = flowType.descriptions; + this._editable = flowType.editable; + this._flowTypeSettings = flowType.flowTypeSettings; + this._id = flowType.id; + this._identifier = flowType.identifier; + this._inputType = flowType.inputType; + this._names = flowType.names; + this._returnType = flowType.returnType; + this._updatedAt = flowType.updatedAt; + } + + + get createdAt(): Maybe | undefined { + return this._createdAt; + } + + get descriptions(): Maybe | undefined { + return this._descriptions; + } + + get editable(): Maybe | undefined { + return this._editable; + } + + get flowTypeSettings(): Maybe> | undefined { + return this._flowTypeSettings; + } + + get id(): Maybe | undefined { + return this._id; + } + + get identifier(): Maybe | undefined { + return this._identifier; + } + + get inputType(): Maybe | undefined { + return this._inputType; + } + + get names(): Maybe | undefined { + return this._names; + } + + get returnType(): Maybe | undefined { + return this._returnType; + } + + get updatedAt(): Maybe | undefined { + return this._updatedAt; + } + + json(): FlowType | undefined { + return undefined + } } \ No newline at end of file diff --git a/src/components/d-flow/viewport/DFlowViewport.edges.hook.ts b/src/components/d-flow/viewport/DFlowViewport.edges.hook.ts new file mode 100644 index 00000000..b0dc473b --- /dev/null +++ b/src/components/d-flow/viewport/DFlowViewport.edges.hook.ts @@ -0,0 +1,234 @@ +import {useService, useStore} from "../../../utils/contextStore"; +import {DFlowReactiveService} from "../DFlow.service"; +import {Edge} from "@xyflow/react"; +import {NodeFunctionView} from "../DFlow.view"; +import {DFlowFunctionReactiveService} from "../function/DFlowFunction.service"; +import {DFlowDataTypeReactiveService} from "../data-type/DFlowDataType.service"; +import React from "react"; +import {DataTypeIdentifier, DataTypeVariant, Scalars} from "@code0-tech/sagittarius-graphql-types"; + +// Deine Primärfarbe als Start, danach harmonisch verteilt +export const FLOW_EDGE_RAINBOW: string[] = [ + '#70ffb2', // 0 – Primary (Grün) + '#70e2ff', // 1 – Cyan + '#709aff', // 2 – Blau + '#a170ff', // 3 – Violett + '#f170ff', // 4 – Magenta + '#ff70b5', // 5 – Pink/Rot + '#ff7070', // 6 – Orange-Rot + '#fff170', // 7 – Gelb +]; + +export const useFlowViewportEdges = (flowId: string): Edge[] => { + const flowService = useService(DFlowReactiveService); + const functionService = useService(DFlowFunctionReactiveService); + const dataTypeService = useService(DFlowDataTypeReactiveService); + const flow = flowService.getById(flowId); + const flowStore = useStore(DFlowReactiveService) + const functionStore = useStore(DFlowFunctionReactiveService) + const dataTypeStore = useStore(DFlowDataTypeReactiveService) + + if (!flow) return []; + + /* ------------------------------------------------------------------ */ + const edges: Edge[] = []; + + /** merkt sich für jede Function-Card die Gruppen-IDs, + * **für die wirklich ein Funktions-Wert existiert** */ + const groupsWithValue = new Map(); + + let idCounter = 0; // globale, fortlaufende Id-Vergabe + + const functionCache = new Map>(); + const dataTypeCache = new Map>(); + + const getFunctionDefinitionCached = ( + id: Scalars['FunctionDefinitionID']['output'], + cache = functionCache, + ) => { + if (!cache.has(id)) { + cache.set(id, functionService.getFunctionDefinition(id)); + } + return cache.get(id); + }; + + const getDataTypeCached = ( + type: DataTypeIdentifier, + cache = dataTypeCache, + ) => { + if (!cache.has(type)) { + cache.set(type, dataTypeService.getDataType(type)); + } + return cache.get(type); + }; + + /* ------------------------------------------------------------------ */ + const traverse = ( + fn: NodeFunctionView, + parentFnId?: string, // Id der *Function-Card* des Aufrufers + level = 0, // Tiefe ⇒ Farbe aus dem Rainbow-Array, + fnCache = functionCache, + dtCache = dataTypeCache, + ): string => { + + /* ------- Id der aktuellen Function-Card im Diagramm ---------- */ + const fnId = `${fn.functionDefinition?.identifier}-${idCounter++}`; + + if (idCounter == 1) { + // erste Function-Card → Verbindung Trigger → Function + edges.push({ + id: `trigger-${fnId}-next`, + source: flow.id as string, // Handle-Bottom des Trigger-Nodes + target: fnId, // Handle-Top der Function-Card + data: { + color: FLOW_EDGE_RAINBOW[level % FLOW_EDGE_RAINBOW.length], + isParameter: false + }, + deletable: false, + selectable: false, + }); + } + + /* ------- vertikale Kante (nextNode) -------------------------- */ + if (parentFnId) { + const startGroups = groupsWithValue.get(parentFnId) ?? []; + + if (startGroups.length > 0) { + startGroups.forEach((gId, idx) => edges.push({ + id: `${gId}-${fnId}-next-${idx}`, + source: gId, // Handle-Bottom der Group-Card + target: fnId, // Handle-Top der Function-Card + data: { + color: FLOW_EDGE_RAINBOW[level % FLOW_EDGE_RAINBOW.length], + isParameter: false + }, + deletable: false, + selectable: false, + })); + } else { + edges.push({ + id: `${parentFnId}-${fnId}-next`, + source: parentFnId, // Handle-Bottom der Function-Card + target: fnId, // Handle-Top der Function-Card + data: { + color: FLOW_EDGE_RAINBOW[level % FLOW_EDGE_RAINBOW.length], + isParameter: false + }, + deletable: false, + selectable: false, + }); + } + } + + /* ------- horizontale Kanten für Parameter -------------------- */ + fn.parameters?.forEach((param) => { + const val = param.value; + const def = getFunctionDefinitionCached(fn.functionDefinition?.id!!, fnCache) + ?.parameterDefinitions?.find(p => p.id === param.id); + const paramType = def?.dataTypeIdentifier; + const paramDT = paramType ? getDataTypeCached(paramType, dtCache) : undefined; + + if (!val) return + + /* --- NODE-Parameter → Group-Card ------------------------- */ + if (paramDT?.variant === DataTypeVariant.Node) { + const groupId = `${fnId}-group-${idCounter++}`; + + /* Verbindung Gruppe → Function-Card (horizontal) */ + edges.push({ + id: `${fnId}-${groupId}-param-${param.id}`, + source: fnId, // FunctionCard (Quelle) + target: groupId, // GroupCard (Ziel – hat Top: target) + deletable: false, + selectable: false, + label: param.id, + data: { + color: FLOW_EDGE_RAINBOW[level % FLOW_EDGE_RAINBOW.length], + isParameter: false, + }, + }); + + /* existiert ein Funktions-Wert für dieses Param-Feld? */ + if (val && val instanceof NodeFunctionView) { + /* merken: diese Group-Card besitzt Content – das ist + später Startpunkt der next-Kante */ + (groupsWithValue.get(fnId) ?? (groupsWithValue.set(fnId, []), + groupsWithValue.get(fnId)!)) + .push(groupId); + + /* rekursiv Funktions-Ast innerhalb der Gruppe */ + traverse(param.value as NodeFunctionView, + undefined, + level + 1, + fnCache, + dtCache); + } + } + + /* --- anderer Parameter, der selbst eine Function hält ---- */ + else if (val && val instanceof NodeFunctionView) { + const subFnId = traverse(param.value as NodeFunctionView, + undefined, + level + 1, + fnCache, + dtCache); + + edges.push({ + id: `${subFnId}-${fnId}-param-${param.id}`, + source: subFnId, + target: fnId, + targetHandle: `param-${param.id}`, + animated: true, + deletable: false, + selectable: false, + data: { + color: FLOW_EDGE_RAINBOW[(level + 1) % FLOW_EDGE_RAINBOW.length], + isParameter: true + }, + }); + } + }); + + /* ------- Rekursion auf nextNode ------------------------------ */ + if (fn.nextNodeId) { + traverse(flow.getNodeById(fn.nextNodeId!!)!!, fnId, level, fnCache, dtCache); // gleiche Ebenentiefe + } else { + // letzte Function-Card im Ast → Add-new-node wie *normale* nextNode behandeln + const suggestionNodeId = `${fnId}-suggestion`; + const startGroups = groupsWithValue.get(fnId) ?? []; + + if (startGroups.length > 0) { + startGroups.forEach((gId, idx) => edges.push({ + id: `${gId}-${suggestionNodeId}-next-${idx}`, + source: gId, // wie bei echter nextNode von Group-Card starten + target: suggestionNodeId, // Ziel ist die Suggestion-Card + data: { + color: FLOW_EDGE_RAINBOW[level % FLOW_EDGE_RAINBOW.length], + isSuggestion: true, + }, + deletable: false, + selectable: false, + })); + } else { + edges.push({ + id: `${fnId}-${suggestionNodeId}-next`, + source: fnId, // Handle-Bottom der Function-Card + target: suggestionNodeId, // Handle-Top der Suggestion-Card + data: { + color: FLOW_EDGE_RAINBOW[level % FLOW_EDGE_RAINBOW.length], + isSuggestion: true, + }, + deletable: false, + selectable: false, + }); + } + } + + return fnId; + }; + + /* ------------------------------------------------------------------ */ + traverse(flow.getNodeById(flow.startingNodeId!!)!!, undefined, 0, functionCache, dataTypeCache); + + return React.useMemo(() => edges, [flowStore, functionStore, dataTypeStore, edges]); +}; \ No newline at end of file diff --git a/src/components/d-flow/viewport/DFlowViewport.nodes.hook.ts b/src/components/d-flow/viewport/DFlowViewport.nodes.hook.ts new file mode 100644 index 00000000..85f640c0 --- /dev/null +++ b/src/components/d-flow/viewport/DFlowViewport.nodes.hook.ts @@ -0,0 +1,239 @@ +import {useService} from "../../../utils/contextStore"; +import {DFlowReactiveService} from "../DFlow.service"; +import {NodeFunctionView} from "../DFlow.view"; +import {Node} from "@xyflow/react"; +import {DFlowFunctionReactiveService} from "../function/DFlowFunction.service"; +import {DFlowDataTypeReactiveService} from "../data-type/DFlowDataType.service"; +import {DataTypeIdentifier, DataTypeVariant, Scalars} from "@code0-tech/sagittarius-graphql-types"; + +const packageNodes = new Map([ + ['std', 'default'], +]); + +/** + * Returns the value of the best-matching key from a Map. + * + * Matching priority: + * 1) Exact match + * 2) Longest prefix match (with bonus if the prefix ends at a token boundary) + * 3) Largest common prefix length (with small boundary bonus) + * + * Token boundaries are characters in /[:._\-\/\s]+/ (e.g., "::", ".", "_", "-", "/", whitespace). + * + * Performance: + * - O(N * M), where N = number of keys, M = average key length (string comparisons only). + * + */ +const bestMatchValue = (map: Map, input: string): string => { + if (!input || map.size === 0) return "" + + const SEP = /[:._\-\/\s]+/; + const normInput = input.trim().toLowerCase(); + + let bestKey: string | null = null; + let bestScore = -Infinity; + + for (const [key, value] of map.entries()) { + const normKey = key.trim().toLowerCase(); + + // (1) Exact match → immediately return (strongest possible score) + if (normInput === normKey) { + return value; + } + + let score = 0; + + // (2) Prefix match + if (normInput.startsWith(normKey)) { + score = 2000 + normKey.length * 2; + + // Bonus if the prefix ends at a clean token boundary (or equals the whole input) + const boundaryChar = normInput.charAt(normKey.length); // '' if out of range + if (boundaryChar === "" || SEP.test(boundaryChar)) { + score += 200; + } + } else { + // (3) Largest common prefix (LCP) + const max = Math.min(normInput.length, normKey.length); + let lcp = 0; + while (lcp < max && normInput.charCodeAt(lcp) === normKey.charCodeAt(lcp)) { + lcp++; + } + if (lcp > 0) { + score = 1000 + lcp; + + // Small bonus if LCP ends at a boundary on either side + const inBoundaryChar = normInput.charAt(lcp); + const keyBoundaryChar = normKey.charAt(lcp); + if ( + inBoundaryChar === "" || + SEP.test(inBoundaryChar) || + keyBoundaryChar === "" || + SEP.test(keyBoundaryChar) + ) { + score += 50; + } + } + } + + // Best candidate so far? Tie-breaker favors longer key (more specific) + if (score > bestScore) { + bestScore = score; + bestKey = key; + } else if (score === bestScore && bestKey !== null && key.length > bestKey.length) { + bestKey = key; + } else if (score === bestScore && bestKey === null) { + bestKey = key; + } + } + + return bestKey !== null ? map.get(bestKey)! : ""; +}; + +export const useFlowViewportNodes = (flowId: string): Node[] => { + const flowService = useService(DFlowReactiveService); + const functionService = useService(DFlowFunctionReactiveService); + const dataTypeService = useService(DFlowDataTypeReactiveService); + const flow = flowService.getById(flowId); + + if (!flow) return []; + + const nodes: Node[] = []; + let idCounter = 0; + + const functionCache = new Map>(); + const dataTypeCache = new Map>(); + + const getFunctionDefinitionCached = ( + id: Scalars['FunctionDefinitionID']['output'], + cache = functionCache, + ) => { + if (!cache.has(id)) { + cache.set(id, functionService.getFunctionDefinition(id)); + } + return cache.get(id); + }; + + const getDataTypeCached = ( + type: DataTypeIdentifier, + cache = dataTypeCache, + ) => { + if (!cache.has(type)) { + cache.set(type, dataTypeService.getDataType(type)); + } + return cache.get(type); + }; + + // Global, strictly increasing group-id used to build the scope PATH ([0], [0,1], [0,2], [0,2,3], ...) + let globalScopeId = 0; + const nextScopeId = () => ++globalScopeId; + + // Global, strictly increasing node index across the entire flow (only real nodes) + let globalNodeIndex = 0; + + //trigger node + nodes.push({ + id: `${flow.id}`, + type: "trigger", + position: { x: 0, y: 0 }, + draggable: false, + data: { + instance: flow, + flowId, + } + }) + + const traverse = ( + fn: NodeFunctionView, + isParameter = false, + parentId?: string, + depth: number = 0, + scopePath: number[] = [0], + parentGroup?: string, + fnCache = functionCache, + dtCache = dataTypeCache, + ) => { + const id = `${fn.functionDefinition?.identifier}-${idCounter++}`; + const index = ++globalNodeIndex; // global node level + + nodes.push({ + id, + type: bestMatchValue(packageNodes, fn.functionDefinition?.identifier!!), + position: { x: 0, y: 0 }, + draggable: false, + parentId: parentGroup, + extent: parentGroup ? "parent" : undefined, + data: { + instance: fn, + isParameter, + flowId, + linkingId: isParameter ? parentId : undefined, + scope: scopePath, // scope is now a PATH (number[]) + depth, // structural depth (0 at root, +1 per group) + index, // global node level + }, + }); + + if (!fn.nextNodeId && !isParameter) { + nodes.push({ + id: `${id}-suggestion`, + type: "suggestion", + position: { x: 0, y: 0 }, + draggable: false, + extent: parentGroup ? "parent" : undefined, + parentId: parentGroup, + data: { + flowId: flowId, + parentFunction: fn, + }, + }); + } + + const definition = getFunctionDefinitionCached(fn.functionDefinition?.id!!, fnCache); + + fn.parameters?.forEach((param) => { + const paramType = definition?.parameterDefinitions!!.find(p => p.id == param.id)?.dataTypeIdentifier; + const paramDataType = paramType ? getDataTypeCached(paramType, dtCache) : undefined; + + if (paramDataType?.variant === DataTypeVariant.Node) { + if (param.value && param.value instanceof NodeFunctionView) { + const groupId = `${id}-group-${idCounter++}`; + + // New group: extend scope PATH with a fresh segment and increase depth. + const childScopePath = [...scopePath, nextScopeId()]; + + nodes.push({ + id: groupId, + type: "group", + position: { x: 0, y: 0 }, + draggable: false, + parentId: parentGroup, + extent: parentGroup ? "parent" : undefined, + data: { + isParameter: true, + linkingId: id, + flowId, + depth: depth + 1, + scope: childScopePath, + }, + }); + + // Child function inside the group uses the group's depth and scope PATH. + traverse(param.value as NodeFunctionView, false, undefined, depth + 1, childScopePath, groupId, fnCache, dtCache); + } + } else if (param.value && param.value instanceof NodeFunctionView) { + // Functions passed as non-NODE parameters live in the same depth/scope PATH. + traverse(param.value as NodeFunctionView, true, id, depth, scopePath, parentGroup, fnCache, dtCache); + } + }); + + if (fn.nextNodeId) { + // Linear chain continues in the same depth/scope PATH. + traverse(flow.getNodeById(fn.nextNodeId!!)!!, false, undefined, depth, scopePath, parentGroup, fnCache, dtCache); + } + }; + + // Root lane: depth 0, scope path [0] + traverse(flow.getNodeById(flow.startingNodeId!!)!!, false, undefined, 0, [0], undefined, functionCache, dataTypeCache); + return nodes; +}; \ No newline at end of file diff --git a/src/components/d-flow/viewport/DFlowViewportControls.tsx b/src/components/d-flow/viewport/DFlowViewportControls.tsx new file mode 100644 index 00000000..c8c91851 --- /dev/null +++ b/src/components/d-flow/viewport/DFlowViewportControls.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import {Panel, useReactFlow, useViewport} from "@xyflow/react"; +import ButtonGroup from "../../button-group/ButtonGroup"; +import Button from "../../button/Button"; +import {IconFocusCentered, IconMinus, IconPlus} from "@tabler/icons-react"; +import Badge from "../../badge/Badge"; +import Flex from "../../flex/Flex"; +import {DFlowViewportMiniMap} from "./DFlowViewportMiniMap"; + +export const DFlowViewportControls: React.FC = () => { + + const viewport = useViewport(); + const reactFlow = useReactFlow(); + + const zoomIn = () => { + reactFlow.zoomIn() + } + + const zoomOut = () => { + reactFlow.zoomOut() + } + + const center = () => { + reactFlow.fitView() + } + + const getCurrentZoomInPercent = () => { + return Math.round(viewport.zoom * 100); + } + + return + + + + + + + + {getCurrentZoomInPercent()}% + + + + +} \ No newline at end of file diff --git a/src/components/d-flow/viewport/DFlowViewportEdge.tsx b/src/components/d-flow/viewport/DFlowViewportEdge.tsx new file mode 100644 index 00000000..5f85659b --- /dev/null +++ b/src/components/d-flow/viewport/DFlowViewportEdge.tsx @@ -0,0 +1,50 @@ +import {Code0Component} from "../../../utils/types"; +import {BaseEdge, Edge, EdgeLabelRenderer, EdgeProps, getSmoothStepPath, Position} from "@xyflow/react"; +import React, {memo} from "react"; +import Badge from "../../badge/Badge"; + +export interface DFlowViewportEdgeDataProps extends Code0Component { + //some data we will use + color?: string + isParameter?: boolean + isSuggestion?: boolean +} + +// @ts-ignore +export type DFlowViewportEdgeProps = EdgeProps> + +export const DFlowViewportEdge: React.FC = memo((props) => { + + const {sourceX, sourceY, targetX, targetY, id, data, ...rest} = props + + const [edgePath, labelX, labelY] = getSmoothStepPath({ + sourceX, + sourceY, + sourcePosition: data?.isParameter ? Position.Left : Position.Bottom, + targetX, + targetY, + targetPosition: data?.isParameter ? Position.Right : Position.Top, + borderRadius: 16, + centerY: data?.isSuggestion ? targetY - 37.5 : targetY - 37.5 + }) + + return <> + + {props.label ? ( + +
+ + {props.label} + +
+
+ ) : null} + + + +}) \ No newline at end of file diff --git a/src/components/d-flow/viewport/DFlowViewportMiniMap.style.scss b/src/components/d-flow/viewport/DFlowViewportMiniMap.style.scss new file mode 100644 index 00000000..329c940e --- /dev/null +++ b/src/components/d-flow/viewport/DFlowViewportMiniMap.style.scss @@ -0,0 +1,21 @@ +@use "../../../styles/helpers"; +@use "../../../styles/box"; +@use "../../../styles/variables"; + +.d-flow-viewport-mini-map { + + & { + @include box.box(variables.$primary); + @include helpers.borderRadius(); + margin: 0; + width: 100%; + } + + > svg { + @include helpers.borderRadius(); + } + + * .react-flow__minimap-mask { + fill: rgba(variables.$secondary, 0.1); + } +} \ No newline at end of file diff --git a/src/components/d-flow/viewport/DFlowViewportMiniMap.tsx b/src/components/d-flow/viewport/DFlowViewportMiniMap.tsx new file mode 100644 index 00000000..7c1f2119 --- /dev/null +++ b/src/components/d-flow/viewport/DFlowViewportMiniMap.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import {MiniMap, useNodes} from "@xyflow/react"; +import {FLOW_EDGE_RAINBOW} from "./DFlowViewport.edges.hook"; +import "./DFlowViewportMiniMap.style.scss" + +export const DFlowViewportMiniMap: React.FC = (props) => { + + const nodes = useNodes(); + + return { + + const node = nodes.find(node => node.id === props1.id) + if (!node) return null + + if (node.type == "suggestion") return null + + if (node.type == "group") { + + const depth = (node.data as any)?.depth ?? 0; + const color = FLOW_EDGE_RAINBOW[depth % FLOW_EDGE_RAINBOW.length]; + + return + } + + return + + }}/> + +} \ No newline at end of file diff --git a/src/components/d-flow/viewport/cards/DFlowViewportDefaultCard.style.scss b/src/components/d-flow/viewport/cards/DFlowViewportDefaultCard.style.scss new file mode 100644 index 00000000..63c55e1e --- /dev/null +++ b/src/components/d-flow/viewport/cards/DFlowViewportDefaultCard.style.scss @@ -0,0 +1,28 @@ +@use "../../../../styles/variables"; +@use "../../../../styles/helpers"; +@use "../../../../styles/box"; + +.d-flow-viewport-default-card { + &__handle { + border: none !important; + padding: 1px; + z-index: -1; + opacity: 0; + } + + &__inspection { + top: 0; + transform: translateY(-50%); + padding: variables.$xxs; + + & { + @include box.box(variables.$primary); + @include helpers.borderRadius(); + position: absolute; + } + } + + &--active { + box-shadow: inset 0 0 0 1px helpers.borderColor(variables.$info), 0 0 0 3px helpers.borderColor(variables.$info); + } +} \ No newline at end of file diff --git a/src/components/d-flow/viewport/cards/DFlowViewportDefaultCard.tsx b/src/components/d-flow/viewport/cards/DFlowViewportDefaultCard.tsx new file mode 100644 index 00000000..388ba031 --- /dev/null +++ b/src/components/d-flow/viewport/cards/DFlowViewportDefaultCard.tsx @@ -0,0 +1,243 @@ +import {Code0Component} from "../../../../utils/types"; +import {Handle, Node, NodeProps, Position, useReactFlow, useStore, useStoreApi} from "@xyflow/react"; +import {NodeFunctionView, NodeParameterView} from "../../DFlow.view"; +import React, {memo} from "react"; +import Card from "../../../card/Card"; +import "./DFlowViewportDefaultCard.style.scss"; +import CardSection from "../../../card/CardSection"; +import Flex from "../../../flex/Flex"; +import { + IconAlertTriangle, + IconArrowRightCircle, IconCopy, + IconDots, + IconExclamationCircle, + IconFileLambdaFilled, + IconLayoutNavbarCollapseFilled, + IconMessageExclamation, + IconTrash +} from "@tabler/icons-react"; +import Text from "../../../text/Text"; +import Button from "../../../button/Button"; +import {Menu, MenuContent, MenuItem, MenuLabel, MenuPortal, MenuTrigger} from "../../../menu/Menu"; +import Badge from "../../../badge/Badge"; +import {useService} from "../../../../utils/contextStore"; +import {DFlowFunctionReactiveService} from "../../function/DFlowFunction.service"; +import {useFunctionValidation} from "../../function/DFlowFunction.vaildation.hook"; +import {DFlowDataTypeReactiveService} from "../../data-type/DFlowDataType.service"; +import {InspectionSeverity} from "../../../../utils/inspection"; +import {DFlowReactiveService} from "../../DFlow.service"; +import {DFlowSuggestionMenu} from "../../suggestions/DFlowSuggestionMenu"; +import {useSuggestions} from "../../suggestions/DFlowSuggestion.hook"; +import {FileTabsService} from "../../../file-tabs/FileTabs.service"; +import {DFlowViewportDefaultTabContent} from "../file-tabs/DFlowViewportDefaultTabContent"; +import {DataTypeVariant, Maybe, Scalars} from "@code0-tech/sagittarius-graphql-types"; + +export interface DFlowViewportDefaultCardDataProps extends Omit, "scope"> { + instance: NodeFunctionView + flowId: Scalars["FlowID"]["output"] + isParameter: boolean + depth: number + scope: number[] + index: number +} + +// @ts-ignore +export type DFlowViewportDefaultCardProps = NodeProps> + +export const DFlowViewportDefaultCard: React.FC = memo((props) => { + const {data, id} = props; + const viewportWidth = useStore(s => s.width); + const viewportHeight = useStore(s => s.height); + const flowInstance = useReactFlow() + const flowStoreApi = useStoreApi() + const fileTabsService = useService(FileTabsService) + const flowService = useService(DFlowReactiveService) + const functionService = useService(DFlowFunctionReactiveService) + const dataTypeService = useService(DFlowDataTypeReactiveService) + const definition = functionService.getFunctionDefinition(data.instance.functionDefinition?.id!!) + const validation = useFunctionValidation(definition!!, data.instance.parameters!!.map(p => p.value!! instanceof NodeFunctionView ? p.value.json()!! : p.value!!), useService(DFlowDataTypeReactiveService)!!, props.data.flowId) + const edges = useStore(s => s.edges); + const width = props.width ?? 0 + const height = props.height ?? 0 + + // Helper, ob zu diesem Parameter eine Edge existiert: + function isParamConnected(paramId: Maybe): boolean { + return edges.some(e => + e.target === id && + e.targetHandle === `param-${paramId}` + ); + } + + const firstItem = useStore((s) => { + const children = s.nodes.filter((n) => n.parentId === props.parentId); + let start: any | undefined = undefined; + children.forEach((n) => { + const idx = (n.data as any)?.index ?? Infinity; + const startIdx = (start?.data as any)?.index ?? Infinity; + if (!start || idx < startIdx) { + start = n; + } + }); + return start; + }) + + return ( + v.type === InspectionSeverity.ERROR)?.length ?? 0) > 0 ? "error" : "secondary"} + onClick={() => { + flowInstance.setViewport({ + x: (viewportWidth / 2) + (props.positionAbsoluteX * -1) - (width / 2), + y: (viewportHeight / 2) + (props.positionAbsoluteY * -1) - (height / 2), + zoom: 1 + }, { + duration: 250, + }) + fileTabsService.add({ + id: id, + active: true, + closeable: true, + children: {data.instance.id}, + content: + }) + }} style={{position: "relative"}}> + + + + + + {data.instance.id} + + + { + setTimeout(() => { + flowStoreApi.setState({ + nodesDraggable: !event, + nodesConnectable: !event, + elementsSelectable: !event, + }); + }, 250) // Timeout to ensure the menu is fully opened before changing the state + }}> + + + + + + Actions + { + data.instance.deleteNextNode() + flowService.update() + }}> Delete node + Copy node + + + + + + + + + + + {(validation?.length ?? 0) > 0 ? ( +
+ + {(validation?.filter(v => v.type === InspectionSeverity.ERROR)?.length ?? 0) > 0 ? ( + + + + {validation?.filter(v => v.type === InspectionSeverity.ERROR)?.length} + + + ) : null} + + {(validation?.filter(v => v.type === InspectionSeverity.WARNING)?.length ?? 0) > 0 ? ( + + + + {validation?.filter(v => v.type === InspectionSeverity.WARNING)?.length} + + + ) : null} + + {(validation?.filter(v => v.type === InspectionSeverity.GRAMMAR)?.length ?? 0) > 0 ? ( + + + + {validation?.filter(v => v.type === InspectionSeverity.GRAMMAR)?.length} + + + ) : null} + +
+ ) : null} + + {data.instance.parameters?.some(param => { + const parameter = definition?.parameterDefinitions!!.find(p => p.id == param.id) + const isNodeDataType = dataTypeService.getDataType(parameter?.dataTypeIdentifier!!)?.variant === DataTypeVariant.Node; + return (param.value instanceof NodeFunctionView && !isNodeDataType) || (!param.value) + }) ? ( + + {/* Dynamische Parameter-Eingänge (rechts), nur wenn wirklich verbunden */} + {data.instance.parameters?.map((param: NodeParameterView, index: number) => { + + + const parameter = definition?.parameterDefinitions!!.find(p => p.id == param.id) + const isNodeDataType = dataTypeService.getDataType(parameter?.dataTypeIdentifier!!)?.variant === DataTypeVariant.Node; + const result = useSuggestions(parameter?.dataTypeIdentifier ?? undefined, [], "some_database_id", data.depth, data.scope, data.index) + + return (param.value instanceof NodeFunctionView && !isNodeDataType) || (!param.value) ? + + {param.id} + {!param.value ? ( + { + param.value = suggestion.value + flowService.update() + }} suggestions={result} triggerContent={}/> + ) : null} + : null + })} + + ) : null} + + {/* Ausgang */} + +
+ ); +}) \ No newline at end of file diff --git a/src/components/d-flow/viewport/cards/DFlowViewportGroupCard.tsx b/src/components/d-flow/viewport/cards/DFlowViewportGroupCard.tsx new file mode 100644 index 00000000..b36c2e71 --- /dev/null +++ b/src/components/d-flow/viewport/cards/DFlowViewportGroupCard.tsx @@ -0,0 +1,61 @@ +import React, {memo} from "react"; +import {Handle, NodeProps, Position, useStore} from "@xyflow/react"; +import {FLOW_EDGE_RAINBOW} from "../DFlowViewport.edges.hook"; +import Card from "../../../card/Card"; + +export interface DFlowViewportGroupCardProps extends NodeProps { +} + +export const DFlowViewportGroupCard: React.FC = memo((props) => { + const {data, id} = props + const depth = (data as any)?.depth ?? 0; + const color = FLOW_EDGE_RAINBOW[depth % FLOW_EDGE_RAINBOW.length]; + + // Align handles with the first node inside this group + const handleLeft = useStore((s) => { + const children = s.nodes.filter((n) => n.parentId === id); + let start: any | undefined = undefined; + children.forEach((n) => { + const idx = (n.data as any)?.index ?? Infinity; + const startIdx = (start?.data as any)?.index ?? Infinity; + if (!start || idx < startIdx) { + start = n; + } + }); + if (start) { + const width = start.measured.width ?? 0; + return start.position.x + width / 2; + } + return undefined; + }) + + return ( + + + + + ); +}); + +const withAlpha = (hex: string, alpha: number) => { + const h = hex.replace('#', ''); + const r = parseInt(h.length === 3 ? h[0] + h[0] : h.slice(0, 2), 16); + const g = parseInt(h.length === 3 ? h[1] + h[1] : h.slice(2, 4), 16); + const b = parseInt(h.length === 3 ? h[2] + h[2] : h.slice(4, 6), 16); + return `rgba(${r}, ${g}, ${b}, ${alpha})`; +}; diff --git a/src/components/d-flow/viewport/cards/DFlowViewportSuggestionCard.tsx b/src/components/d-flow/viewport/cards/DFlowViewportSuggestionCard.tsx new file mode 100644 index 00000000..e05d875a --- /dev/null +++ b/src/components/d-flow/viewport/cards/DFlowViewportSuggestionCard.tsx @@ -0,0 +1,47 @@ +import {Code0Component} from "../../../../utils/types"; +import {Handle, Node, NodeProps, Position} from "@xyflow/react"; +import React, {memo} from "react"; +import Button from "../../../button/Button"; +import {IconPlus} from "@tabler/icons-react"; +import {useSuggestions} from "../../suggestions/DFlowSuggestion.hook"; +import {NodeFunctionView} from "../../DFlow.view"; +import {useService} from "../../../../utils/contextStore"; +import {DFlowReactiveService} from "../../DFlow.service"; +import {DFlowSuggestionMenu} from "../../suggestions/DFlowSuggestionMenu"; +import {NodeFunction} from "@code0-tech/sagittarius-graphql-types"; + +export interface DFlowViewportSuggestionCardDataProps extends Code0Component { + flowId: string + parentFunction: NodeFunctionView +} + +// @ts-ignore +export type DFlowViewportSuggestionCardProps = NodeProps> + +export const DFlowViewportSuggestionCard: React.FC = memo((props) => { + + const result = useSuggestions(undefined, [], props.data.flowId, 0, [0], 0) + const flowService = useService(DFlowReactiveService) + const flow = flowService.getById(props.data.flowId) + + return { + const nodeFunction = new NodeFunctionView(suggestion.value as NodeFunction) + props.data.parentFunction.nextNodeId = nodeFunction.id!! + flow?.addNode(nodeFunction) + flowService.update() + }} suggestions={result} triggerContent={ + + }/> + +}) \ No newline at end of file diff --git a/src/components/d-flow/viewport/cards/DFlowViewportTriggerCard.tsx b/src/components/d-flow/viewport/cards/DFlowViewportTriggerCard.tsx new file mode 100644 index 00000000..5da0b4cf --- /dev/null +++ b/src/components/d-flow/viewport/cards/DFlowViewportTriggerCard.tsx @@ -0,0 +1,84 @@ +import React, {memo} from "react"; +import {Code0Component} from "../../../../utils/types"; +import {FlowView} from "../../DFlow.view"; +import {Handle, Node, NodeProps, Position, useReactFlow, useStore} from "@xyflow/react"; +import Text from "../../../text/Text"; +import {useService} from "../../../../utils/contextStore"; +import {FileTabsService} from "../../../file-tabs/FileTabs.service"; +import Card from "../../../card/Card"; +import CardSection from "../../../card/CardSection"; +import Flex from "../../../flex/Flex"; +import {IconBolt, IconLayoutNavbarCollapseFilled} from "@tabler/icons-react"; +import Button from "../../../button/Button"; +import Badge from "../../../badge/Badge"; +import {DFlowViewportTriggerTabContent} from "../file-tabs/DFlowViewportTriggerTabContent"; + +export interface DFlowViewportTriggerCardDataProps extends Omit, "scope"> { + instance: FlowView +} + +// @ts-ignore +export type DFlowViewportTriggerCardProps = NodeProps> + +export const DFlowViewportTriggerCard: React.FC = memo((props) => { + + const {data, id} = props + const fileTabsService = useService(FileTabsService) + const flowInstance = useReactFlow() + const width = props.width ?? 0 + const height = props.height ?? 0 + const viewportWidth = useStore(s => s.width) + const viewportHeight = useStore(s => s.height) + + return { + flowInstance.setViewport({ + x: (viewportWidth / 2) + (props.positionAbsoluteX * -1) - (width / 2), + y: (viewportHeight / 2) + (props.positionAbsoluteY * -1) - (height / 2), + zoom: 1 + }, { + duration: 250, + }) + fileTabsService.add({ + id: id, + active: true, + closeable: true, + children: {data.instance.id}, + content: + }) + }}> + + + + + Trigger + + + + + + + + {data.instance.id} + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy. + + + + {/* Ausgang */} + + + + +}) \ No newline at end of file diff --git a/src/components/d-flow/viewport/file-tabs/DFlowViewportDefaultTabContent.tsx b/src/components/d-flow/viewport/file-tabs/DFlowViewportDefaultTabContent.tsx new file mode 100644 index 00000000..c92d6201 --- /dev/null +++ b/src/components/d-flow/viewport/file-tabs/DFlowViewportDefaultTabContent.tsx @@ -0,0 +1,126 @@ +import React from "react"; +import {NodeFunctionView} from "../../DFlow.view"; +import TextInput from "../../../form/TextInput"; +import Flex from "../../../flex/Flex"; +import {useService} from "../../../../utils/contextStore"; +import {DFlowFunctionReactiveService} from "../../function/DFlowFunction.service"; +import {useSuggestions} from "../../suggestions/DFlowSuggestion.hook"; +import {DFlowSuggestionMenuFooter} from "../../suggestions/DFlowSuggestionMenuFooter"; +import {toInputSuggestions} from "../../suggestions/DFlowSuggestionMenu.util"; +import {DFlowReactiveService} from "../../DFlow.service"; +import {DFlowSuggestion} from "../../suggestions/DFlowSuggestion.view"; +import {ParameterDefinitionView} from "../../function/DFlowFunction.view"; +import Badge from "../../../badge/Badge"; +import {DFlowDataTypeReactiveService} from "../../data-type/DFlowDataType.service"; +import {useReturnType} from "../../function/DFlowFunction.return.hook"; +import {resolveGenericKeys} from "../../../../utils/generics"; +import {NodeFunction, NodeParameterValue, ReferenceValue} from "@code0-tech/sagittarius-graphql-types"; + +export interface DFlowViewportFileTabsContentProps { + functionInstance: NodeFunctionView + depthLevel?: number + scopeLevel?: number[] + nodeLevel?: number +} + +export const DFlowViewportDefaultTabContent: React.FC = (props) => { + + const {functionInstance, depthLevel, scopeLevel, nodeLevel} = props + const functionService = useService(DFlowFunctionReactiveService) + const dataTypeService = useService(DFlowDataTypeReactiveService) + const flowService = useService(DFlowReactiveService) + const definition = functionService.getFunctionDefinition(functionInstance.functionDefinition?.id!!) + const paramDefinitions = React.useMemo(() => { + const map: Record = {} + definition?.parameterDefinitions?.forEach(pd => { + map[pd.id!!] = pd + }) + return map + }, [definition?.parameterDefinitions]) + + const sortedParameters = React.useMemo(() => { + return [...(functionInstance.parameters || [])].sort((a, b) => a.id!!.localeCompare(b.id!!)) + }, [functionInstance.parameters]) + + const suggestionsById: Record = {} + sortedParameters.forEach(parameter => { + const parameterDefinition = paramDefinitions[parameter.id!!] + suggestionsById[parameter.id!!] = useSuggestions(parameterDefinition?.dataTypeIdentifier!!, [], "some_database_id", depthLevel, scopeLevel, nodeLevel) + }) + + const returnType = useReturnType(definition!!, sortedParameters.map(p => p.value as NodeParameterValue), dataTypeService) + const genericTypeMap = resolveGenericKeys(definition!!, sortedParameters.map(p => p.value as NodeParameterValue), dataTypeService) + return + {sortedParameters.map(parameter => { + + const submitValue = (value: NodeParameterValue) => { + parameter.value = value + flowService.update() + } + + const submitValueEvent = (event: any) => { + try { + const value = JSON.parse(event.target.value) + submitValue(value) + } catch (e) { + // @ts-ignore + submitValue(event.target.value == "" ? undefined : event.target.value) + } + } + + const parameterDefinition = paramDefinitions[parameter.id!!] + const result = suggestionsById[parameter.id!!] + const title = parameterDefinition?.names ? parameterDefinition?.names?.nodes!![0]?.content : parameterDefinition?.id + const description = parameterDefinition?.descriptions ? parameterDefinition?.descriptions?.nodes!![0]?.content : JSON.stringify(parameterDefinition?.dataTypeIdentifier) + const defaultValue = parameter.value instanceof NodeFunctionView ? JSON.stringify(parameter.value) : typeof parameter.value == "object" || typeof parameter.value == "boolean" ? JSON.stringify(parameter.value) : parameter.value + + + return
+ {JSON.stringify(dataTypeService.getTypeFromValue(parameter.value as NodeParameterValue))} + { + try { + if (!value) return value + if ((value as NodeParameterValue).__typename === "NodeFunction") { + return {(JSON.parse(value) as NodeFunction).id} + } + if ((value as NodeParameterValue).__typename === "ReferenceValue") { + const refObject = JSON.parse(value) as ReferenceValue + return {refObject.depth}-{refObject.scope}-{refObject.node}-{JSON.stringify(refObject.dataTypeIdentifier)} + } + } catch (e) { + } + return value + }} + disableOnValue={value => { + if (!value) return false + try { + return (value as NodeParameterValue).__typename === "NodeFunction" || (value as NodeParameterValue).__typename === "ReferenceValue" + } catch (e) { + } + return false + }} + defaultValue={defaultValue} + onSuggestionSelect={(suggestion) => { + submitValue(suggestion.value) + }} + onBlur={submitValueEvent} + onClear={submitValueEvent} + suggestionsFooter={} + suggestions={toInputSuggestions(result)} + + /> +
+ })} + {JSON.stringify(returnType)} +
+
+ {JSON.stringify(genericTypeMap)} +
+ +} \ No newline at end of file diff --git a/src/components/d-flow/viewport/file-tabs/DFlowViewportTabs.tsx b/src/components/d-flow/viewport/file-tabs/DFlowViewportTabs.tsx new file mode 100644 index 00000000..ad2b81b4 --- /dev/null +++ b/src/components/d-flow/viewport/file-tabs/DFlowViewportTabs.tsx @@ -0,0 +1,110 @@ +import {useService, useStore} from "../../../../utils/contextStore"; +import {FileTabsService} from "../../../file-tabs/FileTabs.service"; +import {FileTabs, FileTabsContent, FileTabsList, FileTabsTrigger} from "../../../file-tabs/FileTabs"; +import React from "react"; +import {Menu, MenuContent, MenuItem, MenuPortal, MenuSeparator, MenuTrigger} from "../../../menu/Menu"; +import Button from "../../../button/Button"; +import {IconChevronDown, IconDotsVertical} from "@tabler/icons-react"; +import {FileTabsView} from "../../../file-tabs/FileTabs.view"; + +export const DFlowViewportTabs = () => { + + const fileTabsService = useService(FileTabsService) + const fileTabsStore = useStore(FileTabsService) + const id = React.useId() + + const activeTabId = React.useMemo(() => { + return fileTabsStore.find((t: any) => (t as any).active)?.id ?? fileTabsService.getActiveTab()?.id; + }, [fileTabsStore, fileTabsService]); + + React.useEffect(() => { + setTimeout(() => { + const parent = document.querySelector("[data-id=" + '"' + id + '"' + "]") as HTMLDivElement + const tabList = parent.querySelector(".file-tabs__list-content") as HTMLDivElement + const trigger = tabList.querySelector("[data-value=" + '"' + fileTabsService.getActiveTab()?.id + '"' + "]") as HTMLDivElement + + if (tabList && trigger) { + const offset = (trigger.offsetLeft + (trigger.offsetWidth / 2)) - (tabList.offsetWidth / 2) + tabList.scrollLeft = 0 //reset to 0 + tabList.scrollBy({ + left: offset, + behavior: 'smooth' + }); + } + }, 0) + }, [activeTabId, id]) + + return ( + { + fileTabsService.activateTab(value); // mutieren reicht; kein .update() nötig, wenn setState benutzt wird + }} + > + + + + + + + + {fileTabsStore.map((tab: FileTabsView) => ( + { + fileTabsService.activateTab(tab.id!) + }}> + {tab.children} + + ))} + + + + + + + + + + + fileTabsService.clear()}>Close all tabs + fileTabsService.clearWithoutActive()}>Close other + tabs + + fileTabsService.clearLeft()}>Close all tabs to + left + fileTabsService.clearRight()}>Close all tabs to + right + + + + + } + > + {fileTabsStore.map((tab: FileTabsView, index: number) => ( + fileTabsService.delete(index)} + > + {tab.children} + + ))} + + + {fileTabsStore.map((tab: FileTabsView) => ( + + {tab.content} + + ))} + + ); + +} \ No newline at end of file diff --git a/src/components/d-flow/viewport/file-tabs/DFlowViewportTriggerTabContent.tsx b/src/components/d-flow/viewport/file-tabs/DFlowViewportTriggerTabContent.tsx new file mode 100644 index 00000000..40edd9da --- /dev/null +++ b/src/components/d-flow/viewport/file-tabs/DFlowViewportTriggerTabContent.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import {FlowView, NodeFunctionView} from "../../DFlow.view"; +import {useService} from "../../../../utils/contextStore"; +import {DFlowReactiveService} from "../../DFlow.service"; +import TextInput from "../../../form/TextInput"; +import Flex from "../../../flex/Flex"; +import {DFlowTypeReactiveService} from "../../type/DFlowType.service"; +import {DFlowSuggestion} from "../../suggestions/DFlowSuggestion.view"; +import {useSuggestions} from "../../suggestions/DFlowSuggestion.hook"; +import {DFlowSuggestionMenuFooter} from "../../suggestions/DFlowSuggestionMenuFooter"; +import {toInputSuggestions} from "../../suggestions/DFlowSuggestionMenu.util"; +import {FlowTypeSetting} from "@code0-tech/sagittarius-graphql-types"; + +export interface DFlowViewportTriggerTabContentProps { + instance: FlowView +} + +export const DFlowViewportTriggerTabContent: React.FC = (props) => { + + const {instance} = props + const flowService = useService(DFlowReactiveService) + const flowTypeService = useService(DFlowTypeReactiveService) + const definition = flowTypeService.getById(instance.type?.id!!) + + const flowTypeSettingsDefinition = React.useMemo(() => { + const map: Record = {} + definition?.flowTypeSettings?.forEach(def => { + map[def.identifier!!] = def + }) + return map + }, [definition?.flowTypeSettings]) + + const suggestionsById: Record = {} + instance?.settings?.forEach(setting => { + const settingDefinition = flowTypeSettingsDefinition[setting.flowSettingId!!] + suggestionsById[setting.flowSettingId!!] = useSuggestions({dataType: settingDefinition.dataType}, [], "some_database_id", 0, [0], 0) + }) + + return + {instance.settings?.map(setting => { + + const settingsDefinition = flowTypeSettingsDefinition[setting.flowSettingId!!] + const title = settingsDefinition?.names ? settingsDefinition.names?.nodes!![0]?.content : setting.flowSettingId + const description = settingsDefinition?.descriptions ? settingsDefinition?.descriptions?.nodes!![0]?.content : JSON.stringify(settingsDefinition!!.dataType) + const result = suggestionsById[setting.flowSettingId!!] + const defaultValue = typeof setting.value == "object" ? JSON.stringify(setting.value) : setting.value + + return
+ } + suggestions={toInputSuggestions(result)}/> +
+ })} +
+} \ No newline at end of file diff --git a/src/components/d-namespace/DNamespace.service.ts b/src/components/d-namespace/DNamespace.service.ts new file mode 100644 index 00000000..5ec09ddb --- /dev/null +++ b/src/components/d-namespace/DNamespace.service.ts @@ -0,0 +1,20 @@ +import {Maybe, Scalars} from "@code0-tech/sagittarius-graphql-types"; +import {DNamespaceView} from "./DNamespace.view"; +import {ReactiveArrayService, ReactiveArrayStore} from "../../utils/reactiveArrayService"; + +export abstract class DNamespaceService extends ReactiveArrayService { + + constructor(payload: ReactiveArrayStore) { + super(payload); + } + + abstract findById(id: Maybe): DNamespaceView | undefined +} + +export abstract class DNamespaceReactiveService extends DNamespaceService { + + findById(id: Maybe): DNamespaceView | undefined { + return this.values().find(namespace => namespace.id === id); + } + +} \ No newline at end of file diff --git a/src/components/d-namespace/DNamespace.view.ts b/src/components/d-namespace/DNamespace.view.ts new file mode 100644 index 00000000..9dec2df4 --- /dev/null +++ b/src/components/d-namespace/DNamespace.view.ts @@ -0,0 +1,76 @@ +import { + Maybe, Namespace, + NamespaceLicenseConnection, + NamespaceMemberConnection, NamespaceParent, NamespaceProjectConnection, NamespaceRoleConnection, RuntimeConnection, + Scalars +} from "@code0-tech/sagittarius-graphql-types"; + +export class DNamespaceView { + /** Time when this Namespace was created */ + private readonly _createdAt?: Maybe; + /** Global ID of this Namespace */ + private readonly _id?: Maybe; + /** Members of the namespace */ + private readonly _members?: Maybe; + /** Licenses of the namespace */ + private readonly _namespaceLicenses?: Maybe; + /** Parent of this namespace */ + private readonly _parent?: Maybe; + /** Projects of the namespace */ + private readonly _projects?: Maybe; + /** Roles of the namespace */ + private readonly _roles?: Maybe; + /** Runtime of the namespace */ + private readonly _runtimes?: Maybe; + /** Time when this Namespace was last updated */ + private readonly _updatedAt?: Maybe; + + constructor(payload: Namespace) { + this._createdAt = payload.createdAt; + this._id = payload.id; + this._members = payload.members; + this._namespaceLicenses = payload.namespaceLicenses; + this._parent = payload.parent; + this._projects = payload.projects; + this._roles = payload.roles; + this._runtimes = payload.runtimes; + this._updatedAt = payload.updatedAt; + } + + + get createdAt(): Maybe | undefined { + return this._createdAt; + } + + get id(): Maybe | undefined { + return this._id; + } + + get members(): Maybe | undefined { + return this._members; + } + + get namespaceLicenses(): Maybe | undefined { + return this._namespaceLicenses; + } + + get parent(): Maybe | undefined { + return this._parent; + } + + get projects(): Maybe | undefined { + return this._projects; + } + + get roles(): Maybe | undefined { + return this._roles; + } + + get runtimes(): Maybe | undefined { + return this._runtimes; + } + + get updatedAt(): Maybe | undefined { + return this._updatedAt; + } +} \ No newline at end of file diff --git a/src/components/d-namespace/license/DNamespaceLicense.service.ts b/src/components/d-namespace/license/DNamespaceLicense.service.ts new file mode 100644 index 00000000..28083310 --- /dev/null +++ b/src/components/d-namespace/license/DNamespaceLicense.service.ts @@ -0,0 +1,32 @@ +import { + NamespacesLicensesCreateInput, + NamespacesLicensesDeleteInput, + Scalars +} from "@code0-tech/sagittarius-graphql-types"; +import {DNamespaceLicenseView} from "./DNamespaceLicense.view"; +import {ReactiveArrayService, ReactiveArrayStore} from "../../../utils/reactiveArrayService"; + +export abstract class DNamespaceLicenseService extends ReactiveArrayService { + + constructor(payload: ReactiveArrayStore) { + super(payload); + } + + abstract licenseCreate(payload: NamespacesLicensesCreateInput): DNamespaceLicenseView | undefined + + abstract licenseDelete(payload: NamespacesLicensesDeleteInput): void + + abstract findById(id: Scalars['NamespaceLicenseID']['output']): DNamespaceLicenseView | undefined +} + +export abstract class DNamespaceLicenseReactiveService extends DNamespaceLicenseService { + + findById(id: Scalars["NamespaceLicenseID"]["output"]): DNamespaceLicenseView | undefined { + return this.values().find(license => license.id === id); + } + + abstract override licenseCreate(payload: NamespacesLicensesCreateInput): DNamespaceLicenseView | undefined + + abstract override licenseDelete(payload: NamespacesLicensesDeleteInput): void + +} \ No newline at end of file diff --git a/src/components/d-namespace/license/DNamespaceLicense.view.ts b/src/components/d-namespace/license/DNamespaceLicense.view.ts new file mode 100644 index 00000000..a87c167c --- /dev/null +++ b/src/components/d-namespace/license/DNamespaceLicense.view.ts @@ -0,0 +1,56 @@ +import {Maybe, Namespace, NamespaceLicense, Scalars} from "@code0-tech/sagittarius-graphql-types"; + +export class DNamespaceLicenseView { + /** Time when this NamespaceLicense was created */ + private readonly _createdAt?: Maybe; + /** The end date of the license */ + private readonly _endDate?: Maybe; + /** Global ID of this NamespaceLicense */ + private readonly _id?: Maybe; + /** The licensee information */ + private readonly _licensee?: Maybe; + /** The namespace the license belongs to */ + private readonly _namespace?: Maybe; + /** The start date of the license */ + private readonly _startDate?: Maybe; + /** Time when this NamespaceLicense was last updated */ + private readonly _updatedAt?: Maybe; + + constructor(payload: NamespaceLicense) { + this._createdAt = payload.createdAt; + this._endDate = payload.endDate; + this._id = payload.id; + this._licensee = payload.licensee; + this._namespace = payload.namespace; + this._startDate = payload.startDate; + this._updatedAt = payload.updatedAt; + } + + get createdAt(): Maybe | undefined { + return this._createdAt; + } + + get endDate(): Maybe | undefined { + return this._endDate; + } + + get id(): Maybe | undefined { + return this._id; + } + + get licensee(): Maybe | undefined { + return this._licensee; + } + + get namespace(): Maybe | undefined { + return this._namespace; + } + + get startDate(): Maybe | undefined { + return this._startDate; + } + + get updatedAt(): Maybe | undefined { + return this._updatedAt; + } +} \ No newline at end of file diff --git a/src/components/d-namespace/member/DNamespaceMember.service.ts b/src/components/d-namespace/member/DNamespaceMember.service.ts new file mode 100644 index 00000000..1fac9352 --- /dev/null +++ b/src/components/d-namespace/member/DNamespaceMember.service.ts @@ -0,0 +1,39 @@ +import { + Maybe, + NamespacesMembersAssignRolesInput, + NamespacesMembersDeleteInput, + NamespacesMembersInviteInput, + Scalars +} from "@code0-tech/sagittarius-graphql-types"; +import {DNamespaceMemberView} from "./DNamespaceMember.view"; +import {ReactiveArrayService, ReactiveArrayStore} from "../../../utils/reactiveArrayService"; + +export abstract class DNamespaceMemberService extends ReactiveArrayService { + + constructor(payload: ReactiveArrayStore) { + super(payload); + } + + abstract membersInvite(payload: NamespacesMembersInviteInput): DNamespaceMemberView | undefined + + abstract membersDelete(payload: NamespacesMembersDeleteInput): void + + abstract membersAssignRoles(payload: NamespacesMembersAssignRolesInput): DNamespaceMemberView | undefined + + abstract findById(id: Maybe): DNamespaceMemberView | undefined +} + +export abstract class DNamespaceMemberReactiveService extends DNamespaceMemberService { + + findById(id: Maybe): DNamespaceMemberView | undefined { + return this.values().find(member => member.id === id); + } + + abstract override membersAssignRoles(payload: NamespacesMembersAssignRolesInput): DNamespaceMemberView | undefined + + abstract override membersDelete(payload: NamespacesMembersDeleteInput): void + + abstract override membersInvite(payload: NamespacesMembersInviteInput): DNamespaceMemberView | undefined + + +} \ No newline at end of file diff --git a/src/components/d-namespace/member/DNamespaceMember.view.ts b/src/components/d-namespace/member/DNamespaceMember.view.ts new file mode 100644 index 00000000..88d1b672 --- /dev/null +++ b/src/components/d-namespace/member/DNamespaceMember.view.ts @@ -0,0 +1,42 @@ +import {Maybe, Namespace, NamespaceMember, Scalars, User} from "@code0-tech/sagittarius-graphql-types"; + +export class DNamespaceMemberView { + /** Time when this NamespaceMember was created */ + private readonly _createdAt?: Maybe; + /** Global ID of this NamespaceMember */ + private readonly _id?: Maybe; + /** Namespace this member belongs to */ + private readonly _namespace?: Maybe; + /** Time when this NamespaceMember was last updated */ + private readonly _updatedAt?: Maybe; + /** User this member belongs to */ + private readonly _user?: Maybe; + + constructor(payload: NamespaceMember) { + this._createdAt = payload.createdAt; + this._id = payload.id; + this._namespace = payload.namespace; + this._updatedAt = payload.updatedAt; + this._user = payload.user; + } + + get createdAt(): Maybe | undefined { + return this._createdAt; + } + + get id(): Maybe | undefined { + return this._id; + } + + get namespace(): Maybe | undefined { + return this._namespace; + } + + get updatedAt(): Maybe | undefined { + return this._updatedAt; + } + + get user(): Maybe | undefined { + return this._user; + } +} \ No newline at end of file diff --git a/src/components/d-namespace/project/DNamespaceProject.service.ts b/src/components/d-namespace/project/DNamespaceProject.service.ts new file mode 100644 index 00000000..deb7c379 --- /dev/null +++ b/src/components/d-namespace/project/DNamespaceProject.service.ts @@ -0,0 +1,37 @@ +import { + NamespacesProjectsAssignRuntimesInput, + NamespacesProjectsCreateInput, + NamespacesProjectsDeleteInput, + Scalars +} from "@code0-tech/sagittarius-graphql-types"; +import {DNamespaceProjectView} from "./DNamespaceProject.view"; +import {ReactiveArrayService, ReactiveArrayStore} from "../../../utils/reactiveArrayService"; + +export abstract class DNamespaceProjectService extends ReactiveArrayService { + + constructor(payload: ReactiveArrayStore) { + super(payload); + } + + abstract projectAssignRuntimes(payload: NamespacesProjectsAssignRuntimesInput): DNamespaceProjectView | undefined + + abstract projectsCreate(payload: NamespacesProjectsCreateInput): DNamespaceProjectView | undefined + + abstract projectsDelete(payload: NamespacesProjectsDeleteInput): void + + abstract findById(id: Scalars['NamespaceProjectID']['output']): DNamespaceProjectView | undefined +} + +export abstract class DNamespaceProjectReactiveService extends DNamespaceProjectService { + + findById(id: Scalars["NamespaceProjectID"]["output"]): DNamespaceProjectView | undefined { + return this.values().find(project => project.id === id) + } + + abstract projectAssignRuntimes(payload: NamespacesProjectsAssignRuntimesInput): DNamespaceProjectView | undefined + + abstract projectsCreate(payload: NamespacesProjectsCreateInput): DNamespaceProjectView | undefined + + abstract projectsDelete(payload: NamespacesProjectsDeleteInput): void + +} \ No newline at end of file diff --git a/src/components/d-namespace/project/DNamespaceProject.view.ts b/src/components/d-namespace/project/DNamespaceProject.view.ts new file mode 100644 index 00000000..da0a65f1 --- /dev/null +++ b/src/components/d-namespace/project/DNamespaceProject.view.ts @@ -0,0 +1,86 @@ +import { + Flow, + FlowConnection, + Maybe, + Namespace, NamespaceProject, + Runtime, + RuntimeConnection, + Scalars +} from "@code0-tech/sagittarius-graphql-types"; + +export class DNamespaceProjectView { + /** Time when this NamespaceProject was created */ + private readonly _createdAt?: Maybe; + /** Description of the project */ + private readonly _description?: Maybe; + /** Fetches an flow given by its ID */ + private readonly _flow?: Maybe; + /** Fetches all flows in this project */ + private readonly _flows?: Maybe; + /** Global ID of this NamespaceProject */ + private readonly _id?: Maybe; + /** Name of the project */ + private readonly _name?: Maybe; + /** The namespace where this project belongs to */ + private readonly _namespace?: Maybe; + /** The primary runtime for the project */ + private readonly _primaryRuntime?: Maybe; + /** Runtimes assigned to this project */ + private readonly _runtimes?: Maybe; + /** Time when this NamespaceProject was last updated */ + private readonly _updatedAt?: Maybe; + + constructor(payload: NamespaceProject) { + this._createdAt = payload.createdAt; + this._description = payload.description; + this._flow = payload.flow; + this._flows = payload.flows; + this._id = payload.id; + this._name = payload.name; + this._namespace = payload.namespace; + this._primaryRuntime = payload.primaryRuntime; + this._runtimes = payload.runtimes; + this._updatedAt = payload.updatedAt; + } + + + get createdAt(): Maybe | undefined { + return this._createdAt; + } + + get description(): Maybe | undefined { + return this._description; + } + + get flow(): Maybe | undefined { + return this._flow; + } + + get flows(): Maybe | undefined { + return this._flows; + } + + get id(): Maybe | undefined { + return this._id; + } + + get name(): Maybe | undefined { + return this._name; + } + + get namespace(): Maybe | undefined { + return this._namespace; + } + + get primaryRuntime(): Maybe | undefined { + return this._primaryRuntime; + } + + get runtimes(): Maybe | undefined { + return this._runtimes; + } + + get updatedAt(): Maybe | undefined { + return this._updatedAt; + } +} \ No newline at end of file diff --git a/src/components/d-namespace/role/DNamespaceRole.service.ts b/src/components/d-namespace/role/DNamespaceRole.service.ts new file mode 100644 index 00000000..a8d050f6 --- /dev/null +++ b/src/components/d-namespace/role/DNamespaceRole.service.ts @@ -0,0 +1,48 @@ +import {ReactiveArrayService, ReactiveArrayStore} from "../../../utils/reactiveArrayService"; +import {DNamespaceRoleView} from "./DNamespaceRole.view"; +import { + NamespacesRolesAssignAbilitiesInput, + NamespacesRolesAssignProjectsInput, + NamespacesRolesCreateInput, + NamespacesRolesDeleteInput, + NamespacesRolesUpdateInput, + Scalars +} from "@code0-tech/sagittarius-graphql-types"; + +export abstract class DNamespaceRoleService extends ReactiveArrayService { + + constructor(payload: ReactiveArrayStore) { + super(payload); + + } + + abstract findById(id: Scalars['NamespaceRoleID']['output']): DNamespaceRoleView | undefined + + abstract roleAssignAbilities(payload: NamespacesRolesAssignAbilitiesInput): DNamespaceRoleView | undefined + + abstract roleAssignProjects(payload: NamespacesRolesAssignProjectsInput): DNamespaceRoleView | undefined + + abstract roleCreate(payload: NamespacesRolesCreateInput): DNamespaceRoleView | undefined + + abstract roleDelete(payload: NamespacesRolesDeleteInput): DNamespaceRoleView | undefined + + abstract roleUpdate(payload: NamespacesRolesUpdateInput): DNamespaceRoleView | undefined +} + +export abstract class DNamespaceRoleReactiveService extends DNamespaceRoleService { + + findById(id: Scalars["NamespaceRoleID"]["output"]): DNamespaceRoleView | undefined { + return this.values().find(role => role.id === id); + } + + abstract override roleAssignAbilities(payload: NamespacesRolesAssignAbilitiesInput): DNamespaceRoleView | undefined + + abstract override roleAssignProjects(payload: NamespacesRolesAssignProjectsInput): DNamespaceRoleView | undefined + + abstract override roleCreate(payload: NamespacesRolesCreateInput): DNamespaceRoleView | undefined + + abstract override roleDelete(payload: NamespacesRolesDeleteInput): DNamespaceRoleView | undefined + + abstract override roleUpdate(payload: NamespacesRolesUpdateInput): DNamespaceRoleView | undefined + +} \ No newline at end of file diff --git a/src/components/d-namespace/role/DNamespaceRole.view.ts b/src/components/d-namespace/role/DNamespaceRole.view.ts new file mode 100644 index 00000000..7934cdf4 --- /dev/null +++ b/src/components/d-namespace/role/DNamespaceRole.view.ts @@ -0,0 +1,63 @@ +import { + Maybe, Namespace, + NamespaceProjectConnection, + NamespaceRole, + NamespaceRoleAbility, Scalars +} from "@code0-tech/sagittarius-graphql-types"; + +export class DNamespaceRoleView { + + /** The abilities the role is granted */ + private readonly _abilities?: Maybe>; + /** The projects this role is assigned to */ + private readonly _assignedProjects?: Maybe; + /** Time when this NamespaceRole was created */ + private readonly _createdAt?: Maybe; + /** Global ID of this NamespaceRole */ + private readonly _id?: Maybe; + /** The name of this role */ + private readonly _name?: Maybe; + /** The namespace where this role belongs to */ + private readonly _namespace?: Maybe; + /** Time when this NamespaceRole was last updated */ + private readonly _updatedAt?: Maybe; + + constructor(payload: NamespaceRole) { + this._abilities = payload.abilities; + this._assignedProjects = payload.assignedProjects; + this._createdAt = payload.createdAt; + this._id = payload.id; + this._name = payload.name; + this._namespace = payload.namespace; + this._updatedAt = payload.updatedAt; + } + + + get abilities(): Maybe> | undefined { + return this._abilities; + } + + get assignedProjects(): Maybe | undefined { + return this._assignedProjects; + } + + get createdAt(): Maybe | undefined { + return this._createdAt; + } + + get id(): Maybe | undefined { + return this._id; + } + + get name(): Maybe | undefined { + return this._name; + } + + get namespace(): Maybe | undefined { + return this._namespace; + } + + get updatedAt(): Maybe | undefined { + return this._updatedAt; + } +} \ No newline at end of file diff --git a/src/components/d-organization/DOrganizationService.ts b/src/components/d-organization/DOrganizationService.ts new file mode 100644 index 00000000..1d51b776 --- /dev/null +++ b/src/components/d-organization/DOrganizationService.ts @@ -0,0 +1,38 @@ +import {ReactiveArrayService, ReactiveArrayStore} from "../../utils/reactiveArrayService"; +import {DOrganizationView} from "./DOrganizationView"; +import { + OrganizationsCreateInput, + OrganizationsDeleteInput, + OrganizationsUpdateInput, + Scalars +} from "@code0-tech/sagittarius-graphql-types"; + +export abstract class DOrganizationService extends ReactiveArrayService { + + constructor(payload: ReactiveArrayStore) { + super(payload); + } + + abstract organizationCreate(payload: OrganizationsCreateInput): DOrganizationView | undefined + + abstract organizationDelete(payload: OrganizationsDeleteInput): void + + abstract organizationUpdate(payload: OrganizationsUpdateInput): DOrganizationView | undefined + + abstract findById(id: Scalars['OrganizationID']['input']): DOrganizationView | undefined +} + +export abstract class DOrganizationReactiveService extends DOrganizationService { + + + findById(id: Scalars["OrganizationID"]["input"]): DOrganizationView | undefined { + return this.values().find(organization => organization.id === id) + } + + abstract override organizationCreate(payload: OrganizationsCreateInput): DOrganizationView | undefined + + abstract override organizationDelete(payload: OrganizationsDeleteInput): void + + abstract override organizationUpdate(payload: OrganizationsUpdateInput): DOrganizationView | undefined + +} \ No newline at end of file diff --git a/src/components/d-organization/DOrganizationView.ts b/src/components/d-organization/DOrganizationView.ts new file mode 100644 index 00000000..bbf92460 --- /dev/null +++ b/src/components/d-organization/DOrganizationView.ts @@ -0,0 +1,46 @@ +import {Maybe, Namespace, Organization, Scalars} from "@code0-tech/sagittarius-graphql-types"; + +export class DOrganizationView { + + private readonly _createdAt?: Maybe; + /** Global ID of this Organization */ + private readonly _id?: Maybe; + /** Name of the organization */ + private _name?: Maybe; + /** Namespace of this organization */ + private readonly _namespace?: Maybe; + /** Time when this Organization was last updated */ + private readonly _updatedAt?: Maybe; + + constructor(payload: Organization) { + this._createdAt = payload.createdAt; + this._id = payload.id; + this._name = payload.name; + this._namespace = payload.namespace; + this._updatedAt = payload.updatedAt; + } + + get createdAt(): Maybe | undefined { + return this._createdAt; + } + + get id(): Maybe { + return this._id; + } + + get name(): Maybe | undefined { + return this._name; + } + + get namespace(): Maybe | undefined { + return this._namespace; + } + + get updatedAt(): Maybe | undefined { + return this._updatedAt; + } + + set name(value: Maybe) { + this._name = value; + } +} \ No newline at end of file diff --git a/src/components/d-resizable/DResizable.stories.tsx b/src/components/d-resizable/DResizable.stories.tsx index 46c1057b..776859e0 100644 --- a/src/components/d-resizable/DResizable.stories.tsx +++ b/src/components/d-resizable/DResizable.stories.tsx @@ -4,18 +4,25 @@ import React from "react"; import DFullScreen from "../d-fullscreen/DFullScreen"; import DFolder from "../d-folder/DFolder"; import Button from "../button/Button"; -import { - IconDatabase, - IconFileFilled, - IconHierarchy3, - IconSettings, - IconTicket -} from "@tabler/icons-react"; +import {IconDatabase, IconFileFilled, IconHierarchy3, IconSettings, IconTicket} from "@tabler/icons-react"; import Flex from "../flex/Flex"; -import {ExampleFileTabs} from "../file-tabs/FileTabs.stories"; import {ScrollArea, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport} from "../scroll-area/ScrollArea"; import {Tooltip, TooltipContent, TooltipPortal, TooltipTrigger} from "../tooltip/Tooltip"; -import {ExampleFlowLine} from "../flow-line/FlowLines.stories"; +import {FunctionDefinitionView} from "../d-flow/function/DFlowFunction.view"; +import {useReactiveArrayService} from "../../utils/reactiveArrayService"; +import {FileTabsView} from "../file-tabs/FileTabs.view"; +import {FileTabsService} from "../file-tabs/FileTabs.service"; +import {DataTypeView} from "../d-flow/data-type/DFlowDataType.view"; +import {DFlowDataTypeReactiveService} from "../d-flow/data-type/DFlowDataType.service"; +import {DFlowFunctionReactiveService} from "../d-flow/function/DFlowFunction.service"; +import {DFlowReactiveService} from "../d-flow/DFlow.service"; +import {DFlowSuggestion} from "../d-flow/suggestions/DFlowSuggestion.view"; +import {DFlowReactiveSuggestionService} from "../d-flow/suggestions/DFlowSuggestion.service"; +import {FlowView} from "../d-flow/DFlow.view"; +import {ContextStoreProvider} from "../../utils/contextStore"; +import {DFlowViewportTabs} from "../d-flow/viewport/file-tabs/DFlowViewportTabs"; +import {DFlowTypeReactiveService} from "../d-flow/type/DFlowType.service"; +import {FlowTypeView} from "../d-flow/type/DFlowType.view"; const meta: Meta = { title: "Dashboard Resizable", @@ -32,13 +39,21 @@ export default meta export const Dashboard = () => { + const [fileTabsStore, fileTabsService] = useReactiveArrayService(FileTabsService) + const [dataTypeStore, dataTypeService] = useReactiveArrayService(DFlowDataTypeReactiveService) + const [functionStore, functionService] = useReactiveArrayService(DFlowFunctionReactiveService); + const [flowStore, flowService] = useReactiveArrayService(DFlowReactiveService); + const [suggestionStore, suggestionService] = useReactiveArrayService(DFlowReactiveSuggestionService); + const [flowTypeStore, flowTypeService] = useReactiveArrayService(DFlowTypeReactiveService); + return - @@ -50,7 +65,8 @@ export const Dashboard = () => { - @@ -76,7 +92,8 @@ export const Dashboard = () => {
- @@ -91,7 +108,7 @@ export const Dashboard = () => { {[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1].map((_, index) => { - return + return } name={"Google Cloud Flows"}/> @@ -114,13 +131,15 @@ export const Dashboard = () => { }}>
- - - - - - - + + + + + + + +
diff --git a/src/components/d-runtime/DRuntime.service.ts b/src/components/d-runtime/DRuntime.service.ts new file mode 100644 index 00000000..cbec46d2 --- /dev/null +++ b/src/components/d-runtime/DRuntime.service.ts @@ -0,0 +1,42 @@ +import {ReactiveArrayService, ReactiveArrayStore} from "../../utils/reactiveArrayService"; +import {DRuntimeView} from "./DRuntime.view"; +import { + RuntimesCreateInput, + RuntimesDeleteInput, + RuntimesRotateTokenInput, + RuntimesUpdateInput, + Scalars +} from "@code0-tech/sagittarius-graphql-types"; + +export abstract class DRuntimeService extends ReactiveArrayService { + constructor(payload: ReactiveArrayStore) { + super(payload); + } + + abstract findById(id: Scalars['RuntimeID']['output']): DRuntimeView | undefined + + abstract runtimeCreate(payload: RuntimesCreateInput): DRuntimeView | undefined + + abstract runtimeDelete(payload: RuntimesDeleteInput): DRuntimeView | undefined + + abstract runtimeRotateToken(payload: RuntimesRotateTokenInput): DRuntimeView | undefined + + abstract runtimeUpdate(payload: RuntimesUpdateInput): DRuntimeView | undefined + +} + +export abstract class DRuntimeReactiveService extends DRuntimeService { + + findById(id: Scalars["RuntimeID"]["output"]): DRuntimeView | undefined { + return this.values().find(runtime => runtime.id === id); + } + + abstract override runtimeCreate(payload: RuntimesCreateInput): DRuntimeView | undefined + + abstract override runtimeDelete(payload: RuntimesDeleteInput): DRuntimeView | undefined + + abstract override runtimeRotateToken(payload: RuntimesRotateTokenInput): DRuntimeView | undefined + + abstract override runtimeUpdate(payload: RuntimesUpdateInput): DRuntimeView | undefined + +} \ No newline at end of file diff --git a/src/components/d-runtime/DRuntime.view.ts b/src/components/d-runtime/DRuntime.view.ts new file mode 100644 index 00000000..9f2d5751 --- /dev/null +++ b/src/components/d-runtime/DRuntime.view.ts @@ -0,0 +1,94 @@ +import { + DataTypeConnection, + FlowTypeConnection, + Maybe, + Namespace, + NamespaceProjectConnection, + Runtime, + RuntimeStatusType, + Scalars +} from "@code0-tech/sagittarius-graphql-types"; + +export class DRuntimeView { + + /** Time when this Runtime was created */ + private readonly _createdAt?: Maybe; + /** DataTypes of the runtime */ + private readonly _dataTypes?: Maybe; + /** The description for the runtime if present */ + private readonly _description?: Maybe; + /** FlowTypes of the runtime */ + private readonly _flowTypes?: Maybe; + /** Global ID of this Runtime */ + private readonly _id?: Maybe; + /** The name for the runtime */ + private readonly _name?: Maybe; + /** The parent namespace for the runtime */ + private readonly _namespace?: Maybe; + /** Projects associated with the runtime */ + private readonly _projects?: Maybe; + /** The status of the runtime */ + private readonly _status?: Maybe; + /** Token belonging to the runtime, only present on creation */ + private readonly _token?: Maybe; + /** Time when this Runtime was last updated */ + private readonly _updatedAt?: Maybe; + + constructor(payload: Runtime) { + this._createdAt = payload.createdAt; + this._dataTypes = payload.dataTypes; + this._description = payload.description; + this._flowTypes = payload.flowTypes; + this._id = payload.id; + this._name = payload.name; + this._namespace = payload.namespace; + this._projects = payload.projects; + this._status = payload.status; + this._token = payload.token; + this._updatedAt = payload.updatedAt; + } + + get createdAt(): Maybe | undefined { + return this._createdAt; + } + + get dataTypes(): Maybe | undefined { + return this._dataTypes; + } + + get description(): Maybe | undefined { + return this._description; + } + + get flowTypes(): Maybe | undefined { + return this._flowTypes; + } + + get id(): Maybe | undefined { + return this._id; + } + + get name(): Maybe | undefined { + return this._name; + } + + get namespace(): Maybe | undefined { + return this._namespace; + } + + get projects(): Maybe | undefined { + return this._projects; + } + + get status(): Maybe | undefined { + return this._status; + } + + get token(): Maybe | undefined { + return this._token; + } + + get updatedAt(): Maybe | undefined { + return this._updatedAt; + } +} \ No newline at end of file diff --git a/src/components/d-user/DUser.mutations.ts b/src/components/d-user/DUser.mutations.ts new file mode 100644 index 00000000..949e2581 --- /dev/null +++ b/src/components/d-user/DUser.mutations.ts @@ -0,0 +1,80 @@ +export const DUserLoginMutation = ` +mutation login( + $username: String, + $email: String, + $password: String!, +) { + usersLogin(input: { + email: $email, + username: $username, + password: $password, + }) { + errors { + ...on ActiveModelError { attribute type } + ...on MessageError { message } + } + userSession { + user { + admin + avatarPath + createdAt + email + firstname + id + lastname + namespace { + id + } + namespaceMemberships { + nodes { + id + } + } + updatedAt + username + } + active + createdAt + id + token + updatedAt + } + } +}` + +export const DUserLoginMfaMutation = ` +mutation login( + $username: String, + $email: String, + $password: String!, + $mfa_type: MfaType!, + $mfa_value: String! +) { + usersLogin(input: { + email: $email, + username: $username, + password: $password, + mfa: { + type: $mfa_type, + value: $mfa_value + } + }) { + errors { + ...on ActiveModelError { attribute type } + ...on MessageError { message } + } + userSession { + user { + namespace { + id + } + } + active + createdAt + id + token + updatedAt + } + } +} +` \ No newline at end of file diff --git a/src/components/d-user/DUser.queries.ts b/src/components/d-user/DUser.queries.ts new file mode 100644 index 00000000..329bceb5 --- /dev/null +++ b/src/components/d-user/DUser.queries.ts @@ -0,0 +1,4 @@ +export const DUserValuesQuery = ` + + +` \ No newline at end of file diff --git a/src/components/d-user/DUser.service.ts b/src/components/d-user/DUser.service.ts new file mode 100644 index 00000000..712cd77d --- /dev/null +++ b/src/components/d-user/DUser.service.ts @@ -0,0 +1,86 @@ +import {ReactiveArrayService, ReactiveArrayStore} from "../../utils/reactiveArrayService"; +import { + User, + UsersEmailVerificationInput, + UserSession, + UsersLoginInput, + UsersLogoutInput, + UsersMfaBackupCodesRotateInput, + UsersMfaTotpGenerateSecretInput, + UsersMfaTotpValidateSecretInput, + UsersRegisterInput, + UsersUpdateInput +} from "@code0-tech/sagittarius-graphql-types"; +import {DUserView} from "./DUser.view"; + +export abstract class DUserService extends ReactiveArrayService { + + constructor(store: ReactiveArrayStore) { + super(store); + } + + abstract userEmailVerification(payload: UsersEmailVerificationInput): User | undefined + + abstract userLogin(payload: UsersLoginInput): User | undefined + + abstract userLogout(payload: UsersLogoutInput): void + + abstract userMfaBackupCodesRotate(payload: UsersMfaBackupCodesRotateInput): void + + abstract userMfaTotpGenerateSecret(payload: UsersMfaTotpGenerateSecretInput): void + + abstract userMfaTotpValidateSecret(payload: UsersMfaTotpValidateSecretInput): void + + abstract userRegister(payload: UsersRegisterInput): User | undefined + + abstract userUpdate(payload: UsersUpdateInput): User | undefined + + abstract createUserSession(payload: UserSession): void + + abstract getUserSession(): UserSession | undefined +} + +export abstract class DUserReactiveService extends DUserService { + + override userLogin(payload: UsersLoginInput): User | undefined { + //check if a session already exists + if (this.getUserSession()) { + return this.values().find(user => user.id === this.getUserSession()?.user?.id); + } + + return undefined; + } + + override userLogout(payload: UsersLogoutInput): void { + if (!this.getUserSession()) return + + const user = this.values().find(user => user.id === this.getUserSession()?.user?.id); + window.localStorage.removeItem("ide_code-zero_session"); + const index = this.values().findIndex(lUser => lUser.id === user?.id) + this.delete(index) + this.update() + + } + + override createUserSession(payload: UserSession): void { + window.localStorage.setItem("ide_code-zero_session", JSON.stringify(payload)); + } + + override getUserSession(): UserSession | undefined { + return window.localStorage.getItem("ide_code-zero_session") as UserSession + } + + abstract override userMfaBackupCodesRotate(payload: UsersMfaBackupCodesRotateInput): void + + abstract override userMfaTotpGenerateSecret(payload: UsersMfaTotpGenerateSecretInput): void + + abstract override userMfaTotpValidateSecret(payload: UsersMfaTotpValidateSecretInput): void + + abstract override userRegister(payload: UsersRegisterInput): User | undefined + + abstract override userUpdate(payload: UsersUpdateInput): User | undefined + + abstract override userEmailVerification(payload: UsersEmailVerificationInput): User | undefined + + +} \ No newline at end of file diff --git a/src/components/d-user/DUser.view.ts b/src/components/d-user/DUser.view.ts new file mode 100644 index 00000000..cad02f9d --- /dev/null +++ b/src/components/d-user/DUser.view.ts @@ -0,0 +1,109 @@ +import {Maybe, Namespace, NamespaceMemberConnection, Scalars, User} from "@code0-tech/sagittarius-graphql-types"; + +export class DUserView { + + /** Global admin status of the user */ + private _admin?: Maybe; + /** The avatar if present of the user */ + private _avatarPath?: Maybe; + /** Time when this User was created */ + private readonly _createdAt?: Maybe; + /** Email of the user */ + private _email?: Maybe; + /** Firstname of the user */ + private _firstname?: Maybe; + /** Global ID of this User */ + private readonly _id?: Maybe; + /** Lastname of the user */ + private _lastname?: Maybe; + /** Namespace of this user */ + private readonly _namespace?: Maybe; + /** Namespace Memberships of this user */ + private readonly _namespaceMemberships?: Maybe; + /** Time when this User was last updated */ + private readonly _updatedAt?: Maybe; + /** Username of the user */ + private _username?: Maybe; + + constructor(user: User) { + this._admin = user.admin; + this._avatarPath = user.avatarPath; + this._createdAt = user.createdAt; + this._email = user.email; + this._firstname = user.firstname; + this._id = user.id; + this._lastname = user.lastname; + this._namespace = user.namespace; + this._namespaceMemberships = user.namespaceMemberships; + this._updatedAt = user.updatedAt; + this._username = user.username; + } + + get admin(): Maybe | undefined { + return this._admin; + } + + get avatarPath(): Maybe | undefined { + return this._avatarPath; + } + + get createdAt(): Maybe | undefined { + return this._createdAt; + } + + get email(): Maybe | undefined { + return this._email; + } + + get firstname(): Maybe | undefined { + return this._firstname; + } + + get id(): Maybe | undefined { + return this._id; + } + + get lastname(): Maybe | undefined { + return this._lastname; + } + + get namespace(): Maybe | undefined { + return this._namespace; + } + + get namespaceMemberships(): Maybe | undefined { + return this._namespaceMemberships; + } + + get updatedAt(): Maybe | undefined { + return this._updatedAt; + } + + get username(): Maybe | undefined { + return this._username; + } + + set admin(value: Maybe) { + this._admin = value; + } + + set avatarPath(value: Maybe) { + this._avatarPath = value; + } + + set email(value: Maybe) { + this._email = value; + } + + set firstname(value: Maybe) { + this._firstname = value; + } + + set lastname(value: Maybe) { + this._lastname = value; + } + + set username(value: Maybe) { + this._username = value; + } +} \ No newline at end of file diff --git a/src/components/file-tabs/FileTabs.service.ts b/src/components/file-tabs/FileTabs.service.ts index 6dd80ddb..be34863b 100644 --- a/src/components/file-tabs/FileTabs.service.ts +++ b/src/components/file-tabs/FileTabs.service.ts @@ -9,21 +9,21 @@ export class FileTabsService extends ReactiveArrayService { public clearLeft(): void { const index = this.getActiveIndex() - this.store[1](prevState => [...prevState.filter((_, index1) => { + this.access.setState(prevState => [...prevState.filter((_, index1) => { return index1 >= index })]) } public clearRight(): void { const index = this.getActiveIndex() - this.store[1](prevState => [...prevState.filter((_, index1) => { + this.access.setState(prevState => [...prevState.filter((_, index1) => { return index1 <= index })]) } public clearWithoutActive(): void { const tab = this.getActiveTab() - if (tab) this.store[1](prevState => [tab]) + if (tab) this.access.setState(prevState => [tab]) } public activateTab(id: string) { @@ -33,6 +33,7 @@ export class FileTabsService extends ReactiveArrayService { const tab = this.values().find((item: FileTabsView) => item.id === id); if (tab) tab.active = true + this.update() } public delete(index: number) { @@ -48,6 +49,13 @@ export class FileTabsService extends ReactiveArrayService { } public add(value: FileTabsView) { + //if tab with id already exists, do not add it again and just activate the existing one + + if (this.values().some(value1 => value1.id == value.id)) { + this.activateTab(value.id!!) + return + } + if (value.active) { this.values().forEach((item: FileTabsView) => { item.active = false diff --git a/src/components/file-tabs/FileTabs.stories.tsx b/src/components/file-tabs/FileTabs.stories.tsx index f99ee69c..933f24ea 100644 --- a/src/components/file-tabs/FileTabs.stories.tsx +++ b/src/components/file-tabs/FileTabs.stories.tsx @@ -3,7 +3,7 @@ import {FileTabs, FileTabsContent, FileTabsList, FileTabsTrigger} from "./FileTa import React from "react"; import {IconChevronDown, IconDotsVertical, IconFileLambdaFilled} from "@tabler/icons-react"; import Flex from "../flex/Flex"; -import {createReactiveArrayService} from "../../utils/reactiveArrayService"; +import {useReactiveArrayService} from "../../utils/reactiveArrayService"; import {FileTabsView} from "./FileTabs.view"; import {FileTabsService} from "./FileTabs.service"; import Button from "../button/Button"; @@ -17,7 +17,7 @@ export default { export const ExampleFileTabs = () => { - const [store, service] = createReactiveArrayService(FileTabsService) + const [store, service] = useReactiveArrayService(FileTabsService) const id = React.useId() React.useEffect(() => { diff --git a/src/components/form/Input.stories.tsx b/src/components/form/Input.stories.tsx index 039cb8cc..f7699425 100644 --- a/src/components/form/Input.stories.tsx +++ b/src/components/form/Input.stories.tsx @@ -49,35 +49,93 @@ export const Login = () => { dolore magna aliquyam erat, sed diam voluptua.
-
- } - {...inputs.getInputProps("email")} - /> -
- } - {...inputs.getInputProps("password")} - /> -
-
} + {...inputs.getInputProps("email")} + /> +
+ } + {...inputs.getInputProps("password")} + /> +
+
+ + + + Login with Passkeys + -
+ - +
} @@ -128,34 +186,34 @@ export const RadioExample = () => { return - - - - - -
-
- + + + + + +
+
+ -
- +
+
} @@ -223,23 +281,23 @@ export const Switch = () => { return - -
-
- + +
+
+ -
- +
+
} diff --git a/src/components/form/Input.style.scss b/src/components/form/Input.style.scss index bd57d487..cdc1e972 100644 --- a/src/components/form/Input.style.scss +++ b/src/components/form/Input.style.scss @@ -23,6 +23,22 @@ @include box.boxActiveStyle(variables.$primary, variables.$white, variables.$white); } + &__syntax { + position: absolute; + z-index: -1; + overflow: hidden; + font-size: variables.$sm; + color: rgba(variables.$white, .5); + white-space: nowrap; + padding: $padding $padding; + box-sizing: border-box; + top: 0; + left: 0; + width: 100%; + height: 100%; + + } + &__left, &__right { display: flex; align-items: stretch; @@ -73,11 +89,16 @@ width: 100%; box-shadow: none; font-size: variables.$sm; + box-sizing: border-box; color: rgba(variables.$white, .5); & { @include helpers.fontStyle(); } + + &--syntax { + color: transparent; + } } diff --git a/src/components/form/Input.tsx b/src/components/form/Input.tsx index 4e28db93..71d7578d 100644 --- a/src/components/form/Input.tsx +++ b/src/components/form/Input.tsx @@ -1,95 +1,244 @@ +/** + * Input.tsx + * + * A highly customizable and accessible input component with extended features, + * including dynamic suggestion rendering, validation handling, and structural + * decoration options. Designed to integrate seamlessly with complex forms, + * this component provides robust interaction patterns and user guidance. + */ + +import React, {ForwardRefExoticComponent, LegacyRef, RefObject, useEffect, useMemo, useRef, useState} from "react"; + import {Code0Component} from "../../utils/types"; -import React, {LegacyRef, RefObject, useEffect} from "react"; import {ValidationProps} from "./useForm"; import {mergeCode0Props} from "../../utils/utils"; -import "./Input.style.scss" + +import "./Input.style.scss"; + import InputLabel from "./InputLabel"; import InputDescription from "./InputDescription"; import InputMessage from "./InputMessage"; -type Code0Input = Omit, "defaultValue">, "left">, "right">, "title"> - -export interface InputProps extends Code0Input, ValidationProps { - - wrapperComponent?: Code0Component - right?: React.ReactNode | React.ReactElement | React.ReactElement[] - left?: React.ReactNode | React.ReactElement | React.ReactElement[] - leftType?: "action" | "placeholder" | "icon" - rightType?: "action" | "placeholder" | "icon" - title?: React.ReactNode | React.ReactElement - description?: React.ReactNode | React.ReactElement -} - -export const setElementKey = (element: HTMLElement, key: string, value: any, event: string) => { - const valueSetter = Object.getOwnPropertyDescriptor(element, key)?.set; +import {Menu, MenuPortal, MenuTrigger} from "../menu/Menu"; +import { + InputSuggestion, + InputSuggestionMenuContent, + InputSuggestionMenuContentItems, + InputSuggestionMenuContentItemsHandle +} from "./InputSuggestion"; + +// Programmatically set a property (like 'value') and dispatch an event (like 'change') +export const setElementKey = ( + element: HTMLElement, + key: string, + value: any, + event: string +) => { + const valueSetter = Object.getOwnPropertyDescriptor(element, key)?.set; // Try direct setter const prototype = Object.getPrototypeOf(element); - const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, key)?.set; + const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, key)?.set; // Fallback to prototype if (valueSetter && valueSetter !== prototypeValueSetter) { - prototypeValueSetter?.call(element, value); + prototypeValueSetter?.call(element, value); // Use prototype's setter if overridden } else { - valueSetter?.call(element, value); + valueSetter?.call(element, value); // Use direct setter } - element.dispatchEvent(new Event(event, { bubbles: true })); -} - - -const Input: React.ForwardRefExoticComponent> = React.forwardRef((props: InputProps, ref: RefObject) => { + element.dispatchEvent(new Event(event, {bubbles: true})); // Fire change/input event +}; - ref = ref || React.useRef(null) +// Base input props without layout-specific keys +export type Code0Input = Omit< + Omit, "left">, "right">, + "title" +>; - const { - wrapperComponent = {}, - title, - description, - disabled = false, - left, - right, - leftType = "icon", - rightType = "action", - formValidation = { - valid: true, - notValidMessage: null, - setValue: null - }, - ...rest - } = props - - useEffect(() => { - if (!ref) return - if (!ref.current) return - if (!formValidation) return - if (!formValidation.setValue) return - - // @ts-ignore - ref.current.addEventListener("change", ev => formValidation.setValue(rest.type != "checkbox" ? ev.target.value : ev.target.checked)) - }, [ref]) - - - return <> - - {!!title ? : null} - {!!description ? : null} - -
- - {!!left ?
- {left} -
: null} - - | undefined} {...mergeCode0Props("input__control", rest)}/> - - {!!right ?
- {right} -
: null} +// Input component props definition +export interface InputProps extends Code0Input, ValidationProps { -
+ suggestions?: InputSuggestion[] // Optional suggestions shown in dropdown + suggestionsHeader?: React.ReactNode // Custom header above suggestions + suggestionsFooter?: React.ReactNode // Custom footer below suggestions + onSuggestionSelect?: (suggestion: InputSuggestion) => void // Callback when a suggestion is selected + transformValue?: (value: T) => React.ReactNode | T // Optional value transformation function + disableOnValue?: (value: T) => boolean + + wrapperComponent?: Code0Component // Props for the wrapping div + right?: React.ReactNode // Right-side icon or element + left?: React.ReactNode // Left-side icon or element + leftType?: "action" | "placeholder" | "icon" // Visual type for left slot + rightType?: "action" | "placeholder" | "icon" // Visual type for right slot + title?: React.ReactNode // Input label + description?: React.ReactNode // Label description below title - {!formValidation?.valid && formValidation?.notValidMessage ? - : null} - -}) +} +const Input: ForwardRefExoticComponent> = React.forwardRef( + (props: InputProps, ref: RefObject) => { + const inputRef = ref || useRef(null); // External ref or fallback internal ref + const menuRef = useRef(null); // Ref to suggestion list + const [open, setOpen] = useState(false); // Dropdown open state + const shouldPreventCloseRef = useRef(true); // Controls if dropdown should stay open on click + const [value, setValue] = useState(props.defaultValue || props.initialValue || props.placeholder) + + const { + wrapperComponent = {}, // Default empty wrapper props + title, // Optional input label + description, // Optional description below label + disabled = false, // Input disabled state + left, // Left element (icon/button) + right, // Right element (icon/button) + leftType = "icon", // Visual hint for left + rightType = "action", // Visual hint for right + formValidation = {valid: true, notValidMessage: null, setValue: null}, // Validation config + suggestions, // Optional suggestions array + suggestionsHeader, // Optional header above suggestion list + suggestionsFooter, // Optional footer below suggestion list + onSuggestionSelect = () => { + }, // Callback for suggestion selection, + disableOnValue = () => false, + ...rest // Remaining native input props + } = props; + + // Sync input value changes to external validation state + useEffect(() => { + const el = inputRef.current; + if (!el || !formValidation?.setValue) return; + + const handleChange = (event: any) => { + const value = rest.type !== "checkbox" ? event.target.value : event.target.checked; // Support checkbox + formValidation.setValue?.(value); // Push value to form context + }; + + el.addEventListener("change", handleChange); // Native listener + return () => el.removeEventListener("change", handleChange); // Cleanup + }, [formValidation?.setValue]); + + // Manage click-outside logic for dropdown + useEffect(() => { + if (!suggestions) return; + + const handlePointerDown = (event: PointerEvent) => { + shouldPreventCloseRef.current = !!inputRef.current?.contains(event.target as Node); // Stay open if click is inside + }; + + document.addEventListener("pointerdown", handlePointerDown, true); + return () => document.removeEventListener("pointerdown", handlePointerDown, true); + }, [suggestions]); + + const disabledOnValue = React.useMemo(() => disableOnValue(value), [value, disableOnValue]) + + useEffect(() => { + if (!inputRef.current) return + inputRef.current.addEventListener("change", (e: any) => { + if (disabledOnValue) return + setValue(e.target.value) + }) + inputRef.current.addEventListener("input", (e: any) => { + if (disabledOnValue) return + setValue(e.target.value) + }) + }, [inputRef, disabledOnValue]) + + + const syntax = React.useMemo(() => { + return props.transformValue ? ( +
+ {props.transformValue(value)} {/* Render transformed value */} +
+ ) : null + }, [props.transformValue, value]) + + // Render suggestion menu dropdown + const suggestionMenu = useMemo(() => ( + { + if (!nextOpen && shouldPreventCloseRef.current) { // Prevent close if internal click + shouldPreventCloseRef.current = false; + return; + } + + setOpen(nextOpen); + + if (nextOpen) { + setTimeout(() => inputRef.current?.focus(), 0); // Refocus input on open + } + }} + > + + } // Cast for TS compatibility + {...mergeCode0Props(`input__control ${props.transformValue ? "input__control--syntax" : ""}`, rest)} + onFocus={() => !open && setOpen(true)} // Open on focus + onKeyDown={(e) => { + if (e.key === "ArrowDown") { + e.preventDefault(); + menuRef.current?.focusFirstItem(); // Navigate down + } else if (e.key === "ArrowUp") { + e.preventDefault(); + menuRef.current?.focusLastItem(); // Navigate up + } + }} + disabled={disabled || disabledOnValue} + /> + + + + {suggestionsHeader} {/* Custom content above suggestions */} + { + // Update value and dispatch event + setElementKey(ref.current, "value", typeof value == "object" ? JSON.stringify(suggestion.value) : suggestion.value, "change"); + onSuggestionSelect(suggestion) + setOpen(false) + }} + /> + {suggestionsFooter} {/* Custom content below suggestions */} + + + + ), [open, suggestions, suggestionsHeader, suggestionsFooter]); + + return ( + <> + {title && {title}} {/* Optional label */} + {description && {description}} {/* Optional description */} + +
+ {left &&
{left}
} {/* Left element */} + + {suggestions ? ( + suggestionMenu // If suggestions exist, render dropdown version + ) : ( + } + {...mergeCode0Props(`input__control ${props.transformValue ? "input__control--syntax" : ""}`, rest)} // Basic input styling and props + /> + )} + + {syntax} + + {right && +
{right}
} {/* Right element */} +
+ + {!formValidation?.valid && formValidation?.notValidMessage && ( + {formValidation.notValidMessage} // Show validation error + )} + + ); + } +); -export default Input \ No newline at end of file +export default Input; \ No newline at end of file diff --git a/src/components/form/InputSuggestion.tsx b/src/components/form/InputSuggestion.tsx new file mode 100644 index 00000000..20dd1481 --- /dev/null +++ b/src/components/form/InputSuggestion.tsx @@ -0,0 +1,68 @@ +import {MenuContent, MenuContentProps, MenuItem} from "../menu/Menu"; +import React from "react"; +import {ScrollArea, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport} from "../scroll-area/ScrollArea"; + +export interface InputSuggestion { + children: React.ReactNode + value: any + ref?: any +} + +export type InputSuggestionMenuContentProps = MenuContentProps + +export interface InputSuggestionMenuContentItemsProps { + suggestions?: InputSuggestion[] + onSuggestionSelect?: (suggestion: InputSuggestion) => void +} + +export interface InputSuggestionMenuContentItemsHandle { + focusFirstItem: () => void + focusLastItem: () => void +} + +export const InputSuggestionMenuContent: React.FC = React.forwardRef((props, ref) => { + + const {children, ...rest} = props + const localRef = React.useRef(null) + + // @ts-ignore + return e.stopPropagation()} + onInteractOutside={(event) => event.target instanceof HTMLInputElement && event.preventDefault()} + onCloseAutoFocus={(event) => event.preventDefault()} + align={"start"} + sideOffset={8} + {...rest} > + {children} + + +}) + + +export const InputSuggestionMenuContentItems: React.FC = React.forwardRef((props, ref) => { + + const {suggestions, onSuggestionSelect = () => {}, ...rest} = props + const itemRefs = React.useRef<(HTMLDivElement | null)[]>([]) + + React.useImperativeHandle(ref, () => ({ + focusFirstItem: () => itemRefs.current[0]?.focus(), + focusLastItem: () => itemRefs.current.at(-1)?.focus(), + }), []) + + // @ts-ignore + return + + {suggestions?.map((suggestion, i) => { + // @ts-ignore + return setTimeout(() => onSuggestionSelect(suggestion), 0)} ref={el => itemRefs.current[i] = el}> + {suggestion.children} + + })} + + + + + + +}) \ No newline at end of file diff --git a/src/components/form/TextInput.tsx b/src/components/form/TextInput.tsx index 67fac7e8..2c673c84 100644 --- a/src/components/form/TextInput.tsx +++ b/src/components/form/TextInput.tsx @@ -7,6 +7,7 @@ import Button from "../button/Button"; export interface TextInputProps extends Omit, "wrapperComponent" | "type"> { //defaults to false clearable?: boolean + onClear?: (event: Event) => void } const TextInput: React.ForwardRefExoticComponent = React.forwardRef((props, ref: RefObject) => { @@ -19,8 +20,9 @@ const TextInput: React.ForwardRefExoticComponent = React.forward ...rest } = props - const toClearable = () => { + const toClearable = (event: Event) => { if (ref.current) setElementKey(ref.current, "value", "", "change") + if (props.onClear) props.onClear(event) } const rightAction = [right] diff --git a/src/components/menu/Menu.style.scss b/src/components/menu/Menu.style.scss index 8bb947ee..adebd665 100644 --- a/src/components/menu/Menu.style.scss +++ b/src/components/menu/Menu.style.scss @@ -3,7 +3,7 @@ @use "../../styles/variables"; .menu { - z-index: 100; + z-index: 999; &__content, &__sub-content { padding: variables.$xxs; @@ -19,7 +19,9 @@ &__label { text-transform: uppercase; font-size: variables.$xs; - display: block; + display: flex; + gap: variables.$xxs; + align-items: center; padding: variables.$xxs variables.$xs; color: helpers.color(); diff --git a/src/components/segmented-control/SegmentedControl.style.scss b/src/components/segmented-control/SegmentedControl.style.scss index d3fdc49f..f4349fa7 100644 --- a/src/components/segmented-control/SegmentedControl.style.scss +++ b/src/components/segmented-control/SegmentedControl.style.scss @@ -21,7 +21,7 @@ & { @include box.box(); - @include helpers.borderRadius(); + border-radius: variables.$borderRadius - variables.$xxs; @include helpers.fontStyle(); font-size: variables.$sm; box-shadow: none; diff --git a/src/components/tooltip/Tooltip.style.scss b/src/components/tooltip/Tooltip.style.scss index 604f8bb0..7baca7b2 100644 --- a/src/components/tooltip/Tooltip.style.scss +++ b/src/components/tooltip/Tooltip.style.scss @@ -6,6 +6,7 @@ &__content { + z-index: 999; padding: variables.$xxs variables.$xs; & { diff --git a/src/styles/_helpers.scss b/src/styles/_helpers.scss index e840dbf5..2654370a 100644 --- a/src/styles/_helpers.scss +++ b/src/styles/_helpers.scss @@ -22,8 +22,10 @@ } @function borderColor($color: variables.$secondary) { - $mixedBorderWhite: color.mix($color, variables.$white, 25%); - @return rgba($mixedBorderWhite, .1); + @if($color == variables.$secondary) { + @return rgba($color, .1); + } + @return rgba($color, .25); } @function color($color: variables.$white, $hierarchy: variables.$hierarchyTertiary) { diff --git a/src/utils/generics.ts b/src/utils/generics.ts index a8dcd860..c877cdb7 100644 --- a/src/utils/generics.ts +++ b/src/utils/generics.ts @@ -1,678 +1,637 @@ +import {FunctionDefinitionView} from "../components/d-flow/function/DFlowFunction.view"; +import {DFlowDataTypeReactiveService, DFlowDataTypeService} from "../components/d-flow/data-type/DFlowDataType.service"; import { - DataTypeObject, - DataTypeRuleObject, - EDataTypeRuleType, - GenericCombinationStrategy, + DataType, + DataTypeIdentifier, + DataTypeRule, + DataTypeRuleConnection, + DataTypeRulesConfig, + DataTypeRulesVariant, + DataTypeVariant, + GenericCombinationStrategyType, GenericMapper, GenericType, - Type, - Value -} from "../components/d-flow/data-type/DFlowDataType.view"; -import {FunctionDefinition} from "../components/d-flow/function/DFlowFunction.view"; -import {DFlowDataTypeReactiveService, DFlowDataTypeService} from "../components/d-flow/data-type/DFlowDataType.service"; -import { - DFlowDataTypeItemOfCollectionRuleConfig -} from "../components/d-flow/data-type/rules/DFlowDataTypeItemOfCollectionRule"; - -/** - * Resolves concrete type mappings for generic keys in a generic type system. - * - * This function takes a parameter type (possibly containing generic keys and nested generic mappers) - * and a value type (the actual, concrete structure) and traverses both in parallel. - * For every generic key from the `genericKeys` array, it finds the concrete type - * that is present at the matching position in the value type. - * - * The implementation supports: - * - Simple and nested generics - * - Generic combination strategies such as AND/OR for multi-key mappings - * - Arbitrarily deep structures - * - * @param parameterType The template type, which may use generics like "D", "E", etc. - * @param valueType The instantiated/concrete type, e.g. "NUMBER", "ARRAY", or objects with their own mappers - * @param genericKeys Array of all generic keys to resolve (from function definition) - * @returns A mapping from each generic key to its resolved concrete type in valueType - * - */ + NodeParameterValue +} from "@code0-tech/sagittarius-graphql-types"; + +const GENERIC_PLACEHOLDER = "GENERIC"; + +type IdentifierLike = DataTypeIdentifier | string | undefined | null; + +type GenericMappingResult = Record; + +type GenericReplacement = DataTypeIdentifier | GenericMapper; + +type GenericMap = Map; + +const isPlainObject = (value: unknown): value is Record => { + return typeof value === "object" && value !== null && !Array.isArray(value); +}; + +const isDataTypeIdentifier = (value: unknown): value is DataTypeIdentifier => { + if (!isPlainObject(value)) return false; + return ( + "genericKey" in value || + "genericType" in value || + "dataType" in value + ); +}; + +const isGenericMapper = (value: GenericMapper): value is GenericMapper => { + return isPlainObject(value) && "target" in value && Array.isArray((value as GenericMapper).sourceDataTypeIdentifiers); +}; + +const isDataType = (value: unknown): value is DataType => { + return isPlainObject(value) && "variant" in value && "identifier" in value; +}; + +const isDataTypeRule = (value: unknown): value is DataTypeRule => { + return isPlainObject(value) && "variant" in value && "config" in value; +}; + +const extractIdentifierId = (identifier: IdentifierLike): string | undefined => { + if (!identifier) return undefined; + if (typeof identifier === "string") return identifier; + return ( + identifier?.dataType?.identifier ?? + identifier?.genericType?.dataType?.identifier + ) ?? undefined; +}; + +const extractIdentifierGenericKey = ( + identifier: IdentifierLike, + genericKeys?: Set +): string | undefined => { + if (!identifier) return undefined; + if (typeof identifier === "string") { + if (genericKeys && genericKeys.has(identifier)) return identifier; + return undefined; + } + return identifier.genericKey ?? undefined; +}; + +const getIdentifierMappers = (identifier: IdentifierLike): GenericMapper[] => { + if (!identifier || typeof identifier === "string") return []; + return identifier.genericType?.genericMappers ?? []; +}; + +const cloneMapperWithSources = ( + mapper: GenericMapper, + sources: DataTypeIdentifier[] +): GenericMapper => { + return { + ...mapper, + sourceDataTypeIdentifiers: sources + }; +}; + +const toCombinationTypes = (mapper: GenericMapper): Set => { + const strategies = mapper.genericCombinationStrategies ?? []; + return new Set(strategies.map(strategy => strategy.type!!)); +}; + +const normalizeObjectForComparison = (value: unknown): unknown => { + if (Array.isArray(value)) { + return value.map(normalizeObjectForComparison); + } + + if (isPlainObject(value)) { + const normalized: Record = {}; + Object.entries(value).forEach(([key, val]) => { + if (key === "__typename" || key === "id" || key === "createdAt" || key === "updatedAt") return; + normalized[key] = normalizeObjectForComparison(val); + }); + return normalized; + } + + return value; +}; + +const identifiersMatch = ( + source: IdentifierLike, + target: IdentifierLike +): boolean => { + if (!target) return !source; + if (typeof target === "string") { + if (target === GENERIC_PLACEHOLDER) return true; + return extractIdentifierId(source) === target; + } + + const genericKey = target.genericKey; + if (genericKey) return true; + + const targetId = extractIdentifierId(target); + const sourceId = extractIdentifierId(source); + return !!targetId && targetId === sourceId; +}; + +const replaceIdentifiersInConfig = ( + config: DataTypeRulesConfig, + genericMap: GenericMap +): DataTypeRulesConfig => { + switch (config.__typename) { + case "DataTypeRulesContainsKeyConfig": + case "DataTypeRulesContainsTypeConfig": + case "DataTypeRulesReturnTypeConfig": { + const identifier = (config as { dataTypeIdentifier?: DataTypeIdentifier }).dataTypeIdentifier; + if (!identifier) return config; + return { + ...config, + dataTypeIdentifier: replaceGenericKeysInType(identifier, genericMap) + } as DataTypeRulesConfig; + } + case "DataTypeRulesInputTypesConfig": { + const typedConfig = config; + const inputTypes = typedConfig.inputTypes?.map(inputType => ({ + ...inputType, + dataTypeIdentifier: replaceGenericKeysInType(inputType.dataTypeIdentifier!!, genericMap) + })); + return { + ...typedConfig, + inputTypes + }; + } + default: + return config; + } +}; + export const resolveGenericKeyMappings = ( - parameterType: Type, - valueType: Type, + parameterType: DataTypeIdentifier, + valueType: DataTypeIdentifier, genericKeys: string[] -): Record => { - const result: Record = {} - - /** - * Recursively matches parameter type and value type, mapping generics to their concrete types - * - * @param param Current node in the parameter type (string or type object) - * @param value Current node in the value type (string or type object) - */ - function recurse(param: Type, value: Type) { - // If param is a string and a generic key, map it directly to value - if (typeof param === "string") { - if (genericKeys.includes(param)) { - result[param] = value +): GenericMappingResult => { + const result: GenericMappingResult = {}; + const genericKeySet = new Set(genericKeys); + + const recurse = (param: IdentifierLike, value: IdentifierLike) => { + if (!param || !value) return; + + const paramKey = extractIdentifierGenericKey(param, genericKeySet); + if (paramKey && genericKeySet.has(paramKey)) { + if (isDataTypeIdentifier(value)) { + result[paramKey] = value; } - return + return; } - // If value is a string but param is a type object, nothing to do - if (typeof value === "string") return - // Only access .generic_mapper if value is an object - const paramGMs = param.generic_mapper ?? [] - const valueGMs = typeof value === "object" && value !== null ? value.generic_mapper ?? [] : [] + const paramMappers = getIdentifierMappers(param); + if (paramMappers.length === 0) return; - for (const paramGM of paramGMs) { - // Match generic_target between parameter and value - const matchingValueGM = valueGMs.find((vgm) => { - return vgm.generic_target === paramGM.generic_target - }) - - if (!matchingValueGM) continue + const valueMappers = getIdentifierMappers(value); - const targetValueTypes = matchingValueGM.types + for (const paramMapper of paramMappers) { + const matchingValueMapper = valueMappers.find(mapper => mapper.target === paramMapper.target); + if (!matchingValueMapper) continue; - // Collect generic keys in this mapping level - const keysInTypes = paramGM.types.filter((t): t is string => { - return typeof t === "string" && genericKeys.includes(t) - }) + const keysInSources = (paramMapper.sourceDataTypeIdentifiers ?? []) + .map(source => extractIdentifierGenericKey(source, genericKeySet)) + .filter((key): key is string => !!key && genericKeySet.has(key)); - const combination = paramGM.generic_combination ?? [] + const combination = toCombinationTypes(paramMapper); + const valueSources = matchingValueMapper.sourceDataTypeIdentifiers ?? []; - // If AND/OR with one value type, assign all keys to that type if ( - (combination.includes(GenericCombinationStrategy.AND) || - combination.includes(GenericCombinationStrategy.OR)) && - targetValueTypes.length === 1 && - keysInTypes.length === paramGM.types.length + (combination.has(GenericCombinationStrategyType.And) || combination.has(GenericCombinationStrategyType.Or)) && + valueSources.length === 1 && + keysInSources.length === (paramMapper.sourceDataTypeIdentifiers?.length ?? 0) ) { - for (const key of keysInTypes) { - result[key] = targetValueTypes[0] + for (const key of keysInSources) { + result[key] = valueSources[0]; } } else { - // Otherwise, recurse element-wise (supports mixed keys and nested objects) - for (let i = 0; i < paramGM.types.length; i++) { - const paramSubType = paramGM.types[i] - const valueSubType = targetValueTypes[i] - recurse(paramSubType, valueSubType) + const length = Math.min(paramMapper.sourceDataTypeIdentifiers?.length ?? 0, valueSources.length); + for (let index = 0; index < length; index++) { + recurse(paramMapper.sourceDataTypeIdentifiers?.[index], valueSources[index]); } } } - } + }; - recurse(parameterType, valueType) - return result -} + recurse(parameterType, valueType); + return result; +}; -/** - * Recursively replaces all generic keys in a parameter type tree with their resolved concrete types. - * If a mapping entry for a generic key is a GenericMapper, its types are inlined into the parent mapper, - * but the parent's generic_target and other properties are preserved. This function guarantees - * that the resulting structure is always a valid Type (never a standalone GenericMapper). - * - * @param type The parameter type to process (could be a string or a type object) - * @param genericMap A Map where each generic key maps to a Type or a GenericMapper as replacement - * @returns The type tree with all generic keys replaced by their resolved form - */ export const replaceGenericKeysInType = ( - type: Type, - genericMap: Map -): Type => { - // If this node is a generic key and there's a replacement in the map... - if (typeof type === "string" && genericMap.has(type)) { - const replacement = genericMap.get(type) - // If the replacement is a valid Type (string or has a 'type' field), just use it directly - if ( - typeof replacement === "string" || - (typeof replacement === "object" && replacement !== null && "type" in replacement) - ) { - return replacement as Type + type: DataTypeIdentifier, + genericMap: GenericMap +): DataTypeIdentifier => { + if (!isDataTypeIdentifier(type)) return type; + + const {genericKey, genericType} = type; + + if (genericKey && genericMap.has(genericKey)) { + const replacement = genericMap.get(genericKey); + if (replacement && isDataTypeIdentifier(replacement)) { + return replacement; } - // If the replacement is a GenericMapper, we DO NOT return it directly - // (It will be inlined in the parent generic_mapper context—handled below) - // Fallback: return the generic key as-is, which is safe in this edge-case - return type - } - // If this node is a string and not a generic key, return as-is - if (typeof type === "string") { - return type + return type; } - // If this node is a type object, process all its generic_mapper entries recursively - const newGenericMapper = (type.generic_mapper ?? []).map(gm => { - let resultTypes: Type[] = [] - for (const t of gm.types) { - // Check if 't' is a generic key with a mapping - if (typeof t === "string" && genericMap.has(t)) { - const replacement = genericMap.get(t) - if ( - replacement && - typeof replacement === "object" && - "types" in replacement && - Array.isArray(replacement.types) - ) { - // If replacement is a GenericMapper: inline all its types into the parent - resultTypes.push(...replacement.types) - } else if ( - typeof replacement === "string" || - (typeof replacement === "object" && replacement !== null && "type" in replacement) - ) { - // If replacement is a valid Type: insert it - resultTypes.push(replacement as Type) + if (!genericType) return type; + + const resolvedMappers = (genericType.genericMappers ?? []).map(mapper => { + const resolvedSources: DataTypeIdentifier[] = []; + + for (const source of mapper.sourceDataTypeIdentifiers ?? []) { + if (!source) continue; + const sourceKey = source.genericKey; + if (sourceKey && genericMap.has(sourceKey)) { + const replacement = genericMap.get(sourceKey); + if (replacement && isGenericMapper(replacement as GenericMapper)) { + resolvedSources.push(...(replacement as GenericMapper).sourceDataTypeIdentifiers!!); + } else if (replacement && isDataTypeIdentifier(replacement)) { + resolvedSources.push(replacement); } else { - // Defensive fallback: insert the original key - resultTypes.push(t) + resolvedSources.push(source); } - } else if (typeof t === "object" && t !== null) { - // Recursively process any nested type objects - resultTypes.push(replaceGenericKeysInType(t, genericMap)) + } else if (isDataTypeIdentifier(source)) { + resolvedSources.push(replaceGenericKeysInType(source, genericMap)); } else { - // Fallback for unknown values (shouldn't happen) - resultTypes.push(t) + resolvedSources.push(source as DataTypeIdentifier); } } - // Always preserve all other properties of the parent GenericMapper - return { - ...gm, - types: resultTypes - } - }) - // Return the new type object with all generics replaced/inlined + return cloneMapperWithSources(mapper, resolvedSources); + }); + return { ...type, - generic_mapper: newGenericMapper - } -} - + genericType: { + ...genericType, + genericMappers: resolvedMappers + } + }; +}; -/** - * Resolves all occurrences of specified generic keys within a DataTypeObject structure, - * comparing a generic (template) DataTypeObject with a concrete (instantiated) DataTypeObject. - * Returns either the concrete Type or GenericMapper found at the matching position. - * - * Performance: - * - Stops recursion for each key as soon as a match is found (early return) - * - Traverses rules, parent, config. Type, and generic_mapper branches recursively - * - * @param genericObj The generic DataTypeObject (may use generic keys in its type tree) - * @param concreteObj The instantiated DataTypeObject (with all generics resolved) - * @param genericKeys The list of all generic keys to resolve (e.g. ["D", "O", "T"]) - * @returns Record mapping each generic key to its resolved Type or GenericMapper (or undefined if not found) - */ export const resolveAllGenericKeysInDataTypeObject = ( - genericObj: DataTypeObject, - concreteObj: DataTypeObject, + genericObj: DataType, + concreteObj: DataType, genericKeys: string[] -): Record => { - const result: Record = {} - - // Track which keys are still missing for early return optimization - const unresolved = new Set(genericKeys) - - function recurse( - genericNode: any, - concreteNode: any, - parentConcreteMapper?: GenericMapper - ) { - if (!genericNode || !concreteNode || unresolved.size === 0) return - - // 1. If the current node matches a generic key and was not resolved yet - if (typeof genericNode === "string" && unresolved.has(genericNode)) { - if (parentConcreteMapper) { - result[genericNode] = parentConcreteMapper - } else if (typeof concreteNode === "string") { - result[genericNode] = concreteNode - } else if ( - typeof concreteNode === "object" && !Array.isArray(concreteNode) && - !("generic_target" in concreteNode) - ) { - result[genericNode] = concreteNode - } else if ( - typeof concreteNode === "object" && - "generic_target" in concreteNode - ) { - result[genericNode] = concreteNode as GenericMapper +): Record => { + const result: Record = {}; + const unresolved = new Set(genericKeys); + + const visit = ( + genericNode: unknown, + concreteNode: unknown, + parentMapper?: GenericMapper + ) => { + if (!genericNode || !concreteNode || unresolved.size === 0) return; + + if (isDataTypeIdentifier(genericNode)) { + const key = genericNode.genericKey; + if (key && unresolved.has(key)) { + if (parentMapper) { + result[key] = parentMapper; + } else if (isGenericMapper(concreteNode as GenericMapper)) { + result[key] = concreteNode as GenericMapper; + } else if (isDataTypeIdentifier(concreteNode)) { + result[key] = concreteNode; + } + unresolved.delete(key); + if (unresolved.size === 0) return; } - unresolved.delete(genericNode) - if (unresolved.size === 0) return } - // 2. Traverse rules array - if (Array.isArray(genericNode.rules) && Array.isArray(concreteNode.rules)) { - for (let i = 0; i < genericNode.rules.length; i++) { - recurse(genericNode.rules[i], concreteNode.rules[i]) - if (unresolved.size === 0) return + if (isGenericMapper(genericNode as GenericMapper) && isGenericMapper(concreteNode as GenericMapper)) { + const length = Math.min((genericNode as GenericMapper).sourceDataTypeIdentifiers?.length!!, (concreteNode as GenericMapper).sourceDataTypeIdentifiers?.length!!); + for (let index = 0; index < length; index++) { + visit((genericNode as GenericMapper).sourceDataTypeIdentifiers!![index], (concreteNode as GenericMapper).sourceDataTypeIdentifiers!![index], concreteNode as GenericMapper); + if (unresolved.size === 0) return; } + return; } - // 3. Traverse parent property - if (genericNode.parent && concreteNode.parent) { - recurse(genericNode.parent, concreteNode.parent) - if (unresolved.size === 0) return + if (isDataType(genericNode) && isDataType(concreteNode)) { + const genericRules = genericNode.rules?.nodes ?? []; + const concreteRules = concreteNode.rules?.nodes ?? []; + const length = Math.min(genericRules.length, concreteRules.length); + + for (let index = 0; index < length; index++) { + const genericRule = genericRules[index]; + const concreteRule = concreteRules[index]; + if (!genericRule || !concreteRule) continue; + visit(genericRule, concreteRule); + if (unresolved.size === 0) return; + } + return; } - // 4. Traverse config.type property - if ( - genericNode.config && concreteNode.config && - genericNode.config.type !== undefined && concreteNode.config.type !== undefined - ) { - recurse(genericNode.config.type, concreteNode.config.type) - if (unresolved.size === 0) return + if (isDataTypeRule(genericNode) && isDataTypeRule(concreteNode)) { + visit(genericNode.config, concreteNode.config); + return; } - // 5. Traverse generic_mapper arrays - if ( - typeof genericNode === "object" && typeof concreteNode === "object" && Array.isArray(genericNode.generic_mapper) && Array.isArray(concreteNode.generic_mapper) - ) { - outer: for (let i = 0; i < genericNode.generic_mapper.length; i++) { - const genericMapper: GenericMapper = genericNode.generic_mapper[i] - const concreteMapper = (concreteNode.generic_mapper as GenericMapper[]).find( - (m: GenericMapper) => m.generic_target === genericMapper.generic_target - ) - if (!concreteMapper) continue - for (let j = 0; j < genericMapper.types.length; j++) { - recurse(genericMapper.types[j], concreteMapper.types[j], concreteMapper) - if (unresolved.size === 0) break outer - } + if (Array.isArray(genericNode) && Array.isArray(concreteNode)) { + const length = Math.min(genericNode.length, concreteNode.length); + for (let index = 0; index < length; index++) { + visit(genericNode[index], concreteNode[index], parentMapper); + if (unresolved.size === 0) return; } + return; } - } - recurse(genericObj, concreteObj) - return result -} + if (isPlainObject(genericNode) && isPlainObject(concreteNode)) { + for (const key of Object.keys(genericNode)) { + if (!(key in concreteNode)) continue; + if (key === "__typename") continue; + visit((genericNode as Record)[key], (concreteNode as Record)[key], parentMapper); + if (unresolved.size === 0) return; + } + } + }; + + visit(genericObj, concreteObj); + return result; +}; -/** - * Recursively replaces all generic keys in a DataTypeObject tree - * (in parent and all nested rules/config.type fields) using the provided genericMap. - * Types are replaced according to the logic in replaceGenericKeysInType. - * - * @param dataType The DataTypeObject to process - * @param genericMap Map from generic key to Type or GenericMapper - * @returns A new DataTypeObject with all generics replaced by their resolved form - */ export const replaceGenericKeysInDataTypeObject = ( - dataType: DataTypeObject, - genericMap: Map -): DataTypeObject => { - // Helper to handle the parent, which may be a generic key or a full Type - const resolvedParent = - dataType.parent !== undefined - ? replaceGenericKeysInType(dataType.parent, genericMap) - : undefined - - // Helper for rules (which may be deeply nested) - const resolvedRules = (dataType.rules ?? []).map(rule => { - const newRule = {...rule} - // Only replace if config has a 'type' field - if ( - newRule.config && - typeof newRule.config === "object" && - "type" in newRule.config - ) { - newRule.config = { - ...newRule.config, - type: replaceGenericKeysInType( - (newRule.config as { type: Type }).type, - genericMap - ) - } + dataType: DataType, + genericMap: GenericMap +): DataType => { + + const resolvedRules = dataType.rules + ? { + ...dataType.rules, + nodes: dataType.rules.nodes?.map(rule => { + if (!rule) return rule; + return { + ...rule, + config: replaceIdentifiersInConfig(rule.config!!, genericMap) + } as DataTypeRule; + }) } - return newRule - }) + : undefined; return { ...dataType, - parent: resolvedParent, - rules: resolvedRules - } -} + rules: resolvedRules as DataTypeRuleConnection + }; +}; -/** - * Resolves all generic keys for a given function definition based on the parameter values and data type service. - * - * For each function parameter, this method matches generic parameter types with their corresponding - * concrete types derived from the provided values array, using a given data type service. - * The result is a map from each generic key to its resolved concrete Type or GenericMapper. - * - * Performance optimizations: - * - Avoids repeated calls to dataTypeService - * - Does not overwrite previously found mappings in the Map - * - Minimizes branching and redundant logic - * - * @param func Function definition (with parameters, genericKeys, etc.) - * @param values Array of concrete values to match against function parameters - * @param dataTypeService Service for resolving types and data type metadata - * @returns Map from generic key to its resolved Type or GenericMapper - */ export const resolveGenericKeys = ( - func: FunctionDefinition, - values: Value[], + func: FunctionDefinitionView, + values: NodeParameterValue[], dataTypeService: DFlowDataTypeService -): Map => { - const genericMap = new Map() - const genericKeys = func.genericKeys ?? [] - - if (!func.parameters) return genericMap - - for (let i = 0; i < func.parameters.length; i++) { - const parameter = func.parameters[i] - const paramType = parameter.type - const value = values[i] - const valueType = dataTypeService.getTypeFromValue(value) - const valueDataType = dataTypeService.getDataType(valueType) - const paramDataType = dataTypeService.getDataType(paramType) - - // Only process if the parameter type or the value type is generic - const paramIsGeneric = typeof paramType === "string" - ? genericKeys.includes(paramType) - : !!paramDataType - - if (!paramIsGeneric) continue - - // CASE 1: Both parameter and value are generic - const valueIsGeneric = typeof valueType === "object" - && valueType !== null - && "type" in valueType - && !!dataTypeService.getDataType(paramType) - - if (valueIsGeneric || (typeof paramType === "string" && genericKeys.includes(paramType))) { - const genericTypes = resolveGenericKeyMappings(paramType, valueType, genericKeys) - for (const genericKey of genericKeys) { - if (!genericMap.has(genericKey) && genericTypes[genericKey]) { - genericMap.set(genericKey, genericTypes[genericKey]) - } - } - continue - } +): GenericMap => { + const genericMap: GenericMap = new Map(); + const genericKeys = func.genericKeys ?? []; - // CASE 2: Parameter is generic, value is concrete - if (valueDataType && paramDataType && valueDataType.json && paramDataType.json && Array.isArray(paramDataType.genericKeys)) { - // Resolve all generic key mappings for the parameter's data type structure - const genericParameterTypes = resolveAllGenericKeysInDataTypeObject( - paramDataType.json, - valueDataType.json, - paramDataType.genericKeys - ) - - // Build a new parameter type object where each mapper's types is replaced by the resolved mapping if available - const mappedParamType = { - ...(paramType as GenericType), - generic_mapper: (paramType as GenericType).generic_mapper?.map(mapper => { - const mapped = genericParameterTypes[mapper.generic_target] - if (mapped) { - return { - ...mapper, - types: [mapped] - } as GenericMapper - } - return mapper - }) - } + if (!func.parameterDefinitions || genericKeys.length === 0) return genericMap; - // Resolve the final generic mappings for this parameter - const genericTypes = resolveGenericKeyMappings(paramType, mappedParamType, genericKeys) - for (const genericKey of genericKeys) { - if (!genericMap.has(genericKey) && genericTypes[genericKey]) { - genericMap.set(genericKey, genericTypes[genericKey]) - } + const genericKeySet = new Set(genericKeys); + + func.parameterDefinitions.forEach((parameter, index) => { + const parameterType = parameter.dataTypeIdentifier as IdentifierLike; + const value = values[index]; + const valueType = dataTypeService.getTypeFromValue(value) as IdentifierLike; + + if (!parameterType || !valueType) return; + + const mappings = resolveGenericKeyMappings( + parameterType as DataTypeIdentifier, + valueType as DataTypeIdentifier, + genericKeys + ); + + for (const [key, identifier] of Object.entries(mappings)) { + if (!genericKeySet.has(key)) continue; + if (!genericMap.has(key)) { + genericMap.set(key, identifier); } } - } + }); - return genericMap -} + return genericMap; +}; -/** - * Checks if a source DataTypeObject matches a target DataTypeObject with the following semantics: - * - Only the fields 'type' and 'rules' are considered. - * - All rules in the target must be matched by at least one rule in the source. - * - Target rules may be more generic (contain "GENERIC" as type/key), whereas source rules can be more specific. - * - For rules with a 'type' or 'key', a GENERIC in target matches any value in source. - * - Source must have at least as many or more specific rules than the target. - * - This function supports all rule types defined in the EDataTypeRuleType enum. - * - * @param source Source DataTypeObject (potentially more specific) - * @param target Target DataTypeObject (potentially generic, e.g., contains "GENERIC" in rules) - * @returns True if source matches all relevant constraints in target, otherwise false - */ export function isMatchingDataTypeObject( - source: DataTypeObject, - target: DataTypeObject + source: DataType, + target: DataType ): boolean { - if (source.type !== target.type) return false; - if (!Array.isArray(target.rules) || target.rules.length === 0) return true; - - for (const targetRule of target.rules) { - const found = source.rules?.some(sourceRule => - ruleMatches(sourceRule, targetRule) - ); + if (source.variant !== target.variant) return false; + const targetRules = target.rules?.nodes ?? []; + if (targetRules.length === 0) return true; + + const sourceRules = source.rules?.nodes ?? []; + + for (const targetRule of targetRules) { + if (!targetRule) continue; + const found = sourceRules.some(sourceRule => { + if (!sourceRule) return false; + return ruleMatches(sourceRule, targetRule); + }); if (!found) return false; } + return true; +} - // Rule comparison logic supporting all relevant EDataTypeRuleType configs - function ruleMatches(sourceRule: DataTypeRuleObject, targetRule: DataTypeRuleObject): boolean { - if (sourceRule.type !== targetRule.type) return false; - switch (targetRule.type) { - case EDataTypeRuleType.CONTAINS_TYPE: - case EDataTypeRuleType.RETURNS_TYPE: - case EDataTypeRuleType.PARENT: - if ("type" in targetRule.config && targetRule.config.type === "GENERIC") return true; - if ("type" in sourceRule.config && "type" in targetRule.config) - return sourceRule.config.type === targetRule.config.type; - return false; - case EDataTypeRuleType.CONTAINS_KEY: - if ("key" in sourceRule.config && "key" in targetRule.config) { - if (sourceRule.config.key !== targetRule.config.key) return false; - if ("type" in targetRule.config && targetRule.config.type === "GENERIC") return true; - if ("type" in sourceRule.config && "type" in targetRule.config) - return sourceRule.config.type === targetRule.config.type; - } - return false; - case EDataTypeRuleType.REGEX: - if ("pattern" in sourceRule.config && "pattern" in targetRule.config) - return sourceRule.config.pattern === targetRule.config.pattern; - return false; - case EDataTypeRuleType.NUMBER_RANGE: - if ("from" in sourceRule.config && "from" in targetRule.config && - "to" in sourceRule.config && "to" in targetRule.config) { - return ( - sourceRule.config.from === targetRule.config.from && - sourceRule.config.to === targetRule.config.to && - ("step" in sourceRule.config ? sourceRule.config.step : undefined) === - ("step" in targetRule.config ? targetRule.config.step : undefined) - ); - } - return false; - case EDataTypeRuleType.ITEM_OF_COLLECTION: - if ("items" in sourceRule.config && "items" in targetRule.config) { - return Array.isArray(sourceRule.config.items) - && Array.isArray(targetRule.config.items) - && sourceRule.config.items.length === targetRule.config.items.length - && sourceRule.config.items.every((v: any, i: number) => v === (targetRule.config as DFlowDataTypeItemOfCollectionRuleConfig).items[i]); - } - return false; - default: - // Fallback: exact deep equality for other configs - return JSON.stringify(sourceRule.config) === JSON.stringify(targetRule.config); +function ruleMatches(sourceRule: DataTypeRule, targetRule: DataTypeRule): boolean { + if (sourceRule.variant !== targetRule.variant) return false; + + switch (targetRule.variant) { + case DataTypeRulesVariant.ContainsType: + case DataTypeRulesVariant.ReturnType: + return identifiersMatch( + (sourceRule.config as { dataTypeIdentifier?: DataTypeIdentifier }).dataTypeIdentifier, + (targetRule.config as { dataTypeIdentifier?: DataTypeIdentifier }).dataTypeIdentifier + ); + case DataTypeRulesVariant.ContainsKey: { + const sourceConfig = sourceRule.config as { key: string; dataTypeIdentifier?: DataTypeIdentifier }; + const targetConfig = targetRule.config as { key: string; dataTypeIdentifier?: DataTypeIdentifier }; + if (sourceConfig.key !== targetConfig.key) return false; + return identifiersMatch(sourceConfig.dataTypeIdentifier, targetConfig.dataTypeIdentifier); + } + case DataTypeRulesVariant.InputType: { + const sourceConfig = sourceRule.config as { inputTypes?: Array<{ dataTypeIdentifier: DataTypeIdentifier }> }; + const targetConfig = targetRule.config as { inputTypes?: Array<{ dataTypeIdentifier: DataTypeIdentifier }> }; + const targetInputTypes = targetConfig.inputTypes ?? []; + const sourceInputTypes = sourceConfig.inputTypes ?? []; + return targetInputTypes.every(targetInput => + sourceInputTypes.some(sourceInput => + identifiersMatch(sourceInput.dataTypeIdentifier, targetInput.dataTypeIdentifier) + ) + ); + } + case DataTypeRulesVariant.ItemOfCollection: { + const sourceItems = (sourceRule.config as { items?: unknown[] }).items ?? []; + const targetItems = (targetRule.config as { items?: unknown[] }).items ?? []; + if (sourceItems.length !== targetItems.length) return false; + return sourceItems.every((item, index) => item === targetItems[index]); } + case DataTypeRulesVariant.NumberRange: { + const sourceConfig = sourceRule.config as { from: number; to: number; steps?: number }; + const targetConfig = targetRule.config as { from: number; to: number; steps?: number }; + return ( + sourceConfig.from === targetConfig.from && + sourceConfig.to === targetConfig.to && + sourceConfig.steps === targetConfig.steps + ); + } + case DataTypeRulesVariant.Regex: { + const sourcePattern = (sourceRule.config as { pattern: string }).pattern; + const targetPattern = (targetRule.config as { pattern: string }).pattern; + return sourcePattern === targetPattern; + } + default: + return JSON.stringify(normalizeObjectForComparison(sourceRule.config)) === + JSON.stringify(normalizeObjectForComparison(targetRule.config)); } } -/** - * Checks if a source Type matches a target Type with the following semantics: - * - Only the structure (type and generic_mapper, etc.) is considered, not DataTypeObject metadata. - * - All values in the target must be matched by at least one corresponding value in the source. - * - "GENERIC" in the target matches anything in the source at the same location. - * - Recursively checks all nested types (e.g. in generic_mapper[].types). - * - Source can be more specific, target can be more generic (with "GENERIC"). - * - Arrays must match in order and length unless the target is "GENERIC". - * - * @param source Source Type (potentially more specific) - * @param target Target Type (potentially generic, e.g., contains "GENERIC" somewhere) - * @returns True if source matches all relevant constraints in target, otherwise false - */ export function isMatchingType( - source: Type, - target: Type + source: DataTypeIdentifier, + target: DataTypeIdentifier ): boolean { - // Helper: Recursively deep compare with GENERIC wildcard logic - function deepMatch(s: any, t: any): boolean { - // GENERIC wildcard: target accepts anything at this position - if (s === "GENERIC") return true; - - // Null/undefined check + const wildcard = (value: unknown): boolean => { + if (value === GENERIC_PLACEHOLDER) return true; + if (isDataTypeIdentifier(value) && value.genericKey) return true; + return false; + }; + + const deepMatch = (s: unknown, t: unknown): boolean => { + if (wildcard(t)) return true; if (s == null || t == null) return s === t; - // If both are arrays, match by length and recurse if (Array.isArray(t)) { if (!Array.isArray(s) || s.length !== t.length) return false; - return s.every((sElem, idx) => deepMatch(sElem, t[idx])); + return s.every((value, index) => deepMatch(value, t[index])); } - // If both are objects (but not arrays), compare all keys in target - if (typeof t === "object" && typeof s === "object") { - const tKeys = Object.keys(t); - for (const key of tKeys) { - // Only compare keys present in target (ignore extra keys in source) - if (!deepMatch(s[key], t[key])) return false; - } - return true; + if (isPlainObject(t)) { + if (!isPlainObject(s)) return false; + const keys = Object.keys(t); + return keys.every(key => deepMatch((s as Record)[key], (t as Record)[key])); } - // Primitive comparison return s === t; - } + }; - return deepMatch(source, target); + const normalizedSource = normalizeObjectForComparison(source); + const normalizedTarget = normalizeObjectForComparison(target); + return deepMatch(normalizedSource, normalizedTarget); } -/** - * Recursively expands all type aliases (e.g. "NUMBER_ARRAY" → ARRAY). - * Ensures every alias in the tree is fully expanded. - * - * @param type The Type or type alias to expand - * @param service Data type service (for lookup) - * @returns Deeply expanded Type object (with aliases replaced) - */ -export const resolveType = (type: Type, service: DFlowDataTypeReactiveService): Type => { - // Alias (string): try to expand via CONTAINS_TYPE recursively - if (typeof type === "string") { - const dt = service.getDataType(type) - if ( - dt && - dt.rules && - dt.rules.some(r => r.type === EDataTypeRuleType.CONTAINS_TYPE && "type" in r.config) - ) { - // Find most generic DataType with matching type and generics - const genericDT = service.values().find( - dt2 => dt2.type === dt.type && dt2.genericKeys && dt2.genericKeys.length > 0 - ) - if (genericDT && genericDT.genericKeys!!.length) { - const rule = dt.rules.find(r => r.type === EDataTypeRuleType.CONTAINS_TYPE && "type" in r.config) - if (rule) { - // Recursively expand inner type - //@ts-ignore - const expandedInner = resolveType(rule.config.type, service) - return { - type: genericDT.id, - generic_mapper: [{ - types: [expandedInner], - generic_target: "GENERIC" - }] +export const resolveType = ( + type: DataTypeIdentifier, + service: DFlowDataTypeReactiveService +): DataTypeIdentifier => { + if (typeof (type as unknown) === "string") { + const dataType = service.getDataType(type); + if (!dataType) return type; + const genericKeys = dataType.genericKeys ?? []; + + if (dataType.variant === DataTypeVariant.Array && genericKeys.length > 0) { + const innerTypeRule = dataType.rules?.nodes?.find(rule => rule?.variant === DataTypeRulesVariant.ContainsType); + const innerIdentifier = (innerTypeRule?.config as { dataTypeIdentifier?: DataTypeIdentifier })?.dataTypeIdentifier; + if (innerIdentifier) { + const [genericKey] = genericKeys; + if (!genericKey) return type; + return { + dataType: dataType.json as DataType, + genericType: { + dataType: dataType.json as DataType, + genericMappers: [ + { + target: genericKey, + sourceDataTypeIdentifiers: [resolveType(innerIdentifier, service)] + } + ] } - } + } as DataTypeIdentifier; } } - // Not an alias or cannot expand further - return type - } - // If already a GenericType object: expand all generic_mapper[].types recursively - if (typeof type === "object" && type !== null) { - const result: any = {} - for (const key of Object.keys(type)) { - if (key === "generic_mapper" && Array.isArray(type[key])) { - result[key] = type[key].map((gm: any) => ({ - ...gm, - types: Array.isArray(gm.types) - ? gm.types.map((t: any) => resolveType(t, service)) - : resolveType(gm.types, service), - generic_target: gm.generic_target // Normalize all generic_target to "GENERIC" - })) - } else if (key === "type") { - // Always expand nested type to primitive string (no nested objects) - if (typeof type[key] === "object") { - result[key] = (type[key] as any).type || type[key] - } else { - result[key] = type[key] - } - } else { - // Recursively handle all other fields - //@ts-ignore - result[key] = resolveType(type[key], service) - } - } - return result + + return type; } - // Primitive (number/boolean): just return as-is - return type -} -export const replaceGenericsAndSortType = ( - type: Type, - genericKeys?: string[] -): Type => { - function deepReplaceAndSort(node: any): any { - // 1. Replace generic keys if string (überall, nicht nur in generic_target) - if ( - typeof node === "string" && - Array.isArray(genericKeys) && - genericKeys.includes(node) - ) { - return "GENERIC"; + if (!isDataTypeIdentifier(type)) return type; + + if (!type.genericType) return type; + + const resolvedMappers = type.genericType.genericMappers?.map(mapper => ({ + ...mapper, + sources: mapper?.sourceDataTypeIdentifiers?.map(source => resolveType(source, service)) + })) ?? []; + + return { + ...type, + genericType: { + ...type.genericType, + genericMappers: resolvedMappers } + }; +}; - // 2. Array: Rekursiv, ggf. sortieren (für deterministische Ausgabe) - if (Array.isArray(node)) { - const mapped = node.map(deepReplaceAndSort); - - // Spezieller Fall: Array von Objekten mit generic_target → sortieren - if (mapped.length > 0 && typeof mapped[0] === "object" && mapped[0] !== null && "generic_target" in mapped[0]) { - // Sortierung: zuerst generic_target, dann types - return mapped.sort((a, b) => { - // generic_target ist jetzt immer "GENERIC" – also ggf. noch types vergleichen - const aTypes = JSON.stringify(a.types); - const bTypes = JSON.stringify(b.types); - if (aTypes < bTypes) return -1; - if (aTypes > bTypes) return 1; - return 0; - }); - } - // Auch primitive Arrays sortieren (z. B. ["FOOBAR", "MY_KEY"]) - if (typeof mapped[0] === "string" || typeof mapped[0] === "number") { - return [...mapped].sort(); - } - return mapped; +const sortValue = (value: unknown): unknown => { + if (Array.isArray(value)) { + const mapped = value.map(sortValue); + if (mapped.length <= 1) return mapped; + + const allPlainObjects = mapped.every(isPlainObject); + if (allPlainObjects) { + return [...mapped].sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b))); } - // 3. Object: keys sortieren, rekursiv ersetzen - if (typeof node === "object" && node !== null) { - const sortedKeys = Object.keys(node).sort(); - const result: any = {}; - for (const key of sortedKeys) { - // generic_target IMMER zu "GENERIC" - if (key === "generic_target") { - result[key] = "GENERIC"; - } else { - result[key] = deepReplaceAndSort(node[key]); - } - } - return result; + const allPrimitive = mapped.every(item => !Array.isArray(item) && !isPlainObject(item)); + if (allPrimitive) { + return [...mapped].sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b))); } - // 4. Primitives - return node; + + return mapped; } - return deepReplaceAndSort(type); -} + if (isPlainObject(value)) { + const recordValue = value as Record; + return Object.keys(recordValue) + .sort() + .reduce>((acc, key) => { + acc[key] = sortValue(recordValue[key]); + return acc; + }, {}); + } + + return value; +}; + +export const replaceGenericsAndSortType = ( + type: DataTypeIdentifier, + genericKeys: string[] = [] +): DataTypeIdentifier => { + const genericKeySet = new Set(genericKeys); + + const replaceIdentifier = (identifier: DataTypeIdentifier): DataTypeIdentifier => { + const genericKey = + typeof identifier.genericKey === "string" && genericKeySet.has(identifier.genericKey) + ? GENERIC_PLACEHOLDER + : identifier.genericKey; + + const genericType = identifier.genericType; + const replacedGenericType = genericType + ? (sortValue({ + ...genericType, + genericMappers: (genericType.genericMappers ?? []).map(mapper => { + const replacedMapper = { + ...mapper, + target: genericKeySet.has(mapper.target!!) + ? GENERIC_PLACEHOLDER + : mapper.target, + sources: mapper.sourceDataTypeIdentifiers?.map(source => replaceIdentifier(source)) + }; + + return sortValue(replacedMapper) as GenericMapper; + }) + }) as GenericType) + : genericType; + + return sortValue({ + ...identifier, + genericKey, + genericType: replacedGenericType + }) as DataTypeIdentifier; + }; + + return replaceIdentifier(type); +}; diff --git a/src/utils/inspection.ts b/src/utils/inspection.ts index e2184b6a..02f37d10 100644 --- a/src/utils/inspection.ts +++ b/src/utils/inspection.ts @@ -1,6 +1,6 @@ import {Translation} from "./translation"; -export const enum InspectionSeverity { +export enum InspectionSeverity { TYPO, GRAMMAR, WEAK, diff --git a/src/utils/reactiveArrayService.ts b/src/utils/reactiveArrayService.ts index c59c9bde..ccccfbcc 100644 --- a/src/utils/reactiveArrayService.ts +++ b/src/utils/reactiveArrayService.ts @@ -1,71 +1,90 @@ +// reactiveArrayService.ts import React from "react"; -import {ArrayService} from "./arrayService"; +import { ArrayService } from "./arrayService"; -/** - * using a React ref as a store in combination with a map - * to be able to access and modify the store easily - */ -export type ReactiveArrayStore = [T[], React.Dispatch>] +// Zugriffstyp: aktueller State via Getter, Updates via setState +export type ReactiveArrayStore = { + getState: () => T[]; + setState: React.Dispatch>; +}; export class ReactiveArrayService implements ArrayService { - - protected readonly store: ReactiveArrayStore - - constructor(store: ReactiveArrayStore) { - this.store = store + protected readonly access: ReactiveArrayStore; + constructor(access: ReactiveArrayStore) { + this.access = access; } - public delete(index: number) { - this.store[1](prevState => { - const newState = prevState.filter((value, index1) => index1 !== index); - return [ - ...newState, - ] - }) + delete(index: number) { + this.access.setState(prev => prev.filter((_, i) => i !== index)); } - public add(value: T) { - this.store[1](prevState => { - return [ - ...prevState, - value - ] - }) + add(value: T) { + this.access.setState(prev => [...prev, value]); } - public set(index: number, value: T) { - this.store[1](prevState => { - prevState[index] = value - return [ - ...prevState - ] - }) + set(index: number, value: T) { + this.access.setState(prev => { + const next = prev.slice(); + next[index] = value; + return next; + }); } - public has(index: number) { - return !!this.store[0][index] + has(index: number) { + const arr = this.access.getState(); + return index >= 0 && index < arr.length; } - public get(index: number) { - return this.store[0][index] + get(index: number) { + return this.access.getState()[index]; } - public values() { - return this.store[0] + values() { + return this.access.getState(); } - public update() { - this.store[1](prevState => [...prevState]) + update() { + this.access.setState(prev => prev.slice()); } - clear(): void { - this.store[1](_ => []) + clear() { + this.access.setState(() => []); } - } -// @ts-ignore -export const createReactiveArrayService = >(service: typeof T, callback?: (store: ReactiveArrayStore) => T, initial?: K[]): [K[] | undefined, T] => { - const store = React.useState(initial ?? []) - return [store[0], (callback ? callback(store) : new service(store)) as T] +// Hilfstyp für den Konstruktor des Service +type ArrayServiceCtor> = + new (access: ReactiveArrayStore) => S; + +// initial kann Array oder Callback sein +type InitialArg> = K[] | ((svc: S) => K[]); + +export function useReactiveArrayService>( + Ctor: ArrayServiceCtor, + initial: InitialArg = [] +): [K[], S] { + // 1) State + Ref + const [state, setState] = React.useState( + Array.isArray(initial) ? initial : [] // Platzhalter; Callback folgt in Effect + ); + const ref = React.useRef(state); + React.useEffect(() => { ref.current = state; }, [state]); + + // 2) stabiler Getter + const getState = React.useCallback(() => ref.current, []); + + // 3) Service erstellen (stabil über getState/setState) + const service = React.useMemo(() => new Ctor({ getState, setState }), [Ctor, getState, setState]); + + // 4) Falls initial ein Callback ist, einmalig ausführen, sobald Service existiert + React.useEffect(() => { + if (typeof initial === "function") { + // Nur einmal setzen (überschreibt ggf. den Platzhalter) + setState((initial as (svc: S) => K[])(service)); + } + // absichtlich keine Abhängigkeit auf `state` – nur einmal ausführen + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [service]); + + return [state, service]; } \ No newline at end of file