diff --git a/api/src/main/java/io/kafbat/ui/mapper/DynamicConfigMapper.java b/api/src/main/java/io/kafbat/ui/mapper/DynamicConfigMapper.java index 59861d49e..703b8a0dc 100644 --- a/api/src/main/java/io/kafbat/ui/mapper/DynamicConfigMapper.java +++ b/api/src/main/java/io/kafbat/ui/mapper/DynamicConfigMapper.java @@ -1,6 +1,7 @@ package io.kafbat.ui.mapper; import io.kafbat.ui.model.ActionDTO; +import io.kafbat.ui.model.ApplicationConfigPropertiesAuthOauth2ClientValueDTO; import io.kafbat.ui.model.ApplicationConfigPropertiesAuthOauth2ResourceServerDTO; import io.kafbat.ui.model.ApplicationConfigPropertiesAuthOauth2ResourceServerJwtDTO; import io.kafbat.ui.model.ApplicationConfigPropertiesAuthOauth2ResourceServerOpaquetokenDTO; diff --git a/api/src/test/java/io/kafbat/ui/service/mcp/McpSpecificationGeneratorTest.java b/api/src/test/java/io/kafbat/ui/service/mcp/McpSpecificationGeneratorTest.java index 6156a0bfa..6cbcf96c5 100644 --- a/api/src/test/java/io/kafbat/ui/service/mcp/McpSpecificationGeneratorTest.java +++ b/api/src/test/java/io/kafbat/ui/service/mcp/McpSpecificationGeneratorTest.java @@ -87,7 +87,7 @@ void testConvertController() { "clusterName", Map.of("type", "string"), "topicName", Map.of("type", "string"), "topicUpdate", SCHEMA_GENERATOR.generateSchema(TopicUpdateDTO.class) - ), List.of("clusterName", "topicName"), false, null, null) + ), List.of("clusterName", "topicName", "topicUpdate"), false, null, null) ) ); assertThat(tools).allMatch(tool -> diff --git a/build.gradle b/build.gradle index f999f4baa..deb45bcf0 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,7 @@ ext { release = resolveBooleanProperty("release") includeFrontend = resolveBooleanProperty("include-frontend", release) buildDockerImages = resolveBooleanProperty("build-docker-images", release) + useTypeSpec = resolveBooleanProperty("typespec", true) runE2e = resolveBooleanProperty("run-e2e") } diff --git a/contract-typespec/api/acls.tsp b/contract-typespec/api/acls.tsp new file mode 100644 index 000000000..6c009edd6 --- /dev/null +++ b/contract-typespec/api/acls.tsp @@ -0,0 +1,141 @@ +import "@typespec/openapi"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/clusters/{clusterName}/acls") +@tag("Acls") +interface AclApi { + @doc("listKafkaAcls") + @get + @operationId("listAcls") + listAcls( + @path clusterName: string, + @query resourceType?: KafkaAclResourceType, + @query resourceName?: string, + @query namePatternType?: KafkaAclNamePatternType, + ): KafkaAcl[]; + + @route("/csv") + @doc("getAclAsCsv") + @get + @operationId("getAclAsCsv") + getAclAsCsv(@path clusterName: string): string; + + @route("/csv") + @doc("syncAclsCsv") + @post + @operationId("syncAclsCsv") + syncAclsCsv(@path clusterName: string, @body content: string): void | ApiBadRequestResponse; + + @doc("createAcl") + @post + @operationId("createAcl") + createAcl(@path clusterName: string, @body acl: KafkaAcl): void | ApiBadRequestResponse; + + @doc("deleteAcl") + @delete + @operationId("deleteAcl") + deleteAcl( + @path clusterName: string, + @body acl: KafkaAcl, + ): void | ApiNotFoundResponse; + + @route("/consumer") + @doc("createConsumerAcl") + @post + @operationId("createConsumerAcl") + createConsumerAcl( + @path clusterName: string, + @body payload: CreateConsumerAcl, + ): void | ApiBadRequestResponse; + + @route("/producer") + @doc("createProducerAcl") + @operationId("createProducerAcl") + @post + createProducerAcl( + @path clusterName: string, + @body payload: CreateProducerAcl, + ): void | ApiBadRequestResponse; + + @route("/streamApp") + @doc("createStreamAppAcl") + @post + @operationId("createStreamAppAcl") + createStreamAppAcl( + @path clusterName: string, + @body payload: CreateStreamAppAcl, + ): void | ApiBadRequestResponse; +} + +model KafkaAcl { + resourceType: KafkaAclResourceType; + resourceName: string; // "*" if acl can be applied to any resource of given type + namePatternType: KafkaAclNamePatternType; + principal: string; + host: string; + operation: KafkaAclOpeations; + permission: "ALLOW" | "DENY"; +} + +alias KafkaAclOpeations = + "UNKNOWN" + | "ALL" + | "READ" + | "WRITE" + | "CREATE" + | "DELETE" + | "ALTER" + | "DESCRIBE" + | "CLUSTER_ACTION" + | "DESCRIBE_CONFIGS" + | "ALTER_CONFIGS" + | "IDEMPOTENT_WRITE" + | "CREATE_TOKENS" + | "DESCRIBE_TOKENS"; + +enum KafkaAclResourceType { + UNKNOWN, + TOPIC, + GROUP, + CLUSTER, + TRANSACTIONAL_ID, + DELEGATION_TOKEN, + USER, +} + +enum KafkaAclNamePatternType { + MATCH, + LITERAL, + PREFIXED, +} + +model CreateConsumerAcl { + principal?: string; + host?: string; + topics?: string[]; + topicsPrefix?: string; + consumerGroups?: string[]; + consumerGroupsPrefix?: string; +} + +model CreateProducerAcl { + principal?: string; + host?: string; + topics?: string[]; + topicsPrefix?: string; + transactionalId?: string; + transactionsIdPrefix?: string; + idempotent?: boolean = false; +} + +model CreateStreamAppAcl { + principal?: string; + host?: string; + inputTopics?: string[]; + outputTopics?: string[]; + applicationId?: string; +} diff --git a/contract-typespec/api/auth.tsp b/contract-typespec/api/auth.tsp new file mode 100644 index 000000000..599403839 --- /dev/null +++ b/contract-typespec/api/auth.tsp @@ -0,0 +1,46 @@ +import "@typespec/openapi"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@tag("Authorization") +interface AuthorizationApi { + @route("/api/authorization") + @doc("Get user authorization related info") + @operationId("getUserAuthInfo") + @get + getUserAuthInfo(): AuthenticationInfo; +} + +@route("/login") +@doc("Authenticate") +@operationId("authenticate") +@post +@tag("Unmapped") +op authenticate(@header contentType: "application/x-www-form-urlencoded", @body form: LoginForm): void | Http.Response<401>; + + +model LoginForm { + username?: string; + password?: string; +} + +model AuthenticationInfo { + rbacEnabled: boolean; // true if role based access control is enabled and granular permission access is required + userInfo?: UserInfo; +} + +model UserInfo { + username: string; + permissions: UserPermission[]; +} + + +model UserPermission { + clusters: string[]; + resource: ResourceType; + value?: string; + actions: Action[]; +} diff --git a/contract-typespec/api/brokers.tsp b/contract-typespec/api/brokers.tsp new file mode 100644 index 000000000..0f7442c1e --- /dev/null +++ b/contract-typespec/api/brokers.tsp @@ -0,0 +1,128 @@ +import "@typespec/openapi"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/clusters/{clusterName}/brokers") +@tag("Brokers") +interface BrokersApi { + @get + @operationId("getBrokers") + @doc("getBrokers") + getBrokers(@path clusterName: string): Broker[]; + + @get + @route("/{id}/configs") + @operationId("getBrokerConfig") + @doc("getBrokerConfig") + getBrokerConfig(@path clusterName: string, @path id: int32): BrokerConfig[]; + + @put + @route("/{id}/configs/{name}") + @operationId("updateBrokerConfigByName") + @doc("updateBrokerConfigByName") + updateBrokerConfigByName( + @path clusterName: string, + @path id: int32, + @path name: string, + @body config: BrokerConfigItem, + ): void | ApiBadRequestResponse; + + @get + @route("/{id}/metrics") + @operationId("getBrokersMetrics") + @doc("getBrokersMetrics") + getBrokersMetrics(@path clusterName: string, @path id: int32): BrokerMetrics; + + @get + @route("/logdirs") + @operationId("getAllBrokersLogdirs") + @doc("getAllBrokersLogdirs") + getAllBrokersLogdirs( + @path clusterName: string, + @query broker?: int32[], + ): BrokersLogdirs[]; + + @patch(#{implicitOptionality: true}) + @route("/{id}/logdirs") + @operationId("updateBrokerTopicPartitionLogDir") + @doc("updateBrokerTopicPartitionLogDir") + updateBrokerTopicPartitionLogDir( + @path clusterName: string, + @path id: int32, + @body update: BrokerLogdirUpdate, + ): void | ApiBadRequestResponse; +} + +model Broker { + id: int32; + host?: string; + port?: int32; + bytesInPerSec?: float64; + bytesOutPerSec?: float64; + partitionsLeader?: int32; + partitions?: int32; + inSyncPartitions?: int32; + partitionsSkew?: float64; + leadersSkew?: float64; +} + +model BrokerLogdirUpdate { + topic?: string; + partition?: int32; + logDir?: string; +} + +model BrokerConfig { + name: string; + value: string; + source: ConfigSource; + isSensitive: boolean; + isReadOnly: boolean; + synonyms?: ConfigSynonym[]; +} + +model BrokerConfigItem { + value?: string; +} + +model BrokerLogdirs { + name?: string; + error?: string; + topics?: TopicLogdirs[]; +} + +model TopicLogdirs { + name?: string; + partitions?: TopicPartitionLogdir[]; +} + +model BrokerMetrics { + segmentSize?: int64; + segmentCount?: int32; + metrics?: Metric[]; +} + +model BrokersLogdirs { + name?: string; + error?: string; + topics?: BrokerTopicLogdirs[]; +} + +model BrokerTopicLogdirs { + name?: string; + partitions?: BrokerTopicPartitionLogdir[]; +} + +model BrokerTopicPartitionLogdir { + ...TopicPartitionLogdir; + broker?: int32; +} + +model TopicPartitionLogdir { + partition?: int32; + size?: int64; + offsetLag?: int64; +} diff --git a/contract-typespec/api/clusters.tsp b/contract-typespec/api/clusters.tsp new file mode 100644 index 000000000..de7b95880 --- /dev/null +++ b/contract-typespec/api/clusters.tsp @@ -0,0 +1,97 @@ +import "@typespec/openapi"; +import "./responses.tsp"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/clusters") +@tag("Clusters") +interface ClustersApi { + @get + @operationId("getClusters") + @doc("getClusters") + getClusters(): Cluster[]; + + @post + @route("/{clusterName}/cache") + @operationId("updateClusterInfo") + @doc("updateClusterInfo") + updateClusterInfo(@path clusterName: string): Cluster; + + @get + @route("/{clusterName}/metrics") + @operationId("getClusterMetrics") + @doc("getClusterMetrics") + getClusterMetrics(@path clusterName: string): ClusterMetrics; + + @get + @route("/{clusterName}/stats") + @operationId("getClusterStats") + @doc("getClusterStats") + getClusterStats(@path clusterName: string): ClusterStats; +} + +model Cluster { + name: string; + defaultCluster?: boolean; + status: ServerStatus; + lastError?: MetricsCollectionError; + brokerCount?: int32; + onlinePartitionCount?: int32; + topicCount?: int32; + bytesInPerSec?: float64; + bytesOutPerSec?: float64; + readOnly?: boolean; + version?: string; + features?: ClusterFeature[]; +} + +alias ClusterFeature = + "SCHEMA_REGISTRY" + | "KAFKA_CONNECT" + | "KSQL_DB" + | "TOPIC_DELETION" + | "KAFKA_ACL_VIEW" + | "KAFKA_ACL_EDIT" + | "CLIENT_QUOTA_MANAGEMENT"; + +enum ServerStatus { + ONLINE, + OFFLINE, + INITIALIZING, +} + +model ClusterMetrics { + items: Metric[]; +} + +model ClusterStats { + brokerCount?: int32; + #deprecated "Unused" + zooKeeperStatus?: int32; + + @doc("Id of broker which is cluster's controller. null, if controller not known yet.") + activeControllers?: int32; + + onlinePartitionCount?: int32; + offlinePartitionCount?: int32; + inSyncReplicasCount?: int32; + outOfSyncReplicasCount?: int32; + underReplicatedPartitionCount?: int32; + diskUsage?: BrokerDiskUsage[]; + version?: string; +} + +model MetricsCollectionError { + message?: string; + stackTrace?: string; +} + + +model BrokerDiskUsage { + brokerId: int32; + segmentSize?: int64; + segmentCount?: int32; +} diff --git a/contract-typespec/api/config.tsp b/contract-typespec/api/config.tsp new file mode 100644 index 000000000..a228a810c --- /dev/null +++ b/contract-typespec/api/config.tsp @@ -0,0 +1,293 @@ +import "@typespec/openapi"; +import "./responses.tsp"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/info") +@doc("Gets application info") +@get +@tag("ApplicationConfig") +op getApplicationInfo(): ApplicationInfo; + +@route("/api/config") +@tag("ApplicationConfig") +interface ApplicationConfigApi { + @doc("Gets current application configuration") + @get + @operationId("getCurrentConfig") + getCurrentConfig(): ApplicationConfig; + + @put + @doc("Restarts application with specified configuration") + @operationId("restartWithConfig") + restartWithConfig(@body config: RestartRequest): void | ApiBadRequestResponse; + + @put + @route("/validated") + @doc("Restarts application with specified configuration") + @operationId("validateConfig") + validateConfig(@body config: ApplicationConfig): ApplicationConfigValidation | ApiBadRequestResponse; + + @post + @route("/relatedfiles") + @doc("Upload config related file") + @operationId("uploadConfigRelatedFile") + uploadConfigRelatedFile( + @multipartBody body: { + file: HttpPart; + } + ): UploadedFileInfo; + + @get + @route("/authentication") + @doc("Get authentication methods enabled for the app and other related settings") + @operationId("getAuthenticationSettings") + getAuthenticationSettings(): AppAuthenticationSettings; +} + + +model ApplicationInfo { + enabledFeatures?: ApplicationFeature[]; + build?: { + commitId?: string; + version?: string; + buildTime?: string; + isLatestRelease?: boolean; + }; + latestRelease?: { + versionTag?: string; + publishedAt?: string; + htmlUrl?: string; + }; +} + +model ApplicationConfig { + properties: { + auth?: { + type: string; + oauth2: { + client?: Record<{ + provider: string; + clientId: string; + clientSecret?: string; + clientName?: string; + redirectUri?: string; + authorizationGrantType?: string; + issuerUri?: string; + authorizationUri?: string; + tokenUri?: string; + userInfoUri?: string; + jwkSetUri?: string; + userNameAttribute?: string; + scope?: string[]; + customParams?: Record; + }>; + resourceServer?: { + jwt?: { + jwkSetUri?: string; + jwsAlgorithms?: string[]; + issuerUri?: string; + publicKeyLocation?: string; + audiences?: string[]; + authorityPrefix?: string; + authoritiesClaimDelimiter?: string; + authoritiesClaimName?: string; + principalClaimName?: string; + }; + opaquetoken?: { + clientId?: string; + clientSecret?: string; + introspectionUri?: string; + }; + }; + }; + }; + rbac?: { + roles?: { + name?: string; + clusters?: string[]; + subjects?: { + provider?: string; + type?: string; + value?: string; + regex?: boolean = false; + }[]; + permissions?: { + resource?: ResourceType; + value?: string; + actions?: Action[]; + }[]; + }[]; + }; + webclient?: { + maxInMemoryBufferSize?: string; + responseTimeoutMs?: int32; + }; + kafka?: { + polling?: { + pollTimeoutMs?: int32; + maxPageSize?: int32; + defaultPageSize?: int32; + responseTimeoutMs?: int32; + }; + adminClientTimeout?: int32; + internalTopicPrefix?: string; + clusters?: { + name?: string; + bootstrapServers?: string; + ssl?: { + truststoreLocation?: string; + truststorePassword?: string; + verifySsl?: boolean = true; + }; + schemaRegistry?: string; + schemaRegistryAuth?: { + username?: string; + password?: string; + }; + schemaRegistrySsl?: { + keystoreLocation?: string; + keystorePassword?: string; + }; + ksqldbServer?: string; + ksqldbServerSsl?: { + keystoreLocation?: string; + keystorePassword?: string; + }; + ksqldbServerAuth?: { + username?: string; + password?: string; + }; + kafkaConnect?: { + name?: string; + address?: string; + username?: string; + password?: string; + keystoreLocation?: string; + keystorePassword?: string; + }[]; + metrics?: { + type?: string; + port?: int32; + ssl?: boolean; + username?: string; + password?: string; + keystoreLocation?: string; + keystorePassword?: string; + }; + properties?: Record; + consumerProperties?: Record; + producerProperties?: Record; + readOnly?: boolean; + serde?: { + name?: string; + className?: string; + filePath?: string; + properties?: Record; + topicKeysPattern?: string; + topicValuesPattern?: string; + }[]; + defaultKeySerde?: string; + defaultValueSerde?: string; + masking?: { + type?: "REMOVE" | "MASK" | "REPLACE"; + fields?: string[]; + fieldsNamePattern?: string; + maskingCharsReplacement?: string[]; + replacement?: string; + topicKeysPattern?: string; + topicValuesPattern?: string; + }[]; + pollingThrottleRate?: int64; + audit?: { + level?: "ALL" | "ALTER_ONLY"; + topic?: string; + auditTopicsPartitions?: int32; + topicAuditEnabled?: boolean; + consoleAuditEnabled?: boolean; + auditTopicProperties?: Record; + }; + }[]; + }; + }; +} + +model ApplicationConfigValidation { + clusters?: Record; +} + +model ApplicationPropertyValidation { + error: boolean; + + @doc("Contains error message if error = true") + errorMessage?: string; +} + +model ClusterConfigValidation { + kafka: ApplicationPropertyValidation; + schemaRegistry?: ApplicationPropertyValidation; + kafkaConnects?: Record; + ksqldb?: ApplicationPropertyValidation; +} + +alias ApplicationFeature = + "DYNAMIC_CONFIG"; + +enum AuthType { + DISABLED, + OAUTH2, + LOGIN_FORM, + LDAP +} + +model AppAuthenticationSettings { + authType?: Api.AuthType; + oAuthProviders?: OAuthProvider[]; +} + +model OAuthProvider { + clientName: string; + authorizationUri: string; +} + +model RestartRequest { + config?: ApplicationConfig; +} + +model UploadedFileInfo { + location: string; +} + +enum Action { + ALL, + VIEW, + EDIT, + CREATE, + DELETE, + RESET_OFFSETS, + EXECUTE, + MODIFY_GLOBAL_COMPATIBILITY, + ANALYSIS_VIEW, + ANALYSIS_RUN, + MESSAGES_READ, + MESSAGES_PRODUCE, + MESSAGES_DELETE, + OPERATE, + RESTART, +} + +enum ResourceType { + APPLICATIONCONFIG, + CLUSTERCONFIG, + TOPIC, + CONSUMER, + SCHEMA, + CONNECT, + KSQL, + ACL, + AUDIT, + CLIENT_QUOTAS, +} diff --git a/contract-typespec/api/consumer-groups.tsp b/contract-typespec/api/consumer-groups.tsp new file mode 100644 index 000000000..49cb13284 --- /dev/null +++ b/contract-typespec/api/consumer-groups.tsp @@ -0,0 +1,141 @@ +import "@typespec/openapi"; +import "./responses.tsp"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/clusters/{clusterName}/consumer-groups") +@tag("Consumer Groups") +interface ConsumerGroupsApi { + @get + @route("/paged") + @operationId("getConsumerGroupsPage") + getConsumerGroupsPage( + @path clusterName: string, + @query page?: int32, + @query perPage?: int32, + @query search?: string, + @query orderBy?: ConsumerGroupOrdering, + @query sortOrder?: SortOrder, + ): ConsumerGroupsPageResponse; + + @get + @route("/{id}") + @operationId("getConsumerGroup") + getConsumerGroup( + @path clusterName: string, + @path id: string, + ): ConsumerGroupDetails; + + @delete + @route("/{id}") + @operationId("deleteConsumerGroup") + deleteConsumerGroup(@path clusterName: string, @path id: string): void; + + @post + @route("/{id}/offsets") + @operationId("resetConsumerGroupOffsets") + resetConsumerGroupOffsets( + @path clusterName: string, + @path id: string, + @body reset: ConsumerGroupOffsetsReset, + ): void; + + @delete + @route("/{id}/topics/{topicName}") + @operationId("deleteConsumerGroupOffsets") + deleteConsumerGroupOffsets( + @path clusterName: string, + @path id: string, + @path topicName: string, + ): void; +} + +@route("/api/clusters/{clusterName}/topics/{topicName}/consumer-groups") +@tag("Consumer Groups") +interface TopicConsumerGroupsApi { + @get + @operationId("getTopicConsumerGroups") + getTopicConsumerGroups( + @path clusterName: string, + @path topicName: string, + ): ConsumerGroup[]; +} + +enum ConsumerGroupState { + UNKNOWN, + PREPARING_REBALANCE, + COMPLETING_REBALANCE, + STABLE, + DEAD, + EMPTY, +} + +@discriminator("inherit") +model ConsumerGroup { + @invisible(Lifecycle) + inherit: string; + groupId: string; + members?: int32; + topics?: int32; + simple?: boolean; + partitionAssignor?: string; + state?: ConsumerGroupState; + coordinator?: Broker; + + @doc("null if consumer group has no offsets committed") + consumerLag?: int64; +} + + +model ConsumerGroupDetails extends ConsumerGroup { + @invisible(Lifecycle) + inherit: "details"; + partitions?: ConsumerGroupTopicPartition[]; +} + +enum ConsumerGroupOrdering { + NAME, + MEMBERS, + STATE, + MESSAGES_BEHIND, + TOPIC_NUM, +} + +model ConsumerGroupsPageResponse { + pageCount?: int32; + consumerGroups?: ConsumerGroup[]; +} + +model ConsumerGroupOffsetsReset { + topic: string; + resetType: ConsumerGroupOffsetsResetType; + partitions?: int32[]; + resetToTimestamp?: int64; + partitionsOffsets?: PartitionOffset[]; +} + +enum ConsumerGroupOffsetsResetType { + EARLIEST, + LATEST, + TIMESTAMP, + OFFSET, +} + +model ConsumerGroupTopicPartition { + topic: string; + partition: int32; + currentOffset?: int64; + endOffset?: int64; + consumerLag?: int64; // null if consumer group has no offsets committed + consumerId?: string; + host?: string; +} + +model PartitionOffset { + partition: int32; + offset?: int64; +} + diff --git a/contract-typespec/api/kafka-connect.tsp b/contract-typespec/api/kafka-connect.tsp new file mode 100644 index 000000000..d64219b82 --- /dev/null +++ b/contract-typespec/api/kafka-connect.tsp @@ -0,0 +1,286 @@ +import "@typespec/openapi"; +import "./responses.tsp"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/clusters/{clusterName}/connects") +@tag("Kafka Connect") +interface ConnectInstancesApi { + @get + @operationId("getConnects") + getConnects(@path clusterName: string): Connect[]; + + @get + @route("/{connectName}/plugins") + @doc("get connector plugins") + @operationId("getConnectorPlugins") + getConnectorPlugins( + @path clusterName: string, + @path connectName: string, + ): ConnectorPlugin[]; + + @put + @route("/{connectName}/plugins/{pluginName}/config/validate") + @doc("validate connector plugin configuration") + @operationId("validateConnectorPluginConfig") + validateConnectorPluginConfig( + @path clusterName: string, + @path connectName: string, + @path pluginName: string, + @body config: ConnectorConfig, + ): ConnectorPluginConfigValidationResponse; +} + +// /api/clusters/{clusterName}/connectors +@route("/api/clusters/{clusterName}/connectors") +@tag("Kafka Connect") +interface ConnectorsApi { + @get + @operationId("getAllConnectors") + getAllConnectors( + @path clusterName: string, + @query search?: string, + @query orderBy?: ConnectorColumnsToSort, + @query sortOrder?: SortOrder, + ): FullConnectorInfo[]; +} + +// /api/clusters/{clusterName}/connects/{connectName}/connectors +@route("/api/clusters/{clusterName}/connects/{connectName}/connectors") +@tag("Kafka Connect") +interface KafkaConnectConnectorsApi { + @get + @operationId("getConnectors") + getConnectors(@path clusterName: string, @path connectName: string): string[]; + + @post + @operationId("createConnector") + createConnector( + @path clusterName: string, + @path connectName: string, + @body connector: NewConnector, + ): Connector | ApiRebalanceInProgressResponse; + + @get + @route("/{connectorName}") + @operationId("getConnector") + getConnector( + @path clusterName: string, + @path connectName: string, + @path connectorName: string, + ): Connector; + + @delete + @route("/{connectorName}") + @operationId("deleteConnector") + deleteConnector( + @path clusterName: string, + @path connectName: string, + @path connectorName: string, + ): void | ApiRebalanceInProgressResponse; + + @post + @route("/{connectorName}/action/{action}") + @operationId("updateConnectorState") + updateConnectorState( + @path clusterName: string, + @path connectName: string, + @path connectorName: string, + @path action: ConnectorAction, + ): void | ApiRebalanceInProgressResponse | ApiBadRequestResponse; + + @get + @route("/{connectorName}/config") + @operationId("getConnectorConfig") + getConnectorConfig( + @path clusterName: string, + @path connectName: string, + @path connectorName: string, + ): ConnectorConfig; + + @put + @route("/{connectorName}/config") + @operationId("setConnectorConfig") + setConnectorConfig( + @path clusterName: string, + @path connectName: string, + @path connectorName: string, + @body config: ConnectorConfig, + ): Connector | ApiRebalanceInProgressResponse | ApiBadRequestResponse; + + @get + @route("/{connectorName}/tasks") + @operationId("getConnectorTasks") + getConnectorTasks( + @path clusterName: string, + @path connectName: string, + @path connectorName: string, + ): Task[]; + + @post + @route("/{connectorName}/tasks/{taskId}/action/restart") + @operationId("restartConnectorTask") + restartConnectorTask( + @path clusterName: string, + @path connectName: string, + @path connectorName: string, + @path taskId: int32, + ): void | ApiBadRequestResponse; + + @delete + @route("/{connectorName}/offsets") + @operationId("resetConnectorOffsets") + resetConnectorOffsets( + @path clusterName: string, + @path connectName: string, + @path connectorName: string, + ): void | ApiBadRequestResponse; +} + + +model Connect { + name: string; + address?: string; +} + +model ConnectorConfig is Record; + +model TaskId { + connector?: string; + task?: int32; +} + +model TaskStatus { + id: int32; + state: ConnectorTaskStatus; + worker_id: string; + trace?: string; +} + +model Task { + id?: TaskId; + status: TaskStatus; + config?: ConnectorConfig; +} + +model NewConnector { + name: string; + config: ConnectorConfig; +} + +enum ConnectorType { + SOURCE, + SINK, +} + +enum ConnectorTaskStatus { + RUNNING, + FAILED, + PAUSED, + RESTARTING, + UNASSIGNED, +} + +enum ConnectorState { + RUNNING, + FAILED, + PAUSED, + UNASSIGNED, + TASK_FAILED, + RESTARTING, + STOPPED, +} + +model ConnectorStatus { + state: ConnectorState; + worker_id?: string; +} + +model Connector { + ...NewConnector; + tasks?: TaskId[]; + type: ConnectorType; + status: ConnectorStatus; + connect: string; +} + +enum ConnectorAction { + RESTART, + RESTART_ALL_TASKS, + RESTART_FAILED_TASKS, + PAUSE, + RESUME, + STOP, +} + +enum TaskAction { + restart, +} + +model ConnectorPlugin { + class?: string; +} + +model ConnectorPluginConfigDefinition { + name?: string; + type?: + | "BOOLEAN" + | "CLASS" + | "DOUBLE" + | "INT" + | "LIST" + | "LONG" + | "PASSWORD" + | "SHORT" + | "STRING"; + required?: boolean; + default_value?: string; + importance?: "LOW" | "MEDIUM" | "HIGH"; + documentation?: string; + group?: string; + width?: "SHORT" | "MEDIUM" | "LONG" | "NONE"; + display_name?: string; + dependents?: string[]; + order?: int32; +} + +model ConnectorPluginConfigValue { + name?: string; + value?: string; + recommended_values?: string[]; + errors?: string[]; + visible?: boolean; +} + +model ConnectorPluginConfig { + definition?: ConnectorPluginConfigDefinition; + value?: ConnectorPluginConfigValue; +} + +model ConnectorPluginConfigValidationResponse { + name?: string; + error_count?: int32; + groups?: string[]; + configs?: ConnectorPluginConfig[]; +} + +model FullConnectorInfo { + connect: string; + name: string; + connector_class?: string; + type?: ConnectorType; + topics?: string[]; + status: ConnectorStatus; + tasks_count?: integer; + failed_tasks_count?: integer; +} + +enum ConnectorColumnsToSort { + NAME, + CONNECT, + TYPE, + STATUS, +} diff --git a/contract-typespec/api/ksql.tsp b/contract-typespec/api/ksql.tsp new file mode 100644 index 000000000..09375e881 --- /dev/null +++ b/contract-typespec/api/ksql.tsp @@ -0,0 +1,72 @@ +import "@typespec/openapi"; +import "./responses.tsp"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/clusters/{clusterName}/ksql") +@tag("Ksql") +interface KsqlApi { + @post + @route("/v2") + @operationId("executeKsql") + executeKsql( + @path clusterName: string, + @body command: KsqlCommandV2, + ): KsqlCommandV2Response | ApiBadRequestResponse; + + @get + @route("/tables") + @operationId("listTables") + listTables(@path clusterName: string): KsqlTableDescription[]; + + @get + @route("/streams") + @operationId("listStreams") + listStreams(@path clusterName: string): KsqlStreamDescription[]; + + @get + @route("/response") + @operationId("openKsqlResponsePipe") + openKsqlResponsePipe( + @path clusterName: string, + @query pipeId: string, + ): SseResponse; +} + + +model KsqlCommandV2 { + ksql: string; + streamsProperties?: Record; +} + +model KsqlCommandV2Response { + pipeId: string; +} + +model KsqlTableDescription { + name?: string; + topic?: string; + keyFormat?: string; + valueFormat?: string; + isWindowed?: boolean; +} + +model KsqlStreamDescription { + name?: string; + topic?: string; + keyFormat?: string; + valueFormat?: string; +} + +model KsqlTableResponse { + header?: string; + columnNames?: string[]; + values?: unknown[][]; +} + +model KsqlResponse { + table?: KsqlTableResponse; +} diff --git a/contract-typespec/api/main.tsp b/contract-typespec/api/main.tsp new file mode 100644 index 000000000..c0e566153 --- /dev/null +++ b/contract-typespec/api/main.tsp @@ -0,0 +1,31 @@ +// APIs +import "./clusters.tsp"; +import "./brokers.tsp"; +import "./topics.tsp"; +import "./messages.tsp"; +import "./consumer-groups.tsp"; +import "./schemas.tsp"; +import "./kafka-connect.tsp"; +import "./ksql.tsp"; +import "./acls.tsp"; +import "./quotas.tsp"; +import "./auth.tsp"; +import "./config.tsp"; + +import "@typespec/http"; +import "@typespec/rest"; +import "@typespec/openapi"; + +using TypeSpec.OpenAPI; +using Http; +using Rest; + +@service(#{ title: "Kafbat UI Api Service" }) +@info(#{ + contact: #{ name: "API Support", email: "support@kafbat.io" }, + license: #{ name: "Apache 2.0", url: "https://www.apache.org/licenses/LICENSE-2.0.html" }, + version: "0.2.0" +}) +@server("http://localhost:8080", "Default endpoint for Kafbat UI API") +namespace Api; + diff --git a/contract-typespec/api/messages.tsp b/contract-typespec/api/messages.tsp new file mode 100644 index 000000000..59dc23a84 --- /dev/null +++ b/contract-typespec/api/messages.tsp @@ -0,0 +1,234 @@ +import "@typespec/openapi"; +import "./models.tsp"; +import "./responses.tsp"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/clusters/{clusterName}/topics/{topicName}") +@tag("Messages") +interface MessagesApi { + @get + @route("/serdes") + @operationId("getSerdes") + getSerdes( + @path clusterName: string, + @path topicName: string, + @query use: SerdeUsage, + ): TopicSerdeSuggestion; + + @get + @route("/messages") + @operationId("getTopicMessages") + getTopicMessages( + @path clusterName: string, + @path topicName: string, + @query seekType?: SeekType, + @query seekTo?: string[], + @query limit?: int32, + @query q?: string, + @query filterQueryType?: MessageFilterType, + @query seekDirection?: SeekDirection, + @query keySerde?: string, + @query valueSerde?: string, + ): SseResponse; + + @delete + @route("/messages") + @operationId("deleteTopicMessages") + deleteTopicMessages( + @path clusterName: string, + @path topicName: string, + @query partitions?: int32[], + ): void | ApiNotFoundResponse | ApiBadRequestResponse; + + @post + @route("/messages") + @operationId("sendTopicMessages") + sendTopicMessages( + @path clusterName: string, + @path topicName: string, + @body message: CreateTopicMessage, + ): void | ApiNotFoundResponse | ApiBadRequestResponse; + + @post + @route("/smartfilters") + @operationId("registerFilter") + registerFilter( + @path clusterName: string, + @path topicName: string, + @body registration: MessageFilterRegistration, + ): MessageFilterId | ApiBadRequestResponse; + + @get + @route("/messages/v2") + @operationId("getTopicMessagesV2") + getTopicMessagesV2( + @path clusterName: string, + @path topicName: string, + @query mode?: PollingMode, + @query partitions?: int32[], + @query limit?: int32, + @query stringFilter?: string, + @query smartFilterId?: string, + @query offset?: int64, + @query timestamp?: int64, + @query keySerde?: string, + @query valueSerde?: string, + @query cursor?: string, + ): SseResponse | ApiBadRequestResponse; +} + +@route("/api/smartfilters/testexecutions") +@tag("Messages") +interface SmartFiltersTestExecutionsApi { + @put + @operationId("executeSmartFilterTest") + executeSmartFilterTest( + @body input: SmartFilterTestExecution, + ): SmartFilterTestExecutionResult | ApiBadRequestResponse; +} + +model TopicSerdeSuggestion { + key?: SerdeDescription[]; + value?: SerdeDescription[]; +} + +enum SerdeUsage { + SERIALIZE, + DESERIALIZE, +} + +model TopicMessageEvent { + type?: "PHASE" | "MESSAGE" | "CONSUMING" | "DONE"; + message?: TopicMessage; + phase?: TopicMessagePhase; + consuming?: TopicMessageConsuming; + cursor?: TopicMessageNextPageCursor; +} + +model TopicMessagePhase { + name?: string; +} + +model TimeStampFormat { + timeStampFormat?: string; +} + +model TopicMessageConsuming { + bytesConsumed?: int64; + elapsedMs?: int64; + isCancelled?: boolean; + messagesConsumed?: int32; + filterApplyErrors?: int32; +} + +model TopicMessageNextPageCursor { + id?: string; +} + +model TopicMessage { + partition: int32; + offset: int64; + timestamp: offsetDateTime; + timestampType?: "NO_TIMESTAMP_TYPE" | "CREATE_TIME" | "LOG_APPEND_TIME"; + key?: string; + headers?: Record; + value?: string; + #deprecated "use 'keySerde' field instead" + keyFormat?: MessageFormat; + #deprecated "use 'valueSerde' field instead" + valueFormat?: MessageFormat; + keySize?: int64; + valueSize?: int64; + #deprecated "use 'keyDeserializeProperties' field instead" + keySchemaId?: string; + #deprecated "use 'valueDeserializeProperties' field instead" + valueSchemaId?: string; + headersSize?: int64; + keySerde?: string; + valueSerde?: string; + keyDeserializeProperties?: Record; + valueDeserializeProperties?: Record; +} + +enum SeekType { + BEGINNING, + OFFSET, + TIMESTAMP, + LATEST, +} + +model MessageFilterRegistration { + filterCode?: string; +} + +model MessageFilterId { + id?: string; +} + +enum PollingMode { + FROM_OFFSET, + TO_OFFSET, + FROM_TIMESTAMP, + TO_TIMESTAMP, + LATEST, + EARLIEST, + TAILING, +} + +enum MessageFilterType { + STRING_CONTAINS, + CEL_SCRIPT, +} + +enum SeekDirection { + FORWARD, + BACKWARD, + TAILING, +} + +model SmartFilterTestExecution { + filterCode: string; + key?: string; + value?: string; + headers?: Record; + partition?: int32; + offset?: int64; + timestampMs?: int64; +} + +model SmartFilterTestExecutionResult { + result?: boolean; + error?: string; +} + +model CreateTopicMessage { + partition: int32; + key?: string | null; + headers?: Record; + value?: string | null; + keySerde?: string | null; + valueSerde?: string | null; +} + +enum MessageFormat { + AVRO, + JSON, + PROTOBUF, + UNKNOWN, +} + + +model SerdeDescription { + name?: string; + description?: string; + + @doc("This serde was automatically chosen by cluster config. This should be enabled in UI by default. Also it will be used for deserialization if no serdes passed.") + preferred?: boolean; + + schema?: string; + additionalProperties?: Record; +} diff --git a/contract-typespec/api/models.tsp b/contract-typespec/api/models.tsp new file mode 100644 index 000000000..ab4b590e7 --- /dev/null +++ b/contract-typespec/api/models.tsp @@ -0,0 +1,25 @@ +namespace Api; + +model Metric { + name?: string; + labels?: Record; + value?: decimal; +} + +enum ConfigSource { + DYNAMIC_TOPIC_CONFIG, + DYNAMIC_BROKER_LOGGER_CONFIG, + DYNAMIC_BROKER_CONFIG, + DYNAMIC_DEFAULT_BROKER_CONFIG, + DYNAMIC_CLIENT_METRICS_CONFIG, + STATIC_BROKER_CONFIG, + DEFAULT_CONFIG, + UNKNOWN, +} + +enum SortOrder { + ASC, + DESC, +} + + diff --git a/contract-typespec/api/package-lock.json b/contract-typespec/api/package-lock.json new file mode 100644 index 000000000..0cc016a26 --- /dev/null +++ b/contract-typespec/api/package-lock.json @@ -0,0 +1,1933 @@ +{ + "name": "tsp", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tsp", + "version": "0.1.0", + "dependencies": { + "@typespec/compiler": "^1.0.0", + "@typespec/http": "^1.0.1", + "@typespec/openapi": "^1.0.0", + "@typespec/openapi3": "^1.0.0", + "@typespec/rest": "^0.70.0", + "@typespec/sse": "^0.70.0", + "build": "^0.1.4" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.2.tgz", + "integrity": "sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==", + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "license": "MIT" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.1.tgz", + "integrity": "sha512-u/kozRnsPO/x8QtKYJOqoGtC4kH6yg1lfYkB9Au0WhYB0FNLpyFusttQtvhlwjtG3rOwiRz4D8DnnXa8iEpIKA==", + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "11.7.2", + "@apidevtools/openapi-schemas": "^2.1.0", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.2" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.8.tgz", + "integrity": "sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.13", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.12.tgz", + "integrity": "sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.1.13", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.13.tgz", + "integrity": "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==", + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.13.tgz", + "integrity": "sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.15.tgz", + "integrity": "sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", + "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.12.tgz", + "integrity": "sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.15.tgz", + "integrity": "sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.15.tgz", + "integrity": "sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.5.3.tgz", + "integrity": "sha512-8YL0WiV7J86hVAxrh3fE5mDCzcTDe1670unmJRz6ArDgN+DBK1a0+rbnNWp4DUB5rPMwqD5ZP6YHl9KK1mbZRg==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.8", + "@inquirer/confirm": "^5.1.12", + "@inquirer/editor": "^4.2.13", + "@inquirer/expand": "^4.0.15", + "@inquirer/input": "^4.1.12", + "@inquirer/number": "^3.0.15", + "@inquirer/password": "^4.0.15", + "@inquirer/rawlist": "^4.1.3", + "@inquirer/search": "^3.0.15", + "@inquirer/select": "^4.2.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.3.tgz", + "integrity": "sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.13", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.15.tgz", + "integrity": "sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.13", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.3.tgz", + "integrity": "sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.13", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", + "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/@typespec/asset-emitter": { + "version": "0.70.1", + "resolved": "https://registry.npmjs.org/@typespec/asset-emitter/-/asset-emitter-0.70.1.tgz", + "integrity": "sha512-X8hRA7LLWkNIWqAkWaWoa84PzDMUvjj3qCLQKT29k5twS419nN1GGT7BQaDQnYfPTsSssEFxgRgugr0AAErEsA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.0.0" + } + }, + "node_modules/@typespec/compiler": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.0.0.tgz", + "integrity": "sha512-QFy0otaB4xkN4kQmYyT17yu3OVhN0gti9+EKnZqs5JFylw2Xecx22BPwUE1Byj42pZYg5d9WlO+WwmY5ALtRDg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.26.2", + "@inquirer/prompts": "^7.4.0", + "ajv": "~8.17.1", + "change-case": "~5.4.4", + "env-paths": "^3.0.0", + "globby": "~14.1.0", + "is-unicode-supported": "^2.1.0", + "mustache": "~4.2.0", + "picocolors": "~1.1.1", + "prettier": "~3.5.3", + "semver": "^7.7.1", + "tar": "^7.4.3", + "temporal-polyfill": "^0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.12", + "yaml": "~2.7.0", + "yargs": "~17.7.2" + }, + "bin": { + "tsp": "cmd/tsp.js", + "tsp-server": "cmd/tsp-server.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@typespec/events": { + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.70.0.tgz", + "integrity": "sha512-qHW1N05n8PkNf2YQGNMdl/sAYqrJv+zQ1kny+3vg/20nzVj7sZpNFIKqUIc11z0GkT7k3Q9SPTymvq+K00sAUg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.0.0" + } + }, + "node_modules/@typespec/http": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.0.1.tgz", + "integrity": "sha512-J5tqBWlmkvI/W+kJn4EFuN0laGxbY8qT68jzEQEiYeAXSfNyFGRSoCwn8Ex6dJphq4IozOMdVTNtOZWIJlwmfw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.0.0", + "@typespec/streams": "^0.70.0" + }, + "peerDependenciesMeta": { + "@typespec/streams": { + "optional": true + } + } + }, + "node_modules/@typespec/openapi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.0.0.tgz", + "integrity": "sha512-pONzKIdK4wHgD1vBfD9opUk66zDG55DlHbueKOldH2p1LVf5FnMiuKE4kW0pl1dokT/HBNR5OJciCzzVf44AgQ==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.0.0", + "@typespec/http": "^1.0.0" + } + }, + "node_modules/@typespec/openapi3": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-1.0.0.tgz", + "integrity": "sha512-cDsnNtJkQCx0R/+9AqXzqAKH6CgtwmnQGQMQHbkw0/Sxs5uk6hoiexx7vz0DUR7H4492MqPT2kE4351KZbDYMw==", + "license": "MIT", + "dependencies": { + "@apidevtools/swagger-parser": "~10.1.1", + "@typespec/asset-emitter": "^0.70.0", + "openapi-types": "~12.1.3", + "yaml": "~2.7.0" + }, + "bin": { + "tsp-openapi3": "cmd/tsp-openapi3.js" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.0.0", + "@typespec/http": "^1.0.0", + "@typespec/json-schema": "^1.0.0", + "@typespec/openapi": "^1.0.0", + "@typespec/versioning": "^0.70.0" + }, + "peerDependenciesMeta": { + "@typespec/json-schema": { + "optional": true + }, + "@typespec/versioning": { + "optional": true + }, + "@typespec/xml": { + "optional": true + } + } + }, + "node_modules/@typespec/rest": { + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.70.0.tgz", + "integrity": "sha512-pn3roMQV6jBNT4bVA/hnrBAAHleXSyfWQqNO+DhI3+tLU4jCrJHmUZDi82nI9xBl+jkmy2WZFZOelZA9PSABeg==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.0.0", + "@typespec/http": "^1.0.0" + } + }, + "node_modules/@typespec/sse": { + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.70.0.tgz", + "integrity": "sha512-11VsIRqPuK+bIq7gHVghM5CAqvcfe9TmL9mZkxlPKuV6RRWju831k18KqlwXTOgeEMwVGA1Xbg1TTi1F4S1B+w==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.0.0", + "@typespec/events": "^0.70.0", + "@typespec/http": "^1.0.0", + "@typespec/streams": "^0.70.0" + } + }, + "node_modules/@typespec/streams": { + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.70.0.tgz", + "integrity": "sha512-WIixoZ7CCLq2INX4UkN+aXlj07Je+ntW0xbeFGmpfq6Z2xifKnL6/sPiztURMXd4Z1I+XXFCn2pw1r9q5i4Cmw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.0.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ansi-styles/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/build": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/build/-/build-0.1.4.tgz", + "integrity": "sha512-KwbDJ/zrsU8KZRRMfoURG14cKIAStUlS8D5jBDvtrZbwO5FEkYqc3oB8HIhRiyD64A48w1lc+sOmQ+mmBw5U/Q==", + "dependencies": { + "cssmin": "0.3.x", + "jsmin": "1.x", + "jxLoader": "*", + "moo-server": "*", + "promised-io": "*", + "timespan": "2.x", + "uglify-js": "1.x", + "walker": "1.x", + "winston": "*", + "wrench": "1.3.x" + }, + "engines": { + "node": ">v0.4.12" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "license": "MIT" + }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "license": "MIT" + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "license": "MIT" + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "license": "MIT", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/cssmin": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/cssmin/-/cssmin-0.3.2.tgz", + "integrity": "sha512-bynxGIAJ8ybrnFobjsQotIjA8HFDDgPwbeUWNXXXfR+B4f9kkxdcUyagJoQCSUOfMV+ZZ6bMn8bvbozlCzUGwQ==", + "bin": { + "cssmin": "bin/cssmin" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsmin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jsmin/-/jsmin-1.0.1.tgz", + "integrity": "sha512-OPuL5X/bFKgVdMvEIX3hnpx3jbVpFCrEM8pKPXjFkZUqg521r41ijdyTz7vACOhW6o1neVlcLyd+wkbK5fNHRg==", + "license": "Doug Crockford's license that allows this module to be used for Good but not for Evil", + "bin": { + "jsmin": "bin/jsmin" + }, + "engines": { + "node": ">=0.1.93" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/jxLoader": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jxLoader/-/jxLoader-0.1.1.tgz", + "integrity": "sha512-ClEvAj3K68y8uKhub3RgTmcRPo5DfIWvtxqrKQdDPyZ1UVHIIKvVvjrAsJFSVL5wjv0rt5iH9SMCZ0XRKNzeUA==", + "dependencies": { + "js-yaml": "0.3.x", + "moo-server": "1.3.x", + "promised-io": "*", + "walker": "1.x" + }, + "engines": { + "node": ">v0.4.10" + } + }, + "node_modules/jxLoader/node_modules/js-yaml": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-0.3.7.tgz", + "integrity": "sha512-/7PsVDNP2tVe2Z1cF9kTEkjamIwz4aooDpRKmN1+g/9eePCgcxsv4QDvEbxO0EH+gdDD7MLyDoR6BASo3hH51g==", + "license": "MIT", + "engines": { + "node": "> 0.4.11" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/moo-server": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/moo-server/-/moo-server-1.3.0.tgz", + "integrity": "sha512-9A8/eor2DXwpv1+a4pZAAydqLFVrWoKoO1fzdzqLUhYVXAO1Kgd1FR2gFZi7YdHzF0s4W8cDNwCfKJQrvLqxDw==", + "engines": { + "node": ">v0.4.10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT" + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/promised-io": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/promised-io/-/promised-io-0.3.6.tgz", + "integrity": "sha512-bNwZusuNIW4m0SPR8jooSyndD35ggirHlxVl/UhIaZD/F0OBv9ebfc6tNmbpZts3QXHggkjIBH8lvtnzhtcz0A==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/temporal-polyfill": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.3.0.tgz", + "integrity": "sha512-qNsTkX9K8hi+FHDfHmf22e/OGuXmfBm9RqNismxBrnSmZVJKegQ+HYYXT+R7Ha8F/YSm2Y34vmzD4cxMu2u95g==", + "license": "MIT", + "dependencies": { + "temporal-spec": "0.3.0" + } + }, + "node_modules/temporal-spec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.3.0.tgz", + "integrity": "sha512-n+noVpIqz4hYgFSMOSiINNOUOMFtV5cZQNCmmszA6GiVFVRt3G7AqVyhXjhCSmowvQn+NsGn+jMDMKJYHd3bSQ==", + "license": "ISC" + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/timespan": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", + "integrity": "sha512-0Jq9+58T2wbOyLth0EU+AUb6JMGCLaTWIykJFa7hyAybjVH9gpVMTfUAwo5fWAvtFt2Tjh/Elg8JtgNpnMnM8g==", + "engines": { + "node": ">= 0.2.0" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/uglify-js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-1.3.5.tgz", + "integrity": "sha512-YPX1DjKtom8l9XslmPFQnqWzTBkvI4N0pbkzLuPZZ4QTyig0uQqvZz9NgUdfEV+qccJzi7fVcGWdESvRIjWptQ==", + "bin": { + "uglifyjs": "bin/uglifyjs" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrench": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.3.9.tgz", + "integrity": "sha512-srTJQmLTP5YtW+F5zDuqjMEZqLLr/eJOZfDI5ibfPfRMeDh3oBUefAscuH0q5wBKE339ptH/S/0D18ZkfOfmKQ==", + "deprecated": "wrench.js is deprecated! You should check out fs-extra (https://github.com/jprichardson/node-fs-extra) for any operations you were using wrench for. Thanks for all the usage over the years.", + "engines": { + "node": ">=0.1.97" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/contract-typespec/api/package.json b/contract-typespec/api/package.json new file mode 100644 index 000000000..d67b17407 --- /dev/null +++ b/contract-typespec/api/package.json @@ -0,0 +1,18 @@ +{ + "name": "tsp", + "version": "0.1.0", + "type": "module", + "dependencies": { + "@typespec/compiler": "^1.0.0", + "@typespec/http": "^1.0.1", + "@typespec/openapi": "^1.0.0", + "@typespec/openapi3": "^1.0.0", + "@typespec/rest": "^0.70.0", + "@typespec/sse": "^0.70.0", + "build": "^0.1.4" + }, + "scripts": { + "build": "tsp compile ." + }, + "private": true +} diff --git a/contract-typespec/api/quotas.tsp b/contract-typespec/api/quotas.tsp new file mode 100644 index 000000000..ba94c4e5b --- /dev/null +++ b/contract-typespec/api/quotas.tsp @@ -0,0 +1,32 @@ +import "@typespec/openapi"; +import "./models.tsp"; +import "./responses.tsp"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/clusters/{clusterName}/clientquotas") +@tag("ClientQuotas") +interface QuoatsApi { + @get + @doc("listQuotas") + @operationId("listQuotas") + listQuotas(@path clusterName: string): ClientQuotas[]; + + @post + @doc("upsertClientQuotas") + @operationId("upsertClientQuotas") + upsertClientQuotas( + @path clusterName: string, + @body quota: ClientQuotas, + ): void | ApiBadRequestResponse; +} + +model ClientQuotas { + user?: string; + clientId?: string; + ip?: string; + quotas?: Record; +} diff --git a/contract-typespec/api/responses.tsp b/contract-typespec/api/responses.tsp new file mode 100644 index 000000000..c784aff70 --- /dev/null +++ b/contract-typespec/api/responses.tsp @@ -0,0 +1,72 @@ +import "@typespec/http"; +import "@typespec/http/streams"; + +namespace Api; + +using TypeSpec.Http; +using TypeSpec.Streams; + +model ApiCreatedResponse is Response<201> { + @body + body: Model; +} + +model SseResponse is Stream { + @header contentType: "text/event-stream"; + @body body: Model[]; +} + +// ----- Error Responses ----- +model ApiNotFoundResponse is Response<404> { + message: string; +} + +model ApiTimeoutResponse is Response<408> { + message: string; +} + +model ApiDuplicateResponse is Response<409> { + message: string; +} + +model ApiRebalanceInProgressResponse is Response<409> { + message: string; +} + +model ApiInvalidParametersResponse is Response<422> { + message: string; +} + +model ApiBadRequestResponse is Response<400> { + @body + message: ErrorResponse; +} + +model ApiUnauthorized is Response<401> { + message: string; +} + +model ErrorResponse { + @doc("Internal error code (can be used for message formatting & localization on UI)") + code: int32; + + @doc("Error message") + message: string; + + @doc("Response unix timestamp in ms") + timestamp: decimal; + + @doc("Unique server-defined request id for convenient debugging") + requestId: string; + + fieldsErrors?: FieldError[]; + stackTrace?: string; +} + +model FieldError { + @doc("Name of field that violated format") + fieldName: string; + + @doc("Field format violations description (ex. [\"size must be between 0 and 20\", \"must be a well-formed email address\"])") + restrictions?: string[]; +} diff --git a/contract-typespec/api/schemas.tsp b/contract-typespec/api/schemas.tsp new file mode 100644 index 000000000..a935edddc --- /dev/null +++ b/contract-typespec/api/schemas.tsp @@ -0,0 +1,162 @@ +import "@typespec/openapi"; +import "./responses.tsp"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/clusters/{clusterName}/schemas") +@tag("Schemas") +interface SchemasApi { + @post + @operationId("createNewSchema") + createNewSchema(@path clusterName: string, @body input: NewSchemaSubject): + | SchemaSubject + | ApiBadRequestResponse + | ApiDuplicateResponse + | ApiInvalidParametersResponse; + + @get + @operationId("getSchemas") + getSchemas( + @path clusterName: string, + @query page?: int32, + @query perPage?: int32, + @query search?: string, + ): SchemaSubjectsResponse; + + @delete + @route("/{subject}") + @operationId("deleteSchema") + deleteSchema( + @path clusterName: string, + @path subject: string, + ): void | ApiNotFoundResponse; + + @get + @route("/{subject}/versions") + @operationId("getAllVersionsBySubject") + getAllVersionsBySubject( + @path clusterName: string, + @path subject: string, + ): SchemaSubject[]; + + @get + @route("/{subject}/latest") + @operationId("getLatestSchema") + getLatestSchema( + @path clusterName: string, + @path subject: string, + ): SchemaSubject; + + @delete + @route("/{subject}/latest") + @operationId("deleteLatestSchema") + deleteLatestSchema( + @path clusterName: string, + @path subject: string, + ): void | ApiNotFoundResponse; + + @get + @route("/{subject}/versions/{version}") + @operationId("getSchemaByVersion") + getSchemaByVersion( + @path clusterName: string, + @path subject: string, + @path version: int32, + ): SchemaSubject; + + @delete + @route("/{subject}/versions/{version}") + @operationId("deleteSchemaByVersion") + deleteSchemaByVersion( + @path clusterName: string, + @path subject: string, + @path version: int32, + ): void | ApiNotFoundResponse; + + @get + @route("/compatibility") + @operationId("getGlobalSchemaCompatibilityLevel") + getGlobalSchemaCompatibilityLevel( + @path clusterName: string, + ): CompatibilityLevel; + + @put + @route("/compatibility") + @operationId("updateGlobalSchemaCompatibilityLevel") + updateGlobalSchemaCompatibilityLevel( + @path clusterName: string, + @body level: CompatibilityLevel, + ): void | ApiNotFoundResponse | ApiBadRequestResponse; + + @put + @route("/{subject}/compatibility") + @operationId("updateSchemaCompatibilityLevel") + updateSchemaCompatibilityLevel( + @path clusterName: string, + @path subject: string, + @body level: CompatibilityLevel, + ): void | ApiNotFoundResponse | ApiBadRequestResponse; + + @post + @route("/{subject}/check") + @operationId("checkSchemaCompatibility") + checkSchemaCompatibility( + @path clusterName: string, + @path subject: string, + @body input: NewSchemaSubject, + ): CompatibilityCheckResponse | ApiNotFoundResponse; +} + +model SchemaReference { + name: string; + subject: string; + version: int32; +} + +enum SchemaType { + AVRO, + JSON, + PROTOBUF, +} + +model SchemaSubject { + id: int32; + subject: string; + version: string; + schema: string; + compatibilityLevel: string; + schemaType: SchemaType; + references?: SchemaReference[]; +} + +model NewSchemaSubject { + @doc("should be set for creating/updating schema subject") + subject: string; + + schema: string; + schemaType: SchemaType; + references?: SchemaReference[]; +} + +model CompatibilityLevel { + compatibility: + | "BACKWARD" + | "BACKWARD_TRANSITIVE" + | "FORWARD" + | "FORWARD_TRANSITIVE" + | "FULL" + | "FULL_TRANSITIVE" + | "NONE"; +} + +model CompatibilityCheckResponse { + isCompatible: boolean; +} + +model SchemaSubjectsResponse { + pageCount?: int32; + schemas?: SchemaSubject[]; +} diff --git a/contract-typespec/api/topics.tsp b/contract-typespec/api/topics.tsp new file mode 100644 index 000000000..8a50e46f6 --- /dev/null +++ b/contract-typespec/api/topics.tsp @@ -0,0 +1,311 @@ +import "@typespec/openapi"; +import "./responses.tsp"; + +namespace Api; + +using TypeSpec.Http; +using OpenAPI; + +@route("/api/clusters/{clusterName}/topics") +@tag("Topics") +interface TopicsApi { + @get + @operationId("getTopics") + getTopics( + @path clusterName: string, + @query page?: int32, + @query perPage?: int32, + @query showInternal?: boolean, + @query search?: string, + @query orderBy?: TopicColumnsToSort, + @query sortOrder?: SortOrder, + ): TopicsResponse; + + @post + @operationId("createTopic") + createTopic( + @path clusterName: string, + @body topic: TopicCreation, + ): ApiCreatedResponse; + + @post + @route("/{topicName}/clone") + @operationId("cloneTopic") + cloneTopic( + @path clusterName: string, + @path topicName: string, + @query newTopicName: string, + ): ApiCreatedResponse | ApiNotFoundResponse; + + @get + @route("/{topicName}/analysis") + @operationId("getTopicAnalysis") + getTopicAnalysis( + @path clusterName: string, + @path topicName: string, + ): TopicAnalysis | ApiNotFoundResponse; + + @post + @route("/{topicName}/analysis") + @operationId("analyzeTopic") + analyzeTopic( + @path clusterName: string, + @path topicName: string, + ): void | ApiNotFoundResponse; + + @delete + @route("/{topicName}/analysis") + @operationId("cancelTopicAnalysis") + cancelTopicAnalysis( + @path clusterName: string, + @path topicName: string, + ): void | ApiNotFoundResponse; + + @get + @route("/{topicName}") + @operationId("getTopicDetails") + getTopicDetails( + @path clusterName: string, + @path topicName: string, + ): TopicDetails; + + @post + @route("/{topicName}") + @operationId("recreateTopic") + recreateTopic(@path clusterName: string, @path topicName: string): + | ApiCreatedResponse + | ApiTimeoutResponse + | ApiNotFoundResponse + | ApiBadRequestResponse; + + @patch(#{implicitOptionality: true}) + @route("/{topicName}") + @operationId("updateTopic") + updateTopic( + @path clusterName: string, + @path topicName: string, + @body update: TopicUpdate, + ): Topic; + + @delete + @route("/{topicName}") + @operationId("deleteTopic") + deleteTopic( + @path clusterName: string, + @path topicName: string, + ): void | NotFoundResponse; + + @get + @route("/{topicName}/config") + @operationId("getTopicConfigs") + getTopicConfigs( + @path clusterName: string, + @path topicName: string, + ): TopicConfig[]; + + @patch(#{ implicitOptionality: true }) + @route("/{topicName}/replications") + @operationId("changeReplicationFactor") + changeReplicationFactor( + @path clusterName: string, + @path topicName: string, + @body change: ReplicationFactorChange, + ): ReplicationFactorChangeResponse | ApiNotFoundResponse | ApiBadRequestResponse; + + @get + @route("/{topicName}/activeproducers") + @operationId("getActiveProducerStates") + getActiveProducerStates( + @path clusterName: string, + @path topicName: string, + ): TopicProducerState[]; + + @patch(#{ implicitOptionality: true }) + @route("/{topicName}/partitions") + @doc("increaseTopicPartitions") + @operationId("increaseTopicPartitions") + increaseTopicPartitions( + @path clusterName: string, + @path topicName: string, + @body partitionsIncrease: PartitionsIncrease, + ): PartitionsIncreaseResponse | ApiNotFoundResponse; +} + +model TopicsResponse { + pageCount?: int32; + topics?: Topic[]; +} + +enum TopicColumnsToSort { + NAME, + OUT_OF_SYNC_REPLICAS, + TOTAL_PARTITIONS, + REPLICATION_FACTOR, + SIZE, +} + +model Topic { + name: string; + internal?: boolean; + partitionCount?: int32; + replicationFactor?: int32; + replicas?: int32; + inSyncReplicas?: int32; + segmentSize?: int64; + segmentCount?: int32; + bytesInPerSec?: float64; + bytesOutPerSec?: float64; + underReplicatedPartitions?: int32; + cleanUpPolicy?: CleanUpPolicy; + partitions?: Partition[]; +} + +model TopicUpdate { + configs: Record; +} + +model TopicAnalysis { + progress?: TopicAnalysisProgress; + result?: TopicAnalysisResult; +} + +model TopicAnalysisProgress { + startedAt?: int64; + completenessPercent?: decimal; + msgsScanned?: int64; + bytesScanned?: int64; +} + +model TopicAnalysisResult { + startedAt?: int64; + finishedAt?: int64; + error?: string; + totalStats?: TopicAnalysisStats; + partitionStats?: TopicAnalysisStats[]; +} + +model TopicAnalysisStats { + @doc("null if this is total stats") + partition?: int32; + + totalMsgs?: int64; + minOffset?: int64; + maxOffset?: int64; + minTimestamp?: int64; + maxTimestamp?: int64; + nullKeys?: int64; + nullValues?: int64; + approxUniqKeys?: int64; + approxUniqValues?: int64; + keySize?: TopicAnalysisSizeStats; + valueSize?: TopicAnalysisSizeStats; + hourlyMsgCounts?: { + hourStart?: int64; + count?: int64; + }[]; +} + +model TopicAnalysisSizeStats { + sum?: int64; + min?: int64; + max?: int64; + avg?: int64; + prctl50?: int64; + prctl75?: int64; + prctl95?: int64; + prctl99?: int64; + prctl999?: int64; +} + +model TopicProducerState { + partition?: int32; + producerId?: int64; + producerEpoch?: int32; + lastSequence?: int32; + lastTimestampMs?: int64; + coordinatorEpoch?: int32; + currentTransactionStartOffset?: int64; +} + +model TopicDetails { + name: string; + internal?: boolean; + partitions?: Partition[]; + partitionCount?: int32; + replicationFactor?: int32; + replicas?: int32; + inSyncReplicas?: int32; + bytesInPerSec?: float64; + bytesOutPerSec?: float64; + segmentSize?: int64; + segmentCount?: int32; + underReplicatedPartitions?: int32; + cleanUpPolicy?: CleanUpPolicy; + keySerde?: string; + valueSerde?: string; +} + +model TopicConfig { + name: string; + value?: string; + defaultValue?: string; + source?: ConfigSource; + isSensitive?: boolean; + isReadOnly?: boolean; + synonyms?: ConfigSynonym[]; + doc?: string; +} + +model TopicCreation { + name: string; + partitions: int32; + replicationFactor?: int32; + configs?: Record; +} + +enum CleanUpPolicy { + DELETE, + COMPACT, + COMPACT_DELETE, + UNKNOWN, +} + +model ConfigSynonym { + name?: string; + value?: string; + source?: ConfigSource; +} + +model Partition { + partition: int32; + leader?: int32; + replicas?: Replica[]; + offsetMax: int64; + offsetMin: int64; +} + +model PartitionsIncrease { + @minValue(1) + totalPartitionsCount: integer; +} + +model PartitionsIncreaseResponse { + totalPartitionsCount: integer; + topicName: string; +} + + +model ReplicationFactorChange { + totalReplicationFactor: integer; +} + +model ReplicationFactorChangeResponse { + totalReplicationFactor: integer; + topicName: string; +} + +model Replica { + broker?: int32; + leader?: boolean; + inSync?: boolean; +} diff --git a/contract-typespec/api/tspconfig.yaml b/contract-typespec/api/tspconfig.yaml new file mode 100644 index 000000000..e84c6b273 --- /dev/null +++ b/contract-typespec/api/tspconfig.yaml @@ -0,0 +1,6 @@ +emit: + - "@typespec/openapi3" +options: + "@typespec/openapi3": + emitter-output-dir: "{output-dir}" + output-file: openapi.yaml diff --git a/contract-typespec/build.gradle b/contract-typespec/build.gradle new file mode 100644 index 000000000..6b5f018d4 --- /dev/null +++ b/contract-typespec/build.gradle @@ -0,0 +1,58 @@ +plugins { + id "java" + alias(libs.plugins.node.gradle) +} + + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + + +node { + download = "true" != project.property("local_node") + version = project.property("node_version").toString() + pnpmVersion = project.property("pnpm_version").toString() +} + +def typeSpecVersion = "1.0.0" + +sourceSets { + main { + resources { + srcDirs += ['build/tsp'] + } + } +} + + + +def typeSpecs = ["api"] + +typeSpecs.each { spec -> + + def tspPath = "\\tsp --output-dir=${project.layout.buildDirectory.get()}/tsp/${spec} compile .\\" + + tasks.register("installDependencies_${spec}", NpmTask) { + group = 'build' + npmCommand = ['install'] + workingDir = project.layout.projectDirectory.dir(spec).asFile + } + + tasks.register("generateOpenApi_${spec}", NpmTask) { + group = 'build' + dependsOn "installDependencies_${spec}" + npmCommand = ['exec'] + workingDir = project.layout.projectDirectory.dir(spec).asFile + args = ["--package=@typespec/compiler@${typeSpecVersion}", '-c', tspPath] + } + + tasks.register("cleanDependencies_${spec}", Delete) { + delete layout.projectDirectory.dir(spec).dir("node_modules") + } + + clean.dependsOn("cleanDependencies_${spec}") + processResources.dependsOn("generateOpenApi_${spec}") + build.dependsOn("generateOpenApi_${spec}") +} diff --git a/contract/build.gradle b/contract/build.gradle index d6d662970..d95af81b8 100644 --- a/contract/build.gradle +++ b/contract/build.gradle @@ -21,7 +21,12 @@ dependencies { tasks.register('generateUiClient', GenerateTask) { generatorName = "java" - inputSpec = specDir.file("kafbat-ui-api.yaml").asFile.absolutePath + if (useTypeSpec) { + dependsOn ":contract-typespec:build" + inputSpec = project(":contract-typespec").layout.buildDirectory.dir("tsp/api/openapi.yaml").get().asFile.absolutePath + } else { + inputSpec = specDir.file("kafbat-ui-api.yaml").asFile.absolutePath + } outputDir = targetDir.dir("kafbat-ui-client").asFile.absolutePath apiPackage = "io.kafbat.ui.api.api" invokerPackage = "io.kafbat.ui.api" @@ -36,7 +41,12 @@ tasks.register('generateUiClient', GenerateTask) { tasks.register('generateBackendApi', GenerateTask) { generatorName = "spring" - inputSpec = specDir.file("kafbat-ui-api.yaml").asFile.absolutePath + if (useTypeSpec) { + dependsOn ":contract-typespec:build" + inputSpec = project(":contract-typespec").layout.buildDirectory.dir("tsp/api/openapi.yaml").get().asFile.absolutePath + } else { + inputSpec = specDir.file("kafbat-ui-api.yaml").asFile.absolutePath + } outputDir = targetDir.dir("api").asFile.absolutePath apiPackage = "io.kafbat.ui.api" invokerPackage = "io.kafbat.ui.api" diff --git a/contract/src/main/resources/swagger/kafbat-ui-api.yaml b/contract/src/main/resources/swagger/kafbat-ui-api.yaml index 8769e6aa1..dbad6d837 100644 --- a/contract/src/main/resources/swagger/kafbat-ui-api.yaml +++ b/contract/src/main/resources/swagger/kafbat-ui-api.yaml @@ -498,6 +498,7 @@ paths: schema: type: string requestBody: + required: true content: application/json: schema: diff --git a/frontend/build.gradle b/frontend/build.gradle index dc982ad73..17e0d74c1 100644 --- a/frontend/build.gradle +++ b/frontend/build.gradle @@ -23,6 +23,12 @@ tasks.named("pnpmInstall") { tasks.register('generateContract', PnpmTask) { dependsOn pnpmInstall + if (useTypeSpec) { + dependsOn ":contract-typespec:build" + inputs.files(project(":contract-typespec").layout.buildDirectory.dir("tsp/api/openapi.yaml")) + } else { + inputs.files(fileTree("../contract/src/main/resources")) + } inputs.files(fileTree("../contract/src/main/resources")) outputs.dir(project.layout.projectDirectory.dir("src/generated-sources")) args = ['gen:sources'] diff --git a/frontend/openapitools.json b/frontend/openapitools.json index 4762ba082..4f82eb54d 100644 --- a/frontend/openapitools.json +++ b/frontend/openapitools.json @@ -7,7 +7,7 @@ "fetch": { "generatorName": "typescript-fetch", "output": "src/generated-sources", - "glob": "../contract/src/main/resources/swagger/kafbat-ui-api.yaml", + "glob": "../contract-typespec/build/tsp/api/openapi.yaml", "additionalProperties": { "enumPropertyNaming": "UPPERCASE", "typescriptThreePlus": true, diff --git a/frontend/package.json b/frontend/package.json index b150467db..0a0d722a6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -40,8 +40,8 @@ "start": "vite", "dev": "vite", "compile": "pnpm gen:sources && tsc --noEmit", - "gen:sources": "rimraf ./src/generated-sources && openapi-generator-cli generate", - "build": "rimraf ./src/generated-sources && openapi-generator-cli generate && tsc --noEmit && vite build", + "gen:sources": "rimraf ./src/generated-sources && cd ../contract-typespec/api && pnpm install && pnpm build && cd ../../frontend && openapi-generator-cli generate", + "build": "pnpm gen:sources && tsc --noEmit && vite build", "preview": "vite preview", "lint": "eslint --ext .tsx,.ts src/", "lint:fix": "eslint --ext .tsx,.ts src/ --fix", diff --git a/frontend/src/lib/fixtures/topics.ts b/frontend/src/lib/fixtures/topics.ts index cff119077..f4b50ec39 100644 --- a/frontend/src/lib/fixtures/topics.ts +++ b/frontend/src/lib/fixtures/topics.ts @@ -64,6 +64,7 @@ export const topicConsumerGroups: ConsumerGroup[] = [ state: ConsumerGroupState.UNKNOWN, coordinator: { id: 1 }, consumerLag: 9, + inherit: "" }, { groupId: 'amazon.msk.canary.group.broker-4', @@ -74,6 +75,7 @@ export const topicConsumerGroups: ConsumerGroup[] = [ state: ConsumerGroupState.COMPLETING_REBALANCE, coordinator: { id: 1 }, consumerLag: 9, + inherit: "" }, ]; diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index edb5cd99d..48390b1ea 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -45,7 +45,7 @@ netty = '4.1.119.Final' spring-boot = { id = 'org.springframework.boot', version.ref = 'spring-boot' } spring-dependency-management = { id = 'io.spring.dependency-management', version = '1.1.3' } git-properties = { id = 'com.gorylenko.gradle-git-properties', version = '2.4.2' } -openapi-generator = { id = 'org.openapi.generator', version = '7.9.0' } +openapi-generator = { id = 'org.openapi.generator', version = '7.13.0' } allure = { id = 'io.qameta.allure', version = '2.10.0' } nexus-publish-plugin = { id = 'io.github.gradle-nexus.publish-plugin', version = '1.1.0' } node-gradle = { id = 'com.github.node-gradle.node', version = '7.1.0' } diff --git a/settings.gradle b/settings.gradle index a9be51116..fa0dcb978 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,6 +7,7 @@ pluginManagement { rootProject.name = "kafbat-ui" include "contract" +include "contract-typespec" include "serde-api" include "api" include "frontend"