From 7636b567e5799da02f81918720186c6728c8f14b Mon Sep 17 00:00:00 2001 From: cdxker Date: Mon, 7 Oct 2024 19:59:15 -0700 Subject: [PATCH 001/159] bugfix: update release CI action to push dittofeed and crawl worker docker images --- .github/workflows/push-server.yml | 3 + .github/workflows/workflow-release.yml | 133 +++++++++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/.github/workflows/push-server.yml b/.github/workflows/push-server.yml index d79a7ffc6c..4904ec2075 100644 --- a/.github/workflows/push-server.yml +++ b/.github/workflows/push-server.yml @@ -593,6 +593,7 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + crawl-worker: name: Push Crawl Worker runs-on: ${{ matrix.runner }} @@ -640,6 +641,7 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + crawl-cron-job: name: Push Crawl Cron Job runs-on: ${{ matrix.runner }} @@ -687,6 +689,7 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + dittofeed-sync-worker: name: Push Dittofeed Sync Worker runs-on: ${{ matrix.runner }} diff --git a/.github/workflows/workflow-release.yml b/.github/workflows/workflow-release.yml index 8e06112f58..de0b381d03 100644 --- a/.github/workflows/workflow-release.yml +++ b/.github/workflows/workflow-release.yml @@ -796,3 +796,136 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + + crawl-worker: + name: Push Crawl Worker + runs-on: ${{ matrix.runner }} + strategy: + matrix: + runner: [blacksmith-8vcpu-ubuntu-2204] + platform: [linux/amd64] + exclude: + - runner: blacksmith-8vcpu-ubuntu-2204 + platform: linux/arm64 + - runner: blacksmith-8vcpu-ubuntu-2204-arm + platform: linux/amd64 + steps: + - name: Checkout the repo + uses: actions/checkout@v4 + + - name: Setup buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + # list of Docker images to use as base name for tags + images: | + trieve/crawl-worker + tags: | + type=raw,latest + type=ref,event=tag + + crawl-cron-job: + name: Push Crawl Cron Job + runs-on: ${{ matrix.runner }} + strategy: + matrix: + runner: [blacksmith-8vcpu-ubuntu-2204] + platform: [linux/amd64] + exclude: + - runner: blacksmith-8vcpu-ubuntu-2204 + platform: linux/arm64 + - runner: blacksmith-8vcpu-ubuntu-2204-arm + platform: linux/amd64 + steps: + - name: Checkout the repo + uses: actions/checkout@v4 + + - name: Setup buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + # list of Docker images to use as base name for tags + images: | + trieve/crawl-cron-job + tags: | + type=raw,latest + type=ref,event=tag + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + platforms: ${{ matrix.platform }} + cache-from: type=registry,ref=trieve/buildcache:crawl-cron-job-${{matrix.runner}} + cache-to: type=registry,ref=trieve/buildcache:crawl-cron-job-${{matrix.runner}},mode=max + context: server/ + file: ./server/Dockerfile.crawl-cron-job + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + dittofeed-sync-worker: + name: Push Dittofeed Sync Worker + runs-on: ${{ matrix.runner }} + strategy: + matrix: + runner: [blacksmith-8vcpu-ubuntu-2204] + platform: [linux/amd64] + exclude: + - runner: blacksmith-8vcpu-ubuntu-2204 + platform: linux/arm64 + - runner: blacksmith-8vcpu-ubuntu-2204-arm + platform: linux/amd64 + steps: + - name: Checkout the repo + uses: actions/checkout@v4 + + - name: Setup buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + # list of Docker images to use as base name for tags + images: | + trieve/dittofeed-sync-worker + tags: | + type=raw,latest + type=ref,event=tag + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + platforms: ${{ matrix.platform }} + cache-from: type=registry,ref=trieve/buildcache:dittofeed-sync-worker-${{matrix.runner}} + cache-to: type=registry,ref=trieve/buildcache:dittofeed-sync-worker-${{matrix.runner}},mode=max + context: server/ + file: ./server/Dockerfile.dittofeed-sync-worker + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + From f59a834986b9e5cbe45669d1ebc85db487ce81cf Mon Sep 17 00:00:00 2001 From: cdxker Date: Mon, 7 Oct 2024 23:20:24 -0700 Subject: [PATCH 002/159] docs: cleanup response types for /chunk/search --- server/src/data/models.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/data/models.rs b/server/src/data/models.rs index 5293e9e6da..6ecc6c0339 100644 --- a/server/src/data/models.rs +++ b/server/src/data/models.rs @@ -878,6 +878,7 @@ pub struct ScoreChunk { } #[derive(Debug, Serialize, Deserialize, Clone, ToSchema)] +#[schema(title = "SlimChunkMetadataWithArrayTagSet")] pub struct SlimChunkMetadataWithArrayTagSet { pub id: uuid::Uuid, pub link: Option, @@ -1298,6 +1299,7 @@ impl From for SlimChunkMetadata { "dataset_id": "e3e3e3e3-e3e3-e3e3-e3e3-e3e3e3e3e3e3", "weight": 0.5, }))] +#[schema(title = "ContentChunkMetadata")] pub struct ContentChunkMetadata { pub id: uuid::Uuid, #[serde(skip)] From c1dfa033f2326a108de111ca98e66e1b9421d216 Mon Sep 17 00:00:00 2001 From: cdxker Date: Mon, 7 Oct 2024 21:30:57 -0700 Subject: [PATCH 003/159] feature: utility functions to get TR-QueryId on all RAG based routes --- clients/ts-sdk/src/functions/chunks/index.ts | 97 +++++++++++++++++++ clients/ts-sdk/src/functions/message/index.ts | 86 +++++++++++++++- 2 files changed, 182 insertions(+), 1 deletion(-) diff --git a/clients/ts-sdk/src/functions/chunks/index.ts b/clients/ts-sdk/src/functions/chunks/index.ts index c2b5227bc0..b11adc8d5b 100644 --- a/clients/ts-sdk/src/functions/chunks/index.ts +++ b/clients/ts-sdk/src/functions/chunks/index.ts @@ -183,6 +183,103 @@ export async function ragOnChunk( ); } +/** + * This function is just like ragOnChunk but it returns a reader to parse the stream easier. + * This function exists as an alternative to the topic+message resource pattern where our Trieve handles chat memory. With this endpoint, the user is responsible for providing the context window and the prompt and the conversation is ephemeral. + * + * + * Example: + * ```js + *const reader = await trieve.ragOnChunkReader({ + chunk_ids: ["d290f1ee-6c54-4b01-90e6-d701748f0851"], + prev_messages: [ + { + content: "How do I setup RAG with Trieve?", + role: "user", + }, + ], + prompt: + "Respond to the instruction and include the doc numbers that you used in square brackets at the end of the sentences that you used the docs for:", + stream_response: true, +}); + * ``` + */ +export async function ragOnChunkReader( + /** @hidden */ + this: TrieveSDK, + props: GenerateOffChunksReqPayload, + signal?: AbortSignal +) { + return this.trieve.fetch( + "/api/chunk/generate", + "post", + { + data: props, + datasetId: this.datasetId, + }, + signal + ); + + const reader = response.body?.getReader(); + + if (!reader) { + throw new Error("Failed to get reader from response body"); + } + + return reader; +} + +/** + * This function is just like ragOnChunk but it returns a reader to parse the stream easier. + * This function exists as an alternative to the topic+message resource pattern where our Trieve handles chat memory. With this endpoint, the user is responsible for providing the context window and the prompt and the conversation is ephemeral. + * + * + * Example: + * ```js + *const { reader, queryId } = await trieve.ragOnChunkReader({ + chunk_ids: ["d290f1ee-6c54-4b01-90e6-d701748f0851"], + prev_messages: [ + { + content: "How do I setup RAG with Trieve?", + role: "user", + }, + ], + prompt: + "Respond to the instruction and include the doc numbers that you used in square brackets at the end of the sentences that you used the docs for:", + stream_response: true, +}); + * ``` + */ +export async function ragOnChunkReaderWithQueryId( + /** @hidden */ + this: TrieveSDK, + props: GenerateOffChunksReqPayload, + signal?: AbortSignal +) { + return this.trieve.fetch( + "/api/chunk/generate", + "post", + { + data: props, + datasetId: this.datasetId, + }, + signal + ); + + const reader = response.body?.getReader(); + + if (!reader) { + throw new Error("Failed to get reader from response body"); + } + + const queryId = response.headers.get("TR-QueryID"); + + return { + reader, + queryId + }; +} + /** * This function will generate 3 suggested queries based off a hybrid search using RAG with the query provided in the request body and return them as a JSON object. * diff --git a/clients/ts-sdk/src/functions/message/index.ts b/clients/ts-sdk/src/functions/message/index.ts index 12f82da4a0..95eadb43ef 100644 --- a/clients/ts-sdk/src/functions/message/index.ts +++ b/clients/ts-sdk/src/functions/message/index.ts @@ -81,7 +81,7 @@ export async function createMessageReader( * * Example: * ```js - *const reader = await trieve.createMessageReader({ + *const { reader, queryId } = await trieve.createMessageReader({ topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a", new_message_content: "a new message" }); @@ -185,6 +185,49 @@ export async function editMessageReader( return reader; } +/** + * Edit message which exists within the topic’s chat history as a stream and returns a reader along with the queryID. This will delete the message and replace it with a new message. The new message will be generated by the AI based on the new content provided in the request body. The response will include Chunks first on the stream if the topic is using RAG. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization. The queryID is used for adding tracking events to the message + * + * Example: + * ```js + *const { reader, queryId } = await trieve.editMessageReader({ + topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a", + new_message_content: "a new message", + message_sort_order: 1 +}); + * ``` + */ +export async function editMessageReaderWithQueryId( + /** @hidden */ + this: TrieveSDK, + data: EditMessageReqPayload, + signal?: AbortSignal +) { + const response = await fetch(this.trieve.baseUrl + "/api/message", { + method: "put", + headers: { + "Content-Type": "application/json", + "TR-Dataset": this.datasetId, + Authorization: `Bearer ${this.trieve.apiKey}`, + }, + body: JSON.stringify(data), + signal, + }); + + const reader = response.body?.getReader(); + + if (!reader) { + throw new Error("Failed to get reader from response body"); + } + + const queryId = response.headers.get("TR-QueryID"); + + return { + reader, + queryId + }; +} + /** * Regenerate the assistant response to the last user message of a topic. This will delete the last message and replace it with a new message. The response will include Chunks first on the stream if the topic is using RAG. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization. * @@ -248,6 +291,47 @@ export async function regenerateMessageReader( return reader; } +/** + * Regenerate the assistant response to the last user message of a topic as a stream and returns a reader. This will delete the last message and replace it with a new message. The response will include Chunks first on the stream if the topic is using RAG. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization. + * + * Example: + * ```js + *const { reader, queryId } = await trieve.regenerateMessageReader({ + topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a", +}); + * ``` + */ +export async function regenerateMessageReaderWithQueryId( + /** @hidden */ + this: TrieveSDK, + data: RegenerateMessageReqPayload, + signal?: AbortSignal +) { + const response = await fetch(this.trieve.baseUrl + "/api/message", { + method: "delete", + headers: { + "Content-Type": "application/json", + "TR-Dataset": this.datasetId, + Authorization: `Bearer ${this.trieve.apiKey}`, + }, + body: JSON.stringify(data), + signal, + }); + + const reader = response.body?.getReader(); + + if (!reader) { + throw new Error("Failed to get reader from response body"); + } + + const queryId = response.headers.get("TR-QueryID"); + + return { + reader, + queryId + }; +} + /** * Get all messages for a given topic. If the topic is a RAG topic then the response will include Chunks first on each message. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information. * From f60a3000ea7922cc259f95a5889e7f42c6b8a0db Mon Sep 17 00:00:00 2001 From: cdxker Date: Mon, 7 Oct 2024 21:46:46 -0700 Subject: [PATCH 004/159] docs: add TR-QueryID response header to the redoc spec --- clients/ts-sdk/openapi.json | 47 ++++++++++++++++++- clients/ts-sdk/src/functions/chunks/index.ts | 30 ++++++------ .../src/functions/message/message.test.ts | 2 +- clients/ts-sdk/src/types.gen.ts | 4 +- server/src/handlers/chunk_handler.rs | 12 ++++- server/src/handlers/message_handler.rs | 42 ++++++++++++++--- 6 files changed, 110 insertions(+), 27 deletions(-) diff --git a/clients/ts-sdk/openapi.json b/clients/ts-sdk/openapi.json index 58b3d3c722..97c7aa4b3f 100644 --- a/clients/ts-sdk/openapi.json +++ b/clients/ts-sdk/openapi.json @@ -1100,6 +1100,15 @@ "responses": { "200": { "description": "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", + "headers": { + "TR-QueryID": { + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Query ID that is used for tracking analytics" + } + }, "content": { "text/plain": { "schema": { @@ -4321,6 +4330,15 @@ "responses": { "200": { "description": "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", + "headers": { + "TR-QueryID": { + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Query ID that is used for tracking analytics" + } + }, "content": { "text/plain": { "schema": { @@ -4379,7 +4397,16 @@ }, "responses": { "200": { - "description": "This will be a HTTP stream, check the chat or search UI for an example how to process this" + "description": "This will be a HTTP stream, check the chat or search UI for an example how to process this", + "headers": { + "TR-QueryID": { + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Query ID that is used for tracking analytics" + } + } }, "400": { "description": "Service error relating to getting a chat completion", @@ -4432,6 +4459,15 @@ "responses": { "200": { "description": "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", + "headers": { + "TR-QueryID": { + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Query ID that is used for tracking analytics" + } + }, "content": { "text/plain": { "schema": { @@ -4492,6 +4528,15 @@ "responses": { "200": { "description": "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", + "headers": { + "TR-QueryID": { + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "Query ID that is used for tracking analytics" + } + }, "content": { "text/plain": { "schema": { diff --git a/clients/ts-sdk/src/functions/chunks/index.ts b/clients/ts-sdk/src/functions/chunks/index.ts index b11adc8d5b..77d9bf21fd 100644 --- a/clients/ts-sdk/src/functions/chunks/index.ts +++ b/clients/ts-sdk/src/functions/chunks/index.ts @@ -210,15 +210,16 @@ export async function ragOnChunkReader( props: GenerateOffChunksReqPayload, signal?: AbortSignal ) { - return this.trieve.fetch( - "/api/chunk/generate", - "post", - { - data: props, - datasetId: this.datasetId, + const response = await fetch(this.trieve.baseUrl + "/api/chunk/generate", { + method: "post", + headers: { + "Content-Type": "application/json", + "TR-Dataset": this.datasetId, + Authorization: `Bearer ${this.trieve.apiKey}`, }, + body: JSON.stringify(props), signal - ); + }); const reader = response.body?.getReader(); @@ -256,15 +257,16 @@ export async function ragOnChunkReaderWithQueryId( props: GenerateOffChunksReqPayload, signal?: AbortSignal ) { - return this.trieve.fetch( - "/api/chunk/generate", - "post", - { - data: props, - datasetId: this.datasetId, + const response = await fetch(this.trieve.baseUrl + "/api/chunk/generate", { + method: "post", + headers: { + "Content-Type": "application/json", + "TR-Dataset": this.datasetId, + Authorization: `Bearer ${this.trieve.apiKey}`, }, + body: JSON.stringify(props), signal - ); + }); const reader = response.body?.getReader(); diff --git a/clients/ts-sdk/src/functions/message/message.test.ts b/clients/ts-sdk/src/functions/message/message.test.ts index ea5ac3a2d3..0dcb316ba3 100644 --- a/clients/ts-sdk/src/functions/message/message.test.ts +++ b/clients/ts-sdk/src/functions/message/message.test.ts @@ -24,7 +24,7 @@ describe("Message Tests", async () => { topic_id: EXAMPLE_TOPIC_ID, }); - expectTypeOf(data).toBeUnknown(); + expectTypeOf(data).toEqualTypeOf(); }); test("regenerateMessage", async () => { diff --git a/clients/ts-sdk/src/types.gen.ts b/clients/ts-sdk/src/types.gen.ts index be47007acc..a3cf5cd82a 100644 --- a/clients/ts-sdk/src/types.gen.ts +++ b/clients/ts-sdk/src/types.gen.ts @@ -3546,7 +3546,7 @@ export type EditMessageData = { trDataset: string; }; -export type EditMessageResponse = (unknown); +export type EditMessageResponse = (string); export type RegenerateMessageData = { /** @@ -4878,7 +4878,7 @@ export type $OpenApiTs = { /** * This will be a HTTP stream, check the chat or search UI for an example how to process this */ - 200: unknown; + 200: string; /** * Service error relating to getting a chat completion */ diff --git a/server/src/handlers/chunk_handler.rs b/server/src/handlers/chunk_handler.rs index 903e99fb3f..fe525b8461 100644 --- a/server/src/handlers/chunk_handler.rs +++ b/server/src/handlers/chunk_handler.rs @@ -2347,8 +2347,16 @@ pub struct GenerateOffChunksReqPayload { tag = "Chunk", request_body(content = GenerateOffChunksReqPayload, description = "JSON request payload to perform RAG on some chunks (chunks)", content_type = "application/json"), responses( - (status = 200, description = "This will be a HTTP stream of a string, check the chat or search UI for an example how to process this. Response if streaming.",), - (status = 200, description = "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", body = String), + (status = 200, description = "This will be a HTTP stream of a string, check the chat or search UI for an example how to process this. Response if streaming.", + headers( + ("TR-QueryID" = uuid::Uuid, description = "Query ID that is used for tracking analytics") + ) + ), + (status = 200, description = "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", body = String, + headers( + ("TR-QueryID" = uuid::Uuid, description = "Query ID that is used for tracking analytics") + ) + ), (status = 400, description = "Service error relating to to updating chunk, likely due to conflicting tracking_id", body = ErrorResponseBody), ), params( diff --git a/server/src/handlers/message_handler.rs b/server/src/handlers/message_handler.rs index 527cb248c5..f850404941 100644 --- a/server/src/handlers/message_handler.rs +++ b/server/src/handlers/message_handler.rs @@ -113,8 +113,16 @@ pub struct CreateMessageReqPayload { tag = "Message", request_body(content = CreateMessageReqPayload, description = "JSON request payload to create a message completion", content_type = "application/json"), responses( - (status = 200, description = "This will be a HTTP stream of a string, check the chat or search UI for an example how to process this. Response if streaming.",), - (status = 200, description = "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", body = String), + (status = 200, description = "This will be a HTTP stream of a string, check the chat or search UI for an example how to process this. Response if streaming.", + headers( + ("TR-QueryID" = uuid::Uuid, description = "Query ID that is used for tracking analytics") + ) + ), + (status = 200, description = "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", body = String, + headers( + ("TR-QueryID" = uuid::Uuid, description = "Query ID that is used for tracking analytics") + ) + ), (status = 400, description = "Service error relating to getting a chat completion", body = ErrorResponseBody), ), params( @@ -381,7 +389,11 @@ impl From for CreateMessageReqPayload { tag = "Message", request_body(content = EditMessageReqPayload, description = "JSON request payload to edit a message and get a new stream", content_type = "application/json"), responses( - (status = 200, description = "This will be a HTTP stream, check the chat or search UI for an example how to process this"), + (status = 200, description = "This will be a HTTP stream, check the chat or search UI for an example how to process this", + headers( + ("TR-QueryID" = uuid::Uuid, description = "Query ID that is used for tracking analytics") + ) + ), (status = 400, description = "Service error relating to getting a chat completion", body = ErrorResponseBody), ), params( @@ -446,8 +458,16 @@ pub async fn edit_message( tag = "Message", request_body(content = RegenerateMessageReqPayload, description = "JSON request payload to delete an agent message then regenerate it in a strem", content_type = "application/json"), responses( - (status = 200, description = "This will be a HTTP stream of a string, check the chat or search UI for an example how to process this. Response if streaming.",), - (status = 200, description = "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", body = String), + (status = 200, description = "This will be a HTTP stream of a string, check the chat or search UI for an example how to process this. Response if streaming.", + headers( + ("TR-QueryID" = uuid::Uuid, description = "Query ID that is used for tracking analytics") + ) + ), + (status = 200, description = "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", body = String, + headers( + ("TR-QueryID" = uuid::Uuid, description = "Query ID that is used for tracking analytics") + ) + ), (status = 400, description = "Service error relating to getting a chat completion", body = ErrorResponseBody), ), params( @@ -582,8 +602,16 @@ pub async fn regenerate_message_patch( tag = "Message", request_body(content = RegenerateMessageReqPayload, description = "JSON request payload to delete an agent message then regenerate it in a strem", content_type = "application/json"), responses( - (status = 200, description = "This will be a HTTP stream of a string, check the chat or search UI for an example how to process this. Response if streaming.",), - (status = 200, description = "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", body = String), + (status = 200, description = "This will be a HTTP stream of a string, check the chat or search UI for an example how to process this. Response if streaming.", + headers( + ("TR-QueryID" = uuid::Uuid, description = "Query ID that is used for tracking analytics") + ) + ), + (status = 200, description = "This will be a JSON response of a string containing the LLM's generated inference. Response if not streaming.", body = String, + headers( + ("TR-QueryID" = uuid::Uuid, description = "Query ID that is used for tracking analytics") + ) + ), (status = 400, description = "Service error relating to getting a chat completion", body = ErrorResponseBody), ), params( From 2345823653c3edd0759fbf426b4539a93ac2f5c1 Mon Sep 17 00:00:00 2001 From: cdxker Date: Mon, 7 Oct 2024 22:05:57 -0700 Subject: [PATCH 005/159] docs: update TR-Dataset and TR-Organization to be uuid::Uuid everywhere --- server/src/handlers/analytics_handler.rs | 20 ++++++------ server/src/handlers/chunk_handler.rs | 30 +++++++++--------- server/src/handlers/dataset_handler.rs | 22 ++++++------- server/src/handlers/event_handler.rs | 2 +- server/src/handlers/file_handler.rs | 8 ++--- server/src/handlers/group_handler.rs | 34 ++++++++++----------- server/src/handlers/invitation_handler.rs | 6 ++-- server/src/handlers/message_handler.rs | 12 ++++---- server/src/handlers/organization_handler.rs | 14 ++++----- server/src/handlers/stripe_handler.rs | 4 +-- server/src/handlers/topic_handler.rs | 8 ++--- 11 files changed, 80 insertions(+), 80 deletions(-) diff --git a/server/src/handlers/analytics_handler.rs b/server/src/handlers/analytics_handler.rs index de8bc24eb0..66210c58e3 100644 --- a/server/src/handlers/analytics_handler.rs +++ b/server/src/handlers/analytics_handler.rs @@ -29,7 +29,7 @@ use utoipa::ToSchema; (status = 400, description = "Service error relating to getting cluster analytics", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -88,7 +88,7 @@ pub struct RateQueryRequest { (status = 400, description = "Service error relating to rating a search query", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -126,7 +126,7 @@ pub async fn set_search_query_rating( (status = 400, description = "Service error relating to rating a RAG query", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -164,7 +164,7 @@ pub async fn set_rag_query_rating( (status = 400, description = "Service error relating to getting search analytics", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -321,7 +321,7 @@ pub async fn get_search_analytics( (status = 400, description = "Service error relating to getting RAG analytics", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -407,7 +407,7 @@ pub async fn get_rag_analytics( (status = 400, description = "Service error relating to getting recommendation analytics", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -499,7 +499,7 @@ pub struct CTRDataRequestBody { (status = 400, description = "Service error relating to sending CTR data", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -536,7 +536,7 @@ pub async fn send_ctr_data( (status = 400, description = "Service error relating to sending event data", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -572,7 +572,7 @@ pub async fn send_event_data( (status = 400, description = "Service error relating to getting CTR analytics", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -704,7 +704,7 @@ pub struct GetTopDatasetsRequestBody { tag = "Analytics", request_body(content = GetTopDatasetsRequestBody, description = "JSON request payload to filter the top datasets", content_type = "application/json"), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ), responses( (status = 200, description = "The top datasets for the request", body = Vec), diff --git a/server/src/handlers/chunk_handler.rs b/server/src/handlers/chunk_handler.rs index fe525b8461..da9773c2b3 100644 --- a/server/src/handlers/chunk_handler.rs +++ b/server/src/handlers/chunk_handler.rs @@ -288,7 +288,7 @@ pub enum CreateChunkReqPayloadEnum { (status = 400, description = "Error typically due to deserialization issues", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -446,7 +446,7 @@ pub async fn create_chunk( (status = 400, description = "Service error relating to finding a chunk by tracking_id", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("chunk_id" = Option, Path, description = "Id of the chunk you want to fetch."), ), security( @@ -492,7 +492,7 @@ pub async fn delete_chunk( (status = 400, description = "Service error relating to finding a chunk by tracking_id", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("tracking_id" = Option, Path, description = "tracking_id of the chunk you want to delete"), ), security( @@ -599,7 +599,7 @@ pub struct UpdateIngestionMessage { (status = 400, description = "Service error relating to to updating chunk, likely due to conflicting tracking_id", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -766,7 +766,7 @@ pub struct UpdateChunkByTrackingIdData { (status = 400, description = "Service error relating to to updating chunk", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -1148,7 +1148,7 @@ pub fn parse_query( (status = 400, description = "Service error relating to searching", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("X-API-Version" = Option, Header, description = "The API version to use for this request. Defaults to V2 for orgs created after July 12, 2024 and V1 otherwise.") ), security( @@ -1394,7 +1394,7 @@ impl From for SearchChunksReqPayload { (status = 400, description = "Service error relating to searching", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("X-API-Version" = Option, Header, description = "The API version to use for this request. Defaults to V2 for orgs created after July 12, 2024 and V1 otherwise.") ), security( @@ -1521,7 +1521,7 @@ pub struct ScrollChunksReqPayload { (status = 400, description = "Service error relating to scrolling chunks", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), @@ -1597,7 +1597,7 @@ pub async fn scroll_dataset_chunks( (status = 404, description = "Chunk not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("X-API-Version" = Option, Header, description = "The API version to use for this request. Defaults to V2 for orgs created after July 12, 2024 and V1 otherwise."), ("chunk_id" = Option, Path, description = "Id of the chunk you want to fetch."), ), @@ -1697,7 +1697,7 @@ pub struct CountChunkQueryResponseBody { (status = 404, description = "Failed to count chunks", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), @@ -1782,7 +1782,7 @@ pub async fn count_chunks( (status = 404, description = "Chunk not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("X-API-Version" = Option, Header, description = "The API version to use for this request. Defaults to V2 for orgs created after July 12, 2024 and V1 otherwise."), ("tracking_id" = Option, Path, description = "tracking_id of the chunk you want to fetch"), ), @@ -1843,7 +1843,7 @@ pub struct GetChunksData { (status = 404, description = "Any one of the specified chunks not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("X-API-Version" = Option, Header, description = "The API version to use for this request. Defaults to V2 for orgs created after July 12, 2024 and V1 otherwise.") ), security( @@ -1915,7 +1915,7 @@ pub struct GetTrackingChunksData { (status = 400, description = "Service error relating to finding a chunk by tracking_id", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("X-API-Version" = Option, Header, description = "The API version to use for this request. Defaults to V2 for orgs created after July 12, 2024 and V1 otherwise.") ), security( @@ -2022,7 +2022,7 @@ pub enum RecommendResponseTypes { (status = 400, description = "Service error relating to to getting similar chunks", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("X-API-Version" = Option, Header, description = "The API version to use for this request. Defaults to V2 for orgs created after July 12, 2024 and V1 otherwise.") ), security( @@ -2360,7 +2360,7 @@ pub struct GenerateOffChunksReqPayload { (status = 400, description = "Service error relating to to updating chunk, likely due to conflicting tracking_id", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), diff --git a/server/src/handlers/dataset_handler.rs b/server/src/handlers/dataset_handler.rs index a46839f6df..a0f5b75b79 100644 --- a/server/src/handlers/dataset_handler.rs +++ b/server/src/handlers/dataset_handler.rs @@ -123,7 +123,7 @@ pub struct CreateDatasetRequest { (status = 400, description = "Service error relating to creating the dataset", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ), security( ("ApiKey" = ["owner"]), @@ -259,7 +259,7 @@ pub struct UpdateDatasetRequest { (status = 404, description = "Dataset not found", body = ErrorResponseBody) ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ), security( ("ApiKey" = ["owner"]), @@ -343,7 +343,7 @@ pub struct GetCrawlOptionsResponse { (status = 404, description = "Dataset not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("dataset_id" = uuid, Path, description = "The id of the dataset you want to retrieve."), ), security( @@ -383,7 +383,7 @@ pub async fn get_dataset_crawl_options( (status = 404, description = "Dataset not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("dataset_id" = uuid, Path, description = "The id of the dataset you want to delete."), ), @@ -429,7 +429,7 @@ pub async fn delete_dataset( (status = 404, description = "Dataset not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("dataset_id" = uuid, Path, description = "The id of the dataset you want to clear."), ), @@ -474,7 +474,7 @@ pub async fn clear_dataset( (status = 404, description = "Dataset not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("tracking_id" = String, Path, description = "The tracking id of the dataset you want to delete."), ), security( @@ -520,7 +520,7 @@ pub async fn delete_dataset_by_tracking_id( (status = 404, description = "Dataset not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("dataset_id" = uuid, Path, description = "The id of the dataset you want to retrieve."), ), security( @@ -559,7 +559,7 @@ pub async fn get_dataset( (status = 404, description = "Dataset not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("dataset_id" = uuid, Path, description = "The id of the dataset you want to retrieve usage for."), ), security( @@ -593,7 +593,7 @@ pub async fn get_usage_by_dataset_id( (status = 404, description = "Dataset not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("tracking_id" = String, Path, description = "The tracking id of the dataset you want to retrieve."), ), security( @@ -639,7 +639,7 @@ pub struct GetDatasetsPagination { (status = 404, description = "Could not find organization", body = ErrorResponseBody) ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ("organization_id" = uuid, Path, description = "id of the organization you want to retrieve datasets for"), ("limit" = Option, Query, description = "The number of records to return"), ("offset" = Option, Query, description = "The number of records to skip"), @@ -712,7 +712,7 @@ pub struct GetAllTagsResponse { (status = 400, description = "Service error relating to finding items by tag", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), diff --git a/server/src/handlers/event_handler.rs b/server/src/handlers/event_handler.rs index 96c0824203..9e801d4083 100644 --- a/server/src/handlers/event_handler.rs +++ b/server/src/handlers/event_handler.rs @@ -37,7 +37,7 @@ pub struct GetEventsData { (status = 400, description = "Service error relating to getting events for the dataset", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), diff --git a/server/src/handlers/file_handler.rs b/server/src/handlers/file_handler.rs index b050e76d96..32232f3559 100644 --- a/server/src/handlers/file_handler.rs +++ b/server/src/handlers/file_handler.rs @@ -103,7 +103,7 @@ pub struct UploadFileResult { (status = 400, description = "Service error relating to uploading the file", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -236,7 +236,7 @@ pub async fn upload_file_handler( (status = 404, description = "File not found", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("file_id" = uuid::Uuid, description = "The id of the file to fetch"), ), security( @@ -279,7 +279,7 @@ pub struct FileData { (status = 400, description = "Service error relating to getting the files in the current datase", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("dataset_id" = uuid::Uuid, description = "The id of the dataset to fetch files for."), ("page" = u64, description = "The page number of files you wish to fetch. Each page contains at most 10 files."), ), @@ -337,7 +337,7 @@ pub async fn get_dataset_files_handler( (status = 400, description = "Service error relating to finding or deleting the file", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("file_id" = uuid::Uuid, description = "The id of the file to delete"), ("delete_chunks" = bool, Query, description = "Delete the chunks within the group"), ), diff --git a/server/src/handlers/group_handler.rs b/server/src/handlers/group_handler.rs index 041a1fedf3..c0f06190d8 100644 --- a/server/src/handlers/group_handler.rs +++ b/server/src/handlers/group_handler.rs @@ -172,7 +172,7 @@ impl From> for CreateChunkGroupResponseEnum { (status = 400, description = "Service error relating to creating the chunk_group(s)", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -311,7 +311,7 @@ pub struct DatasetGroupQuery { (status = 400, description = "Service error relating to getting the groups created by the given dataset", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("dataset_id" = uuid::Uuid, description = "The id of the dataset to fetch groups for."), ("page" = i64, description = "The page of groups to fetch. Page is 1-indexed."), ), @@ -356,7 +356,7 @@ pub struct GetGroupByTrackingIDData { (status = 404, description = "Group not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("tracking_id" = String, description = "The tracking id of the group to fetch."), ), security( @@ -401,7 +401,7 @@ pub struct GetGroupData { (status = 404, description = "Group not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("group_id" = Option, Path, description = "Id of the group you want to fetch."), ), security( @@ -453,7 +453,7 @@ pub struct UpdateGroupByTrackingIDReqPayload { (status = 400, description = "Service error relating to updating the chunkGroup", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("tracking_id" = uuid::Uuid, description = "Tracking id of the chunk_group to update"), ), security( @@ -514,7 +514,7 @@ pub struct DeleteGroupByTrackingIDData { (status = 400, description = "Service error relating to deleting the chunkGroup", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("tracking_id" = uuid::Uuid, description = "Tracking id of the chunk_group to delete"), ("delete_chunks" = bool, Query, description = "Delete the chunks within the group"), ), @@ -575,7 +575,7 @@ pub struct DeleteGroupData { (status = 400, description = "Service error relating to deleting the chunkGroup", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("group_id" = Option, Path, description = "Id of the group you want to fetch."), ("delete_chunks" = bool, Query, description = "Delete the chunks within the group"), ), @@ -650,7 +650,7 @@ pub struct UpdateChunkGroupReqPayload { (status = 400, description = "Service error relating to updating the chunkGroup", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -741,7 +741,7 @@ pub struct AddChunkToGroupReqPayload { (status = 400, description = "Service error relating to getting the groups that the chunk is in.", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("group_id" = uuid, description = "Id of the group to add the chunk to as a bookmark"), ), security( @@ -796,7 +796,7 @@ pub async fn add_chunk_to_group( (status = 400, description = "Service error related to adding the chunk group by tracking_id", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("tracking_id" = uuid, description = "Tracking id of the group to add the chunk to as a bookmark"), ), security( @@ -895,7 +895,7 @@ pub enum GetChunksInGroupResponse { (status = 404, description = "Group not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("group_id" = uuid::Uuid, Path, description = "Id of the group you want to fetch."), ("X-API-Version" = Option, Header, description = "The version of the API to use for the request"), ("page" = Option, description = "The page of chunks to get from the group"), @@ -953,7 +953,7 @@ pub struct GetChunksInGroupByTrackingIdReqPayload { (status = 404, description = "Group not found", body = ErrorResponseBody) ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("group_tracking_id" = String, description = "The id of the group to get the chunks from"), ("X-API-Version" = Option, Header, description = "The version of the API to use for the request"), ("page" = u64, description = "The page of chunks to get from the group"), @@ -1012,7 +1012,7 @@ pub struct GetGroupsForChunksReqPayload { (status = 400, description = "Service error relating to getting the groups that the chunk is in", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), @@ -1054,7 +1054,7 @@ pub struct RemoveChunkFromGroupReqPayload { (status = 400, description = "Service error relating to removing the chunk from the group", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("group_id" = uuid::Uuid, Path, description = "Id of the group you want to remove the chunk from."), ("chunk_id" = Option, Query, description = "Id of the chunk you want to remove from the group"), ), @@ -1151,7 +1151,7 @@ pub enum RecommendGroupsResponse { (status = 400, description = "Service error relating to to getting similar chunks", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("X-API-Version" = Option, Header, description = "The API version to use for this request. Defaults to V2 for orgs created after July 12, 2024 and V1 otherwise.") ), security( @@ -1491,7 +1491,7 @@ impl SearchWithinGroupResults { (status = 400, description = "Service error relating to getting the groups that the chunk is in", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("X-API-Version" = Option, Header, description = "The API version to use for this request. Defaults to V2 for orgs created after July 12, 2024 and V1 otherwise.") ), security( @@ -1672,7 +1672,7 @@ pub struct SearchOverGroupsReqPayload { (status = 400, description = "Service error relating to searching over groups", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("X-API-Version" = Option, Header, description = "The API version to use for this request. Defaults to V2 for orgs created after July 12, 2024 and V1 otherwise.") ), security( diff --git a/server/src/handlers/invitation_handler.rs b/server/src/handlers/invitation_handler.rs index 5878c93f77..4eb350bdd6 100644 --- a/server/src/handlers/invitation_handler.rs +++ b/server/src/handlers/invitation_handler.rs @@ -54,7 +54,7 @@ pub struct InvitationData { (status = 400, description = "Invalid email or some other error", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ), security( ("ApiKey" = ["admin"]), @@ -170,7 +170,7 @@ pub async fn create_invitation( (status = 400, description = "Service error relating to getting invitations for the dataset", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ("organization_id" = uuid, Path, description = "The organization id to get invitations for"), ), security( @@ -204,7 +204,7 @@ pub async fn get_invitations( (status = 400, description = "Service error relating to deleting invitation", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ("invitation_id" = uuid, Path, description = "The id of the invitation to delete"), ), security( diff --git a/server/src/handlers/message_handler.rs b/server/src/handlers/message_handler.rs index f850404941..2294d4352d 100644 --- a/server/src/handlers/message_handler.rs +++ b/server/src/handlers/message_handler.rs @@ -126,7 +126,7 @@ pub struct CreateMessageReqPayload { (status = 400, description = "Service error relating to getting a chat completion", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), @@ -254,7 +254,7 @@ pub async fn create_message( (status = 400, description = "Service error relating to getting the messages", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("messages_topic_id" = uuid, description = "The ID of the topic to get messages for."), ), security( @@ -397,7 +397,7 @@ impl From for CreateMessageReqPayload { (status = 400, description = "Service error relating to getting a chat completion", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), @@ -471,7 +471,7 @@ pub async fn edit_message( (status = 400, description = "Service error relating to getting a chat completion", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), @@ -615,7 +615,7 @@ pub async fn regenerate_message_patch( (status = 400, description = "Service error relating to getting a chat completion", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), @@ -675,7 +675,7 @@ pub struct SuggestedQueriesResponse { (status = 400, description = "Service error relating to to updating chunk, likely due to conflicting tracking_id", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["readonly"]), diff --git a/server/src/handlers/organization_handler.rs b/server/src/handlers/organization_handler.rs index d0fe51f13d..89686b15ca 100644 --- a/server/src/handlers/organization_handler.rs +++ b/server/src/handlers/organization_handler.rs @@ -30,7 +30,7 @@ use utoipa::ToSchema; (status = 404, description = "Organization not found", body = ErrorResponseBody) ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ("organization_id" = Option, Path, description = "The id of the organization you want to fetch."), ), security( @@ -66,7 +66,7 @@ pub async fn get_organization( (status = 400, description = "Service error relating to deleting the organization by id", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ("organization_id" = Option, Path, description = "The id of the organization you want to fetch."), ), security( @@ -119,7 +119,7 @@ pub struct UpdateOrganizationReqPayload { (status = 400, description = "Service error relating to updating the organization", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ), security( ("ApiKey" = ["owner"]), @@ -215,7 +215,7 @@ pub async fn create_organization( (status = 400, description = "Service error relating to finding the organization's usage by id", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ("organization_id" = Option, Path, description = "The id of the organization you want to fetch the usage of."), ), security( @@ -252,7 +252,7 @@ pub async fn get_organization_usage( (status = 400, description = "Service error relating to finding the organization's users by id", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ("organization_id" = Option, Path, description = "The id of the organization you want to fetch the users of."), ), security( @@ -295,7 +295,7 @@ pub struct RemoveUserFromOrgPathParams { (status = 400, description = "Service error relating to removing the user from the organization", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ("organization_id" = uuid::Uuid, Path, description = "The id of the organization you want to remove the user from"), ("user_id" = uuid::Uuid, Path, description = "The id of the user you want to remove from the organization"), ), @@ -374,7 +374,7 @@ pub struct UpdateAllOrgDatasetConfigsReqPayload { (status = 400, description = "Service error relating to updating the dataset ServerConfigurations", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ), security( ("ApiKey" = ["owner"]), diff --git a/server/src/handlers/stripe_handler.rs b/server/src/handlers/stripe_handler.rs index f34aa91caf..5ef472dcbb 100644 --- a/server/src/handlers/stripe_handler.rs +++ b/server/src/handlers/stripe_handler.rs @@ -294,7 +294,7 @@ pub async fn direct_to_payment_link( (status = 400, description = "Service error relating to creating a URL for a stripe checkout page", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ("subscription_id" = uuid, Path, description = "id of the subscription you want to cancel"), ), security( @@ -339,7 +339,7 @@ pub struct UpdateSubscriptionData { (status = 400, description = "Service error relating to updating the subscription to the new plan", body = ErrorResponseBody), ), params( - ("TR-Organization" = String, Header, description = "The organization id to use for the request"), + ("TR-Organization" = uuid::Uuid, Header, description = "The organization id to use for the request"), ("subscription_id" = uuid::Uuid, Path, description = "id of the subscription you want to update"), ("plan_id" = uuid::Uuid, Path, description = "id of the plan you want to subscribe to"), ), diff --git a/server/src/handlers/topic_handler.rs b/server/src/handlers/topic_handler.rs index 3a6e4947d4..ca00657cf4 100644 --- a/server/src/handlers/topic_handler.rs +++ b/server/src/handlers/topic_handler.rs @@ -38,7 +38,7 @@ pub struct CreateTopicReqPayload { (status = 400, description = "Topic name empty or a service error", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -108,7 +108,7 @@ pub struct DeleteTopicData { (status = 400, description = "Service error relating to topic deletion", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ("topic_id" = uuid, Path, description = "The id of the topic you want to delete."), ), security( @@ -151,7 +151,7 @@ pub struct UpdateTopicReqPayload { (status = 400, description = "Service error relating to topic update", body = ErrorResponseBody), ), params( - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), @@ -191,7 +191,7 @@ pub async fn update_topic( ), params ( ("owner_id", description="The owner_id to get topics of; A common approach is to use a browser fingerprint or your user's id"), - ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ("TR-Dataset" = uuid::Uuid, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), ), security( ("ApiKey" = ["admin"]), From 7e8c31c79c548ff626723d5978f952a3fee43312 Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Tue, 8 Oct 2024 12:24:56 -0500 Subject: [PATCH 006/159] styling: fix table overflow --- .../src/analytics/pages/tablePages/RAGAnalyticsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx b/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx index 8299b09364..26af65727b 100644 --- a/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx +++ b/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx @@ -141,7 +141,7 @@ export const RAGAnalyticsPage = () => { -
+
Date: Tue, 8 Oct 2024 13:59:33 -0500 Subject: [PATCH 007/159] feat: use same clickhouse and postgres message ids --- frontends/chat/package.json | 3 +- .../chat/src/components/Atoms/AfMessage.tsx | 6 +- .../src/components/Atoms/MessageVoting.tsx | 114 ++++++++++++++++++ .../src/components/Layouts/MainLayout.tsx | 4 +- frontends/chat/src/types/messages.ts | 1 + server/src/data/models.rs | 3 +- server/src/handlers/message_handler.rs | 1 + server/src/operators/message_operator.rs | 5 +- yarn.lock | 14 ++- 9 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 frontends/chat/src/components/Atoms/MessageVoting.tsx diff --git a/frontends/chat/package.json b/frontends/chat/package.json index 875b55281c..8449a5b32c 100644 --- a/frontends/chat/package.json +++ b/frontends/chat/package.json @@ -19,6 +19,7 @@ "@solid-aria/focus": "^0.1.4", "@solid-aria/switch": "^0.1.2", "@solid-aria/visually-hidden": "^0.1.3", + "@solid-primitives/storage": "^4.2.1", "@solidjs/router": "^0.10.1", "rehype-sanitize": "^6.0.0", "remark-breaks": "3.0.3", @@ -34,11 +35,11 @@ "trieve-ts-sdk": "file:../../clients/ts-sdk" }, "devDependencies": { - "config": "*", "@types/sanitize-html": "^2.9.5", "@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/parser": "7.14.1", "autoprefixer": "^10.4.16", + "config": "*", "eslint": "^8.13.0", "eslint-config-prettier": "^9.1.0", "eslint-config-semistandard": "^17.0.0", diff --git a/frontends/chat/src/components/Atoms/AfMessage.tsx b/frontends/chat/src/components/Atoms/AfMessage.tsx index 90529a73d9..21e9cf9fd3 100644 --- a/frontends/chat/src/components/Atoms/AfMessage.tsx +++ b/frontends/chat/src/components/Atoms/AfMessage.tsx @@ -28,8 +28,9 @@ import { SolidMarkdown } from "solid-markdown"; import remarkGfm from "remark-gfm"; import remarkBreaks from "remark-breaks"; import rehypeSanitize from "rehype-sanitize"; - +import { MessageVoting } from "./MessageVoting"; export interface AfMessageProps { + queryId?: string; normalChat: boolean; role: "user" | "assistant" | "system"; content: string; @@ -234,6 +235,9 @@ export const AfMessage = (props: AfMessageProps) => { + + {(id) => } +
)}
diff --git a/frontends/chat/src/components/Atoms/MessageVoting.tsx b/frontends/chat/src/components/Atoms/MessageVoting.tsx new file mode 100644 index 0000000000..88107b32a9 --- /dev/null +++ b/frontends/chat/src/components/Atoms/MessageVoting.tsx @@ -0,0 +1,114 @@ +import { makePersisted } from "@solid-primitives/storage"; +import { IconTypes } from "solid-icons"; +import { + BsHandThumbsDown, + BsHandThumbsDownFill, + BsHandThumbsUp, + BsHandThumbsUpFill, +} from "solid-icons/bs"; +import { Show, useContext } from "solid-js"; +import { createStore } from "solid-js/store"; +import { UserContext } from "../contexts/UserContext"; + +interface MessageVotingProps { + queryId: string; +} + +type MessageVoteStore = { + [key: string]: number; +}; + +const apiHost = import.meta.env.VITE_API_HOST as unknown as string; + +// Create store exepcted to be directly destructured, this is fine +// eslint-disable-next-line @typescript-eslint/no-unused-vars, solid/reactivity +const [store, setStore] = makePersisted(createStore({}), { + name: "vote-cache", +}); + +export const MessageVoting = (props: MessageVotingProps) => { + const userContext = useContext(UserContext); + + const updateVote = async (queryId: string, vote: number) => { + const prevVote = store[queryId]; + setStore(queryId, vote); + const dataset = userContext.currentDataset?.(); + if (dataset?.dataset.id) { + const response = await fetch(apiHost + "/analytics/rag", { + method: "PUT", + headers: { + "Content-Type": "application/json", + "TR-Dataset": dataset.dataset.id, + }, + credentials: "include", + body: JSON.stringify({ + query_id: queryId, + rating: vote, + }), + }); + + if (response.status !== 204) { + console.error("Failed to update vote", response); + // Rollback the vote + setStore(queryId, prevVote); + } + } + }; + + const VoteIcon = (props: { + icon: IconTypes; + onClick: (e: MouseEvent) => void; + }) => { + return ( +
props.onClick(e)} class="p-2"> + {props.icon({})} +
+ ); + }; + + return ( +
+ { + e.preventDefault(); + updateVote(props.queryId, 1); + }} + /> + } + when={store[props.queryId] === 1} + > + { + e.preventDefault(); + updateVote(props.queryId, 0); + }} + /> + + + { + e.preventDefault(); + updateVote(props.queryId, -1); + }} + /> + } + when={store[props.queryId] === -1} + > + { + e.preventDefault(); + updateVote(props.queryId, 0); + }} + /> + +
+ ); +}; diff --git a/frontends/chat/src/components/Layouts/MainLayout.tsx b/frontends/chat/src/components/Layouts/MainLayout.tsx index ad22bfc9b1..89ffb80bd9 100644 --- a/frontends/chat/src/components/Layouts/MainLayout.tsx +++ b/frontends/chat/src/components/Layouts/MainLayout.tsx @@ -113,6 +113,7 @@ const MainLayout = (props: LayoutProps) => { const newMessage = { content: lastMessage.content + newText, + id: lastMessage.id, }; return [...prev.slice(0, prev.length - 1), newMessage]; }); @@ -177,7 +178,7 @@ const MainLayout = (props: LayoutProps) => { if (regenerateLastMessage) { requestMethod = "PATCH"; setMessages((prev): Message[] => { - const newMessages = [{ content: "" }]; + const newMessages = [{ content: "", id: prev[prev.length - 1].id }]; return [...prev.slice(0, -1), ...newMessages]; }); } else { @@ -297,6 +298,7 @@ const MainLayout = (props: LayoutProps) => { {(message, idx) => { return ( , completion_tokens: Option, dataset_id: T, + message_id: uuid::Uuid, ) -> Self { Message { - id: uuid::Uuid::new_v4(), + id: message_id, topic_id: topic_id.into(), sort_order, content: content.into(), diff --git a/server/src/handlers/message_handler.rs b/server/src/handlers/message_handler.rs index 2294d4352d..892549c214 100644 --- a/server/src/handlers/message_handler.rs +++ b/server/src/handlers/message_handler.rs @@ -180,6 +180,7 @@ pub async fn create_message( None, None, dataset_org_plan_sub.dataset.id, + uuid::Uuid::new_v4(), ); // get the previous messages diff --git a/server/src/operators/message_operator.rs b/server/src/operators/message_operator.rs index 7c7ccf5096..f8eae2657d 100644 --- a/server/src/operators/message_operator.rs +++ b/server/src/operators/message_operator.rs @@ -106,6 +106,7 @@ pub async fn create_generic_system_message( Some(0), Some(0), dataset_id, + uuid::Uuid::new_v4(), ); Ok(system_message) @@ -598,10 +599,11 @@ pub async fn stream_response( .expect("usize to i32 conversion should always succeed"), ), dataset.id, + query_id, ); let clickhouse_rag_event = RagQueryEventClickhouse { - id: uuid::Uuid::new_v4(), + id: query_id, created_at: time::OffsetDateTime::now_utc(), dataset_id: dataset.id, search_id: uuid::Uuid::nil(), @@ -661,6 +663,7 @@ pub async fn stream_response( None, Some(chunk_v.len().try_into().unwrap()), dataset.id, + query_id_arb, ); let clickhouse_rag_event = RagQueryEventClickhouse { diff --git a/yarn.lock b/yarn.lock index e6021cc83f..7d2ad91874 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1120,6 +1120,13 @@ dependencies: "@solid-primitives/utils" "^3.0.2" +"@solid-primitives/storage@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@solid-primitives/storage/-/storage-4.2.1.tgz#8d6f5bebf81d5fd3d852e9eebe9445f95e90ed42" + integrity sha512-1XUJeaSlizH9Eam/+IbIpslHEnggJMNZXzfsr06AlbG6tJtQENMu0+94ZIvooxt4Cyw46wPzcnHYbSK7LzoQAA== + dependencies: + "@solid-primitives/utils" "^6.2.3" + "@solid-primitives/utils@^2.1.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@solid-primitives/utils/-/utils-2.2.1.tgz#7df54fb4802ea5988cc881f6ee3c4d27bff0c6f6" @@ -1130,6 +1137,11 @@ resolved "https://registry.yarnpkg.com/@solid-primitives/utils/-/utils-3.1.0.tgz#52edf36dabe62eba94f8356c3b9b788234d088a8" integrity sha512-/rerChcwgFtHEgVCCBY7BXGHh7a83HcIAzR8QhXJ789geIVbBs2YxHF4UUZlG7ec00NKSfvO3+sQquN/xKQLMw== +"@solid-primitives/utils@^6.2.3": + version "6.2.3" + resolved "https://registry.yarnpkg.com/@solid-primitives/utils/-/utils-6.2.3.tgz#1abed4c74a2696e08bd2e49cf2b86fc8b87a32bd" + integrity sha512-CqAwKb2T5Vi72+rhebSsqNZ9o67buYRdEJrIFzRXz3U59QqezuuxPsyzTSVCacwS5Pf109VRsgCJQoxKRoECZQ== + "@solidjs/router@^0.10.1": version "0.10.10" resolved "https://registry.yarnpkg.com/@solidjs/router/-/router-0.10.10.tgz#cd236ef438e9aa50864e7361db710af26b152ad5" @@ -7586,7 +7598,7 @@ totalist@^3.0.0: integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== "trieve-ts-sdk@file:clients/ts-sdk": - version "0.0.9" + version "0.0.12" trim-lines@^3.0.0: version "3.0.1" From 8c97b2ee5ea64b153e2cd2a6e129d514b91cf3cb Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Tue, 8 Oct 2024 14:19:20 -0500 Subject: [PATCH 008/159] feat: show vote score on dashboard rag table --- .../src/analytics/components/SingleRagInfo/index.tsx | 7 ++----- .../analytics/pages/tablePages/RAGAnalyticsPage.tsx | 12 ++++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx b/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx index 1c125cf4c4..2e3dfcdf3b 100644 --- a/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx +++ b/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx @@ -1,6 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { createQuery } from "@tanstack/solid-query"; import { getRagQuery, getSearchQuery } from "../../api/analytics"; import { createMemo, For, Show, useContext } from "solid-js"; @@ -52,12 +49,12 @@ export const SingleRAGQuery = (props: SingleRAGQueryProps) => {
-

+

{props.rag_data.user_message} diff --git a/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx b/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx index 26af65727b..2df4538406 100644 --- a/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx +++ b/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx @@ -4,6 +4,7 @@ import { createEffect, createSignal, Show, useContext } from "solid-js"; import { SortableColumnDef, TanStackTable } from "shared/ui"; import { useBetterNav } from "../../utils/useBetterNav"; import { + createColumnHelper, createSolidTable, getCoreRowModel, SortingState, @@ -25,6 +26,8 @@ import { IoOpenOutline } from "solid-icons/io"; import { format } from "date-fns"; import { parseCustomDateString } from "../../utils/formatDate"; +const colHelper = createColumnHelper(); + export const RAGAnalyticsPage = () => { const navigate = useBetterNav(); const datasetContext = useContext(DatasetContext); @@ -111,6 +114,15 @@ export const RAGAnalyticsPage = () => { ); }, }, + { + accessorKey: "query_rating", + header: "Query Rating", + cell(props) { + const val = + props.getValue() as unknown as RagQueryEvent["query_rating"]; + return {(rating) =>
{val?.rating}
}
; + }, + }, ]; const table = createSolidTable({ From 5cb6c43f101f9732e2fbe9c5abd3704529b819c5 Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Tue, 8 Oct 2024 14:22:44 -0500 Subject: [PATCH 009/159] fix: eslint --- frontends/chat/src/components/Atoms/MessageVoting.tsx | 8 ++++---- .../src/analytics/pages/tablePages/RAGAnalyticsPage.tsx | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/frontends/chat/src/components/Atoms/MessageVoting.tsx b/frontends/chat/src/components/Atoms/MessageVoting.tsx index 88107b32a9..46781e34da 100644 --- a/frontends/chat/src/components/Atoms/MessageVoting.tsx +++ b/frontends/chat/src/components/Atoms/MessageVoting.tsx @@ -74,7 +74,7 @@ export const MessageVoting = (props: MessageVotingProps) => { icon={BsHandThumbsUp} onClick={(e) => { e.preventDefault(); - updateVote(props.queryId, 1); + void updateVote(props.queryId, 1); }} /> } @@ -84,7 +84,7 @@ export const MessageVoting = (props: MessageVotingProps) => { icon={BsHandThumbsUpFill} onClick={(e) => { e.preventDefault(); - updateVote(props.queryId, 0); + void updateVote(props.queryId, 0); }} /> @@ -95,7 +95,7 @@ export const MessageVoting = (props: MessageVotingProps) => { icon={BsHandThumbsDown} onClick={(e) => { e.preventDefault(); - updateVote(props.queryId, -1); + void updateVote(props.queryId, -1); }} /> } @@ -105,7 +105,7 @@ export const MessageVoting = (props: MessageVotingProps) => { icon={BsHandThumbsDownFill} onClick={(e) => { e.preventDefault(); - updateVote(props.queryId, 0); + void updateVote(props.queryId, 0); }} /> diff --git a/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx b/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx index 2df4538406..57d4690411 100644 --- a/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx +++ b/frontends/dashboard/src/analytics/pages/tablePages/RAGAnalyticsPage.tsx @@ -4,7 +4,6 @@ import { createEffect, createSignal, Show, useContext } from "solid-js"; import { SortableColumnDef, TanStackTable } from "shared/ui"; import { useBetterNav } from "../../utils/useBetterNav"; import { - createColumnHelper, createSolidTable, getCoreRowModel, SortingState, @@ -26,8 +25,6 @@ import { IoOpenOutline } from "solid-icons/io"; import { format } from "date-fns"; import { parseCustomDateString } from "../../utils/formatDate"; -const colHelper = createColumnHelper(); - export const RAGAnalyticsPage = () => { const navigate = useBetterNav(); const datasetContext = useContext(DatasetContext); @@ -120,7 +117,9 @@ export const RAGAnalyticsPage = () => { cell(props) { const val = props.getValue() as unknown as RagQueryEvent["query_rating"]; - return {(rating) =>
{val?.rating}
}
; + return ( + {(rating) =>
{rating().rating}
}
+ ); }, }, ]; From b0642662d3638960bb315ebc6602a7367690a28c Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Tue, 8 Oct 2024 14:39:00 -0500 Subject: [PATCH 010/159] cleanup: ignore clippy too many args --- server/src/data/models.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/data/models.rs b/server/src/data/models.rs index b62571caca..09052479bf 100644 --- a/server/src/data/models.rs +++ b/server/src/data/models.rs @@ -249,6 +249,7 @@ impl From for ChatMessage { } impl Message { + #![allow(clippy::too_many_arguments)] pub fn from_details, T: Into>( content: S, topic_id: T, From 4b04a365a07ac579ce2442c5d5c02d10c8b62400 Mon Sep 17 00:00:00 2001 From: Dens Sumesh Date: Tue, 8 Oct 2024 12:49:08 -0700 Subject: [PATCH 011/159] bugfix: use the search events search id in RAG route --- server/src/operators/message_operator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/operators/message_operator.rs b/server/src/operators/message_operator.rs index f8eae2657d..c8ac5a7b67 100644 --- a/server/src/operators/message_operator.rs +++ b/server/src/operators/message_operator.rs @@ -606,7 +606,7 @@ pub async fn stream_response( id: query_id, created_at: time::OffsetDateTime::now_utc(), dataset_id: dataset.id, - search_id: uuid::Uuid::nil(), + search_id: clickhouse_search_event.id, results: chunk_ids .clone() .into_iter() From ce4db075ff9753ac3af5c3d0725e95da851018db Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Tue, 8 Oct 2024 14:49:47 -0500 Subject: [PATCH 012/159] styling: better vote button styling --- .../src/components/Atoms/MessageVoting.tsx | 73 +++++++++---------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/frontends/chat/src/components/Atoms/MessageVoting.tsx b/frontends/chat/src/components/Atoms/MessageVoting.tsx index 46781e34da..9c91c0ed23 100644 --- a/frontends/chat/src/components/Atoms/MessageVoting.tsx +++ b/frontends/chat/src/components/Atoms/MessageVoting.tsx @@ -59,56 +59,55 @@ export const MessageVoting = (props: MessageVotingProps) => { icon: IconTypes; onClick: (e: MouseEvent) => void; }) => { - return ( -
props.onClick(e)} class="p-2"> - {props.icon({})} -
- ); + return
props.onClick(e)}>{props.icon({})}
; }; return ( -
- +
+
+ { + e.preventDefault(); + void updateVote(props.queryId, 1); + }} + /> + } + when={store[props.queryId] === 1} + > { e.preventDefault(); - void updateVote(props.queryId, 1); + void updateVote(props.queryId, 0); }} /> - } - when={store[props.queryId] === 1} - > - { - e.preventDefault(); - void updateVote(props.queryId, 0); - }} - /> - + - { + e.preventDefault(); + void updateVote(props.queryId, -1); + }} + /> + } + when={store[props.queryId] === -1} + > { e.preventDefault(); - void updateVote(props.queryId, -1); + void updateVote(props.queryId, 0); }} /> - } - when={store[props.queryId] === -1} - > - { - e.preventDefault(); - void updateVote(props.queryId, 0); - }} - /> - -
+ +
+ ); }; From 5dcf9c13454f1873f40f6c62ea7231565cfda8c1 Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Tue, 8 Oct 2024 15:01:17 -0500 Subject: [PATCH 013/159] fix: keep track of message id for voting --- frontends/chat/src/components/Layouts/MainLayout.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frontends/chat/src/components/Layouts/MainLayout.tsx b/frontends/chat/src/components/Layouts/MainLayout.tsx index 89ffb80bd9..e60e893e89 100644 --- a/frontends/chat/src/components/Layouts/MainLayout.tsx +++ b/frontends/chat/src/components/Layouts/MainLayout.tsx @@ -94,6 +94,7 @@ const MainLayout = (props: LayoutProps) => { const handleReader = async ( reader: ReadableStreamDefaultReader, + messageId: string | null, ) => { let done = false; while (!done) { @@ -113,7 +114,7 @@ const MainLayout = (props: LayoutProps) => { const newMessage = { content: lastMessage.content + newText, - id: lastMessage.id, + id: messageId ? messageId : undefined, }; return [...prev.slice(0, prev.length - 1), newMessage]; }); @@ -231,7 +232,9 @@ const MainLayout = (props: LayoutProps) => { return; } - await handleReader(reader); + const messageId = res.headers.get("TR-Queryid"); + + await handleReader(reader, messageId); } catch (e) { console.error(e); } @@ -347,8 +350,9 @@ const MainLayout = (props: LayoutProps) => { if (!reader) { return; } + const messageId = response.headers.get("TR-Queryid"); setStreamingCompletion(true); - handleReader(reader).catch((e) => { + handleReader(reader, messageId).catch((e) => { console.error("Error handling reader: ", e); }); }) From e1346c72a65ffeeb72caa8e113efacf216958442 Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Tue, 8 Oct 2024 15:53:09 -0500 Subject: [PATCH 014/159] feat: bm25 settings when creating new dataset --- .../src/components/NewDatasetModal.tsx | 101 +++++++++++++++--- 1 file changed, 84 insertions(+), 17 deletions(-) diff --git a/frontends/dashboard/src/components/NewDatasetModal.tsx b/frontends/dashboard/src/components/NewDatasetModal.tsx index b4480baaf4..68ff337136 100644 --- a/frontends/dashboard/src/components/NewDatasetModal.tsx +++ b/frontends/dashboard/src/components/NewDatasetModal.tsx @@ -1,6 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { Accessor, createSignal, @@ -32,6 +29,9 @@ import { CrawlInterval, CrawlOptions, DistanceMetric } from "trieve-ts-sdk"; import { FaRegularCircleQuestion } from "solid-icons/fa"; import { Tooltip } from "shared/ui"; import { FiChevronDown, FiChevronUp } from "solid-icons/fi"; +import { createStore, SetStoreFunction, unwrap } from "solid-js/store"; +import { DatasetConfig } from "./dataset-settings/LegacySettingsWrapper"; +import { cn } from "shared/utils"; export interface NewDatasetModalProps { isOpen: Accessor; @@ -42,7 +42,7 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { const userContext = useContext(UserContext); const navigate = useNavigate(); - const [serverConfig, setServerConfig] = createSignal( + const [serverConfig, setServerConfig] = createStore( defaultServerEnvsConfiguration, ); const [crawlOptions, setCrawlOptions] = createSignal(); @@ -53,7 +53,7 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { const [fillWithExampleData, setFillWithExampleData] = createSignal(false); const createDataset = async () => { - const curServerConfig = serverConfig(); + const curServerConfig = unwrap(serverConfig); try { setIsLoading(true); @@ -124,7 +124,7 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { leaveFrom="opacity-100 scale-100" leaveTo="opacity-0 scale-95" > - +
{ e.preventDefault(); @@ -261,13 +261,10 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { name="embeddingSize" class="col-span-2 block w-full rounded-md border-[0.5px] border-neutral-300 bg-white px-3 py-1.5 shadow-sm placeholder:text-neutral-400 focus:outline-magenta-500 sm:text-sm sm:leading-6" value={ - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access availableEmbeddingModels.find( (model) => - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access model.id === - serverConfig().EMBEDDING_MODEL_NAME, - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + serverConfig.EMBEDDING_MODEL_NAME, )?.name ?? availableEmbeddingModels[0].name } onChange={(e) => { @@ -326,12 +323,9 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { name="distanceMetric" class="col-span-2 block w-full rounded-md border-[0.5px] border-neutral-300 bg-white px-3 py-1.5 shadow-sm placeholder:text-neutral-400 focus:outline-magenta-500 sm:text-sm sm:leading-6" value={ - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access availableDistanceMetrics.find( (model) => - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - model.id === serverConfig().DISTANCE_METRIC, - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + model.id === serverConfig.DISTANCE_METRIC, )?.name ?? availableDistanceMetrics[0].name } onChange={(e) => { @@ -411,9 +405,10 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { id="bm25" class="col-span-2 block w-full bg-white px-3 py-1.5 text-sm text-neutral-700" > - Dataset will have BM25 for keyword search. B, K, - and average token length can be configured via the - dataset options after creation. +

@@ -919,3 +914,75 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { ); }; export default NewDatasetModal; + +const BM25Settings = (props: { + config: DatasetConfig; + setConfig: SetStoreFunction; +}) => { + return ( +
+
+ + { + props.setConfig("BM25_ENABLED", e.currentTarget.checked); + }} + class="h-4 w-4 rounded border border-neutral-300 bg-neutral-100 p-1 accent-magenta-400 dark:border-neutral-900 dark:bg-neutral-800" + type="checkbox" + /> +
+
+
+ + { + props.setConfig("BM25_B", parseFloat(e.currentTarget.value)); + }} + class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 group-disabled:opacity-20 focus:outline-magenta-500 sm:text-sm sm:leading-6" + type="number" + /> +
+
+ + { + props.setConfig("BM25_K", parseFloat(e.currentTarget.value)); + }} + class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 group-disabled:opacity-20 focus:outline-magenta-500 sm:text-sm sm:leading-6" + type="number" + /> +
+
+ + { + props.setConfig( + "BM25_AVG_LEN", + parseFloat(e.currentTarget.value), + ); + }} + class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 group-disabled:opacity-20 focus:outline-magenta-500 sm:text-sm sm:leading-6" + type="number" + /> +
+
+
+ ); +}; From 3213aa193aba1772d6553c7abd5f7667d0a7b02b Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Tue, 8 Oct 2024 16:14:57 -0500 Subject: [PATCH 015/159] feat: validation for bm25 --- .../src/components/NewDatasetModal.tsx | 55 +++++++++++++++++-- .../src/pages/dataset/CrawlingSettings.tsx | 9 +-- frontends/dashboard/src/utils/validation.tsx | 20 +++++++ 3 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 frontends/dashboard/src/utils/validation.tsx diff --git a/frontends/dashboard/src/components/NewDatasetModal.tsx b/frontends/dashboard/src/components/NewDatasetModal.tsx index 68ff337136..0de7ebc135 100644 --- a/frontends/dashboard/src/components/NewDatasetModal.tsx +++ b/frontends/dashboard/src/components/NewDatasetModal.tsx @@ -32,12 +32,40 @@ import { FiChevronDown, FiChevronUp } from "solid-icons/fi"; import { createStore, SetStoreFunction, unwrap } from "solid-js/store"; import { DatasetConfig } from "./dataset-settings/LegacySettingsWrapper"; import { cn } from "shared/utils"; +import { ValidateFn, ErrorMsg } from "../utils/validation"; export interface NewDatasetModalProps { isOpen: Accessor; closeModal: () => void; } +const validate: ValidateFn = (value) => { + const errors: Record = {}; + + if (value.BM25_ENABLED) { + if (!value.BM25_B) { + errors.BM25_B = "B is required"; + } else if (value.BM25_B < 0) { + errors.BM25_B = "B must be greater than 0"; + } else if (value.BM25_B > 1) { + errors.BM25_B = "B must be less than 1"; + } + if (!value.BM25_K) { + errors.BM25_K = "K is required"; + } else if (value.BM25_K < 0) { + errors.BM25_K = "K must be greater than 0"; + } + if (!value.BM25_AVG_LEN) { + errors.BM25_AVG_LEN = "Average Length is required"; + } + } + + return { + errors, + valid: Object.values(errors).filter((v) => !!v).length === 0, + }; +}; + export const NewDatasetModal = (props: NewDatasetModalProps) => { const userContext = useContext(UserContext); const navigate = useNavigate(); @@ -52,8 +80,19 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { const [isLoading, setIsLoading] = createSignal(false); const [fillWithExampleData, setFillWithExampleData] = createSignal(false); + const [errors, setErrors] = createStore< + ReturnType>["errors"] + >({}); + const createDataset = async () => { const curServerConfig = unwrap(serverConfig); + const validateResult = validate(curServerConfig); + if (validateResult.valid) { + setErrors({}); + } else { + setErrors(validateResult.errors); + return; + } try { setIsLoading(true); @@ -406,6 +445,7 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { class="col-span-2 block w-full bg-white px-3 py-1.5 text-sm text-neutral-700" > @@ -918,6 +958,7 @@ export default NewDatasetModal; const BM25Settings = (props: { config: DatasetConfig; setConfig: SetStoreFunction; + errors: ReturnType>["errors"]; }) => { return (
@@ -942,36 +983,37 @@ const BM25Settings = (props: { { props.setConfig("BM25_B", parseFloat(e.currentTarget.value)); }} class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 group-disabled:opacity-20 focus:outline-magenta-500 sm:text-sm sm:leading-6" type="number" /> +
{ props.setConfig("BM25_K", parseFloat(e.currentTarget.value)); }} class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 group-disabled:opacity-20 focus:outline-magenta-500 sm:text-sm sm:leading-6" type="number" /> +
{ props.setConfig( "BM25_AVG_LEN", @@ -981,6 +1023,7 @@ const BM25Settings = (props: { class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 group-disabled:opacity-20 focus:outline-magenta-500 sm:text-sm sm:leading-6" type="number" /> +
diff --git a/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx b/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx index b722aba899..f80c943718 100644 --- a/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx +++ b/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx @@ -13,6 +13,7 @@ import { toTitleCase } from "../../analytics/utils/titleCase"; import { Spacer } from "../../components/Spacer"; import { UserContext } from "../../contexts/UserContext"; import { createToast } from "../../components/ShowToasts"; +import { ValidateFn } from "../../utils/validation"; const defaultCrawlOptions: CrawlOptions = { boost_titles: false, @@ -120,14 +121,6 @@ const Error = (props: { error: string | null | undefined }) => { ); }; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type ValidateFn> = (value: T) => { - errors: { - [key in keyof T]: string | undefined; - }; - valid: boolean; -}; - const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { const [options, setOptions] = createStore(props.initialCrawlingSettings); const [errors, setErrors] = createStore< diff --git a/frontends/dashboard/src/utils/validation.tsx b/frontends/dashboard/src/utils/validation.tsx new file mode 100644 index 0000000000..f37621516f --- /dev/null +++ b/frontends/dashboard/src/utils/validation.tsx @@ -0,0 +1,20 @@ +import { Show } from "solid-js"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ValidateFn> = (value: T) => { + errors: { + [key in keyof T]: string | undefined; + }; + valid: boolean; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ValidateErrors> = ReturnType["errors"]; + +export const ErrorMsg = (props: { error: string | null | undefined }) => { + return ( + +
{props.error}
+
+ ); +}; From 4471cb8aa67a9fc3724fead41672737abee6839c Mon Sep 17 00:00:00 2001 From: cdxker Date: Tue, 8 Oct 2024 12:35:41 -0700 Subject: [PATCH 016/159] feature: clone topic and message route --- server/src/handlers/topic_handler.rs | 66 +++++++++++++++++++++++- server/src/lib.rs | 6 +++ server/src/operators/message_operator.rs | 14 ++--- 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/server/src/handlers/topic_handler.rs b/server/src/handlers/topic_handler.rs index ca00657cf4..54245d743f 100644 --- a/server/src/handlers/topic_handler.rs +++ b/server/src/handlers/topic_handler.rs @@ -3,10 +3,10 @@ use crate::{ errors::ServiceError, handlers::auth_handler::AdminOnly, operators::{ - message_operator::get_topic_string, + message_operator::{create_messages_query, get_topic_messages, get_topic_string}, topic_operator::{ create_topic_query, delete_topic_query, get_all_topics_for_owner_id_query, - update_topic_query, + get_topic_query, update_topic_query, }, }, }; @@ -89,6 +89,68 @@ pub async fn create_topic( Ok(HttpResponse::Ok().json(new_topic1)) } +#[derive(Debug, Deserialize, Serialize, ToSchema)] +pub struct CloneTopicReqPayload { + /// The topic_id to clone from + pub topic_id: uuid::Uuid, + /// The name of the topic. If this is not provided, the topic name is the same as the previous topic + pub name: Option, + /// The owner_id of the topic. This is typically a browser fingerprint or your user's id. It is used to group topics together for a user. + pub owner_id: String, +} + +/// Clone Topic +/// +/// Create a new chat topic from a `topic_id`. The new topic will be attched to the owner_id and act as a coordinator for conversation message history of gen-AI chat sessions. Auth'ed user or api key must have an admin or owner role for the specified dataset's organization. +#[utoipa::path( + post, + path = "/topic/clone", + context_path = "/api", + tag = "Topic", + request_body(content = CloneTopicReqPayload, description = "JSON request payload to create chat topic", content_type = "application/json"), + responses( + (status = 200, description = "The JSON response payload containing the created topic", body = Topic), + (status = 400, description = "Topic name empty or a service error", body = ErrorResponseBody), + ), + params( + ("TR-Dataset" = String, Header, description = "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid."), + ), + security( + ("ApiKey" = ["admin"]), + ) +)] +#[tracing::instrument(skip(pool))] +pub async fn clone_topic( + data: web::Json, + user: AdminOnly, + dataset_org_plan_sub: DatasetAndOrgWithSubAndPlan, + pool: web::Data, +) -> Result { + let data = data.into_inner(); + + // get topic from topic_id + let original_topic = + get_topic_query(data.topic_id, dataset_org_plan_sub.dataset.id, &pool).await?; + + let topic_name = data.name.unwrap_or(original_topic.name); + + let new_topic = Topic::from_details(topic_name, data.owner_id, dataset_org_plan_sub.dataset.id); + + create_topic_query(new_topic.clone(), &pool).await?; + + let mut old_messages = + get_topic_messages(original_topic.id, dataset_org_plan_sub.dataset.id, &pool).await?; + + old_messages.iter_mut().for_each(|message| { + message.topic_id = new_topic.id; + message.id = uuid::Uuid::new_v4(); + }); + + create_messages_query(old_messages, &pool).await?; + + Ok(HttpResponse::Ok().json(new_topic)) +} + #[derive(Debug, Deserialize, Serialize, ToSchema)] pub struct DeleteTopicData { /// The id of the topic to target. diff --git a/server/src/lib.rs b/server/src/lib.rs index f489af064c..a2605d4106 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -163,6 +163,7 @@ impl Modify for SecurityAddon { handlers::topic_handler::create_topic, handlers::topic_handler::delete_topic, handlers::topic_handler::update_topic, + handlers::topic_handler::clone_topic, handlers::topic_handler::get_all_topics_for_owner_id, handlers::message_handler::create_message, handlers::message_handler::get_all_topic_messages, @@ -250,6 +251,7 @@ impl Modify for SecurityAddon { schemas( handlers::auth_handler::AuthQuery, handlers::topic_handler::CreateTopicReqPayload, + handlers::topic_handler::CloneTopicReqPayload, handlers::topic_handler::DeleteTopicData, handlers::topic_handler::UpdateTopicReqPayload, handlers::message_handler::CreateMessageReqPayload, @@ -836,6 +838,10 @@ pub fn main() -> std::io::Result<()> { .route(web::post().to(handlers::topic_handler::create_topic)) .route(web::put().to(handlers::topic_handler::update_topic)), ) + .service( + web::resource("/topic/clone") + .route(web::post().to(handlers::topic_handler::clone_topic)) + ) .service( web::resource("/topic/{topic_id}") .route(web::delete().to(handlers::topic_handler::delete_topic)), diff --git a/server/src/operators/message_operator.rs b/server/src/operators/message_operator.rs index c8ac5a7b67..04f5cde69c 100644 --- a/server/src/operators/message_operator.rs +++ b/server/src/operators/message_operator.rs @@ -65,8 +65,8 @@ pub async fn get_topic_messages( } #[tracing::instrument(skip(pool))] -pub async fn create_message_query( - new_message: Message, +pub async fn create_messages_query( + new_messages: Vec, pool: &web::Data, ) -> Result<(), ServiceError> { use crate::data::schema::messages::dsl::messages; @@ -77,7 +77,7 @@ pub async fn create_message_query( .map_err(|_| ServiceError::BadRequest("Could not get database connection".to_string()))?; diesel::insert_into(messages) - .values(&new_message) + .values(&new_messages) .execute(&mut conn) .await .map_err(|_db_error| { @@ -133,13 +133,13 @@ pub async fn create_topic_message_query( ) .await?; ret_messages.extend(vec![system_message.clone()]); - create_message_query(system_message, pool).await?; + create_messages_query(vec![system_message], pool).await?; previous_messages_len = 1; } new_message_copy.sort_order = previous_messages_len as i32; - create_message_query(new_message_copy.clone(), pool).await?; + create_messages_query(vec![new_message_copy.clone()], pool).await?; ret_messages.push(new_message_copy); Ok(ret_messages) @@ -626,7 +626,7 @@ pub async fn stream_response( .send(ClickHouseEvent::RagQueryEvent(clickhouse_rag_event.clone())) .await; - create_message_query(new_message, &pool).await?; + create_messages_query(vec![new_message], &pool).await?; return Ok(HttpResponse::Ok() .insert_header(("TR-QueryID", query_id.to_string())) @@ -690,7 +690,7 @@ pub async fn stream_response( .send(ClickHouseEvent::RagQueryEvent(clickhouse_rag_event.clone())) .await; - let _ = create_message_query(new_message, &pool).await; + let _ = create_messages_query(vec![new_message], &pool).await; }); let chunk_stream = stream::iter(vec![Ok(Bytes::from(chunk_metadatas_stringified1))]); From 71c20d65d0295d6af7fbd8dc758095e75f4f1b51 Mon Sep 17 00:00:00 2001 From: cdxker Date: Tue, 8 Oct 2024 14:52:16 -0700 Subject: [PATCH 017/159] feature: clone topic in chat frontend --- .../chat/src/components/Navbar/Sidebar.tsx | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/frontends/chat/src/components/Navbar/Sidebar.tsx b/frontends/chat/src/components/Navbar/Sidebar.tsx index dc6bbf2cf6..64a4c7a6a5 100644 --- a/frontends/chat/src/components/Navbar/Sidebar.tsx +++ b/frontends/chat/src/components/Navbar/Sidebar.tsx @@ -5,6 +5,7 @@ import { BiRegularPlus, BiRegularTrash, BiRegularX, + BiRegularDuplicate, } from "solid-icons/bi"; import { Accessor, @@ -81,6 +82,34 @@ export const Sidebar = (props: SidebarProps) => { await props.refetchTopics(); }; + const cloneTopic = async () => { + const dataset = userContext.currentDataset?.(); + if (!dataset) return; + + const res = await fetch(`${apiHost}/topic/clone`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "TR-Dataset": dataset.dataset.id, + }, + body: JSON.stringify({ + topic_id: props.currentTopic()?.id, + owner_id: userContext.user?.()?.id, + }), + credentials: "include", + }); + + if (res.ok) { + await props.refetchTopics(); + } else { + createToast({ + type: "error", + message: "Error deleting topic", + }); + return; + } + }; + const deleteSelected = async () => { const dataset = userContext.currentDataset?.(); if (!dataset) return; @@ -218,6 +247,16 @@ export const Sidebar = (props: SidebarProps) => {
{props.currentTopic()?.id === topic.id && (
+ @@ -234,6 +274,7 @@ export const Sidebar = (props: SidebarProps) => { void deleteSelected(); }} class="text-lg hover:text-red-500" + title="Delete chat" > From 8b6fe7ed261f75b68fa46dd13c7eed513340d9eb Mon Sep 17 00:00:00 2001 From: cdxker Date: Tue, 8 Oct 2024 15:06:08 -0700 Subject: [PATCH 018/159] feature: add /topic/clone to the ts-sdk --- clients/ts-sdk/openapi.json | 84 +++++++++++++++++++++ clients/ts-sdk/src/functions/topic/index.ts | 30 ++++++++ clients/ts-sdk/src/types.gen.ts | 43 +++++++++++ 3 files changed, 157 insertions(+) diff --git a/clients/ts-sdk/openapi.json b/clients/ts-sdk/openapi.json index 97c7aa4b3f..3a419e2e21 100644 --- a/clients/ts-sdk/openapi.json +++ b/clients/ts-sdk/openapi.json @@ -5451,6 +5451,67 @@ ] } }, + "/api/topic/clone": { + "post": { + "tags": [ + "Topic" + ], + "summary": "Clone Topic", + "description": "Create a new chat topic from a `topic_id`. The new topic will be attched to the owner_id and act as a coordinator for conversation message history of gen-AI chat sessions. Auth'ed user or api key must have an admin or owner role for the specified dataset's organization.", + "operationId": "clone_topic", + "parameters": [ + { + "name": "TR-Dataset", + "in": "header", + "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "JSON request payload to create chat topic", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CloneTopicReqPayload" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "The JSON response payload containing the created topic", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Topic" + } + } + } + }, + "400": { + "description": "Topic name empty or a service error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseBody" + } + } + } + } + }, + "security": [ + { + "ApiKey": [ + "admin" + ] + } + ] + } + }, "/api/topic/owner/{owner_id}": { "get": { "tags": [ @@ -7093,6 +7154,29 @@ } } }, + "CloneTopicReqPayload": { + "type": "object", + "required": [ + "topic_id", + "owner_id" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the topic. If this is not provided, the topic name is the same as the previous topic", + "nullable": true + }, + "owner_id": { + "type": "string", + "description": "The owner_id of the topic. This is typically a browser fingerprint or your user's id. It is used to group topics together for a user." + }, + "topic_id": { + "type": "string", + "format": "uuid", + "description": "The topic_id to clone from" + } + } + }, "ClusterAnalytics": { "oneOf": [ { diff --git a/clients/ts-sdk/src/functions/topic/index.ts b/clients/ts-sdk/src/functions/topic/index.ts index e9b7a9e2f4..eb585fb365 100644 --- a/clients/ts-sdk/src/functions/topic/index.ts +++ b/clients/ts-sdk/src/functions/topic/index.ts @@ -8,6 +8,7 @@ import { DeleteTopicData2, GetAllTopicsForOwnerIdData, UpdateTopicReqPayload, + CloneTopicReqPayload } from "../../fetch-client"; import { TrieveSDK } from "../../sdk"; @@ -40,6 +41,35 @@ export async function createTopic( ); } +/** + * Clone a chat topic and all its messages to a new topic. Topics are attached to a owner_id’s and act as a coordinator for conversation message history of gen-AI chat sessions. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization. + * + * Example: + * ```js + *const data = await trieve.cloneTopic({ + first_user_message: "hello", + name: "Test", + owner_id: "3c90c3cc-1d76-27198-8888-8dd25736052a", +}); + * ``` + */ +export async function cloneTopic( + /** @hidden */ + this: TrieveSDK, + data: CloneTopicReqPayload, + signal?: AbortSignal +) { + return await this.trieve.fetch( + "/api/topic/clone", + "post", + { + data, + datasetId: this.datasetId, + }, + signal + ); +} + /** * Update an existing chat topic. Currently, only the name of the topic can be updated. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization. * diff --git a/clients/ts-sdk/src/types.gen.ts b/clients/ts-sdk/src/types.gen.ts index a3cf5cd82a..e1819a33be 100644 --- a/clients/ts-sdk/src/types.gen.ts +++ b/clients/ts-sdk/src/types.gen.ts @@ -345,6 +345,21 @@ export type ChunkWithPosition = { position: number; }; +export type CloneTopicReqPayload = { + /** + * The name of the topic. If this is not provided, the topic name is the same as the previous topic + */ + name?: (string) | null; + /** + * The owner_id of the topic. This is typically a browser fingerprint or your user's id. It is used to group topics together for a user. + */ + owner_id: string; + /** + * The topic_id to clone from + */ + topic_id: string; +}; + export type ClusterAnalytics = { filter?: ((ClusterAnalyticsFilter) | null); type: 'cluster_topics'; @@ -3761,6 +3776,19 @@ export type UpdateTopicData = { export type UpdateTopicResponse = (void); +export type CloneTopicData = { + /** + * JSON request payload to create chat topic + */ + requestBody: CloneTopicReqPayload; + /** + * The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid. + */ + trDataset: string; +}; + +export type CloneTopicResponse = (Topic); + export type GetAllTopicsForOwnerIdData = { /** * The owner_id to get topics of; A common approach is to use a browser fingerprint or your user's id @@ -5149,6 +5177,21 @@ export type $OpenApiTs = { }; }; }; + '/api/topic/clone': { + post: { + req: CloneTopicData; + res: { + /** + * The JSON response payload containing the created topic + */ + 200: Topic; + /** + * Topic name empty or a service error + */ + 400: ErrorResponseBody; + }; + }; + }; '/api/topic/owner/{owner_id}': { get: { req: GetAllTopicsForOwnerIdData; From 1f12579c56bc53b422afad605dff64cd2bf710b5 Mon Sep 17 00:00:00 2001 From: cdxker Date: Fri, 4 Oct 2024 18:13:42 -0700 Subject: [PATCH 019/159] feature: add ImageConfig for sending images to the llm --- server/src/data/models.rs | 15 ++++++ server/src/handlers/chunk_handler.rs | 65 ++++++++++++++++++------ server/src/lib.rs | 1 + server/src/operators/message_operator.rs | 56 +++++++++++++++++--- 4 files changed, 115 insertions(+), 22 deletions(-) diff --git a/server/src/data/models.rs b/server/src/data/models.rs index 09052479bf..55ca10777f 100644 --- a/server/src/data/models.rs +++ b/server/src/data/models.rs @@ -5609,6 +5609,19 @@ pub struct TypoRange { pub max: Option, } +#[derive(Debug, Serialize, Deserialize, ToSchema, Clone, Default)] +#[schema(example = json!({ + "use_images": true, + "images_per_chunk": 1 +}))] +/// Configuration for sending images to the llm +pub struct ImageConfig { + /// This sends images to the llm if chunk_metadata.image_urls has some value, the call will error if the model is not a vision LLM model. default: false + pub use_images: Option, + /// The number of Images to send to the llm per chunk that is fetched more images may slow down llm inference time. default: 5 + pub images_per_chunk: Option, +} + #[derive(Debug, Serialize, Deserialize, ToSchema, Clone, Default)] /// LLM options to use for the completion. If not specified, this defaults to the dataset's LLM options. pub struct LLMOptions { @@ -5628,6 +5641,8 @@ pub struct LLMOptions { pub stop_tokens: Option>, /// Optionally, override the system prompt in dataset server settings. pub system_prompt: Option, + /// Configuration for sending images to the llm + pub image_config: Option, } // Helper function to extract SortOptions and HighlightOptions diff --git a/server/src/handlers/chunk_handler.rs b/server/src/handlers/chunk_handler.rs index da9773c2b3..872088becc 100644 --- a/server/src/handlers/chunk_handler.rs +++ b/server/src/handlers/chunk_handler.rs @@ -2,10 +2,11 @@ use super::auth_handler::{AdminOnly, LoggedUser}; use crate::data::models::{ ChatMessageProxy, ChunkMetadata, ChunkMetadataStringTagSet, ChunkMetadataWithScore, ConditionType, CountSearchMethod, DatasetAndOrgWithSubAndPlan, DatasetConfiguration, GeoInfo, - HighlightOptions, IngestSpecificChunkMetadata, Pool, QueryTypes, RagQueryEventClickhouse, - RecommendType, RecommendationEventClickhouse, RecommendationStrategy, RedisPool, ScoreChunk, - ScoreChunkDTO, SearchMethod, SearchQueryEventClickhouse, SlimChunkMetadataWithScore, - SortByField, SortOptions, TypoOptions, UnifiedId, UpdateSpecificChunkMetadata, + HighlightOptions, ImageConfig, IngestSpecificChunkMetadata, Pool, QueryTypes, + RagQueryEventClickhouse, RecommendType, RecommendationEventClickhouse, RecommendationStrategy, + RedisPool, ScoreChunk, ScoreChunkDTO, SearchMethod, SearchQueryEventClickhouse, + SlimChunkMetadataWithScore, SortByField, SortOptions, TypoOptions, UnifiedId, + UpdateSpecificChunkMetadata, }; use crate::errors::ServiceError; use crate::get_env; @@ -33,6 +34,7 @@ use openai_dive::v1::api::Client; use openai_dive::v1::resources::chat::{ ChatCompletionParameters, ChatMessage, ChatMessageContent, Role, }; +use openai_dive::v1::resources::chat::{ImageUrl, ImageUrlType}; use openai_dive::v1::resources::shared::StopToken; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -2335,6 +2337,8 @@ pub struct GenerateOffChunksReqPayload { pub stop_tokens: Option>, /// User ID is the id of the user who is making the request. This is used to track user interactions with the RAG results. pub user_id: Option, + /// Configuration for sending images to the llm + pub image_config: Option, } /// RAG on Specified Chunks @@ -2478,6 +2482,31 @@ pub async fn generate_off_chunks( tool_call_id: None, }); + if let Some(image_config) = &data.image_config { + if image_config.use_images.unwrap_or(false) { + if let Some(image_urls) = bookmark.image_urls.clone() { + let urls = image_urls + .iter() + .filter_map(|image| image.clone()) + .take(image_config.images_per_chunk.unwrap_or(5)) + .map(|url| ImageUrl { + r#type: "image_url".to_string(), + text: None, + image_url: ImageUrlType { url, detail: None }, + }) + .collect::>(); + + messages.push(ChatMessage { + role: Role::User, + content: ChatMessageContent::ImageUrl(urls), + tool_calls: None, + name: None, + tool_call_id: None, + }); + } + } + } + messages.push(ChatMessage { role: Role::Assistant, content: ChatMessageContent::Text("".to_string()), @@ -2628,20 +2657,24 @@ pub async fn generate_off_chunks( }); let completion_stream = stream.map(move |response| -> Result { - if let Ok(response) = response { - let chat_content = match response.choices.get(0) { - Some(choice) => choice.delta.content.clone(), - None => Some("failed to get response completion".to_string()), - }; - if let Some(message) = chat_content.clone() { - s.send(message).unwrap(); + match response { + Ok(response) => { + let chat_content = match response.choices.get(0) { + Some(choice) => choice.delta.content.clone(), + None => Some("failed to get response completion".to_string()), + }; + if let Some(message) = chat_content.clone() { + s.send(message).unwrap(); + } + + Ok(Bytes::from(chat_content.unwrap_or("".to_string()))) } - return Ok(Bytes::from(chat_content.unwrap_or("".to_string()))); + Err(e) => Err(ServiceError::InternalServerError(format!( + "Model Response Error. Please try again later. {:?}", + e + )) + .into()), } - Err(ServiceError::InternalServerError( - "Model Response Error. Please try again later.".into(), - ) - .into()) }); Ok(HttpResponse::Ok() diff --git a/server/src/lib.rs b/server/src/lib.rs index a2605d4106..455ee379fb 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -432,6 +432,7 @@ impl Modify for SecurityAddon { data::models::QdrantSortBy, data::models::SortOptions, data::models::LLMOptions, + data::models::ImageConfig, data::models::HighlightOptions, data::models::TypoOptions, data::models::TypoRange, diff --git a/server/src/operators/message_operator.rs b/server/src/operators/message_operator.rs index 04f5cde69c..7f652ff492 100644 --- a/server/src/operators/message_operator.rs +++ b/server/src/operators/message_operator.rs @@ -1,6 +1,6 @@ use crate::data::models::{ - self, ChunkMetadataStringTagSet, ChunkMetadataTypes, Dataset, DatasetConfiguration, QueryTypes, - RagQueryEventClickhouse, RedisPool, SearchMethod, + self, ChunkMetadataStringTagSet, ChunkMetadataTypes, Dataset, DatasetConfiguration, LLMOptions, + QueryTypes, RagQueryEventClickhouse, RedisPool, SearchMethod, }; use crate::diesel::prelude::*; use crate::get_env; @@ -19,6 +19,7 @@ use crossbeam_channel::unbounded; use diesel_async::RunQueryDsl; use futures::StreamExt; use futures_util::stream; +use openai_dive::v1::resources::chat::{ImageUrl, ImageUrlType}; use openai_dive::v1::{ api::Client, resources::{ @@ -484,8 +485,19 @@ pub async fn stream_response( rag_content, )); + let images: Vec = chunk_metadatas + .iter() + .filter_map(|chunk| chunk.image_urls.clone()) + .flat_map(|image_urls| { + image_urls + .iter() + .filter_map(|image| image.clone()) + .collect::>() + }) + .collect(); + // replace the last message with the last message with evidence - let open_ai_messages: Vec = openai_messages + let mut open_ai_messages: Vec = openai_messages .clone() .into_iter() .enumerate() @@ -504,6 +516,37 @@ pub async fn stream_response( }) .collect(); + if !images.is_empty() { + if let Some(LLMOptions { + image_config: Some(ref image_config), + .. + }) = create_message_req_payload.llm_options + { + if image_config.use_images.unwrap_or(false) { + open_ai_messages.push(ChatMessage { + name: None, + role: Role::User, + tool_calls: None, + tool_call_id: None, + content: ChatMessageContent::ImageUrl( + images + .iter() + .take(image_config.images_per_chunk.unwrap_or(5)) + .map(|url| ImageUrl { + r#type: "image_url".to_string(), + text: None, + image_url: ImageUrlType { + url: url.to_string(), + detail: None, + }, + }) + .collect(), + ), + }) + } + } + } + let mut parameters = ChatCompletionParameters { model: chosen_model, messages: open_ai_messages, @@ -706,9 +749,10 @@ pub async fn stream_response( } return Ok(Bytes::from(chat_content.unwrap_or("".to_string()))); } - Err(ServiceError::InternalServerError( - "Model Response Error. Please try again later.".into(), - ) + Err(ServiceError::InternalServerError(format!( + "Model Response Error. Please try again later. {:?}", + response + )) .into()) }); From c2db61cb3c7544754a6786c0453666ea98325e48 Mon Sep 17 00:00:00 2001 From: cdxker Date: Tue, 8 Oct 2024 16:09:43 -0700 Subject: [PATCH 020/159] feature: added useImages to the frontend as a parameter --- .../src/components/Layouts/MainLayout.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/frontends/chat/src/components/Layouts/MainLayout.tsx b/frontends/chat/src/components/Layouts/MainLayout.tsx index e60e893e89..7349da878b 100644 --- a/frontends/chat/src/components/Layouts/MainLayout.tsx +++ b/frontends/chat/src/components/Layouts/MainLayout.tsx @@ -77,6 +77,11 @@ const MainLayout = (props: LayoutProps) => { boolean | null >(null); + const [useImages, setUseImages] = createSignal< + boolean | null + >(null); + + const [streamCompletionsFirst, setStreamCompletionsFirst] = createSignal< boolean | null >(null); @@ -221,6 +226,9 @@ const MainLayout = (props: LayoutProps) => { system_prompt: systemPrompt(), llm_options: { completion_first: streamCompletionsFirst(), + image_options: { + use_images: useImages() + } }, search_type: searchType(), }), @@ -339,6 +347,9 @@ const MainLayout = (props: LayoutProps) => { topic_id: props.selectedTopic?.id, llm_options: { completion_first: streamCompletionsFirst(), + image_options: { + use_images: useImages() + } }, }), }) @@ -449,6 +460,20 @@ const MainLayout = (props: LayoutProps) => { }} />
+
+ + { + setUseImages(e.target.checked); + }} + /> +
Date: Mon, 7 Oct 2024 20:17:50 -0700 Subject: [PATCH 021/159] feature: headless analytics --- .../components/SingleQueryInfo/ResultCard.tsx | 6 +- .../components/SingleQueryInfo/index.tsx | 95 ++++-- .../components/SingleRagInfo/index.tsx | 156 +++++---- .../SingleRecommendationInfo/index.tsx | 103 ++++-- frontends/shared/types.ts | 31 +- .../down.sql | 1 + .../up.sql | 1 + server/src/data/models.rs | 296 ++++++++++++++++-- server/src/handlers/analytics_handler.rs | 48 ++- server/src/handlers/chunk_handler.rs | 12 +- server/src/lib.rs | 3 + server/src/operators/message_operator.rs | 34 +- 12 files changed, 580 insertions(+), 206 deletions(-) create mode 100644 server/ch_migrations/1728342969_create_json_results_for_rag/down.sql create mode 100644 server/ch_migrations/1728342969_create_json_results_for_rag/up.sql diff --git a/frontends/dashboard/src/analytics/components/SingleQueryInfo/ResultCard.tsx b/frontends/dashboard/src/analytics/components/SingleQueryInfo/ResultCard.tsx index c005882cb3..5f66596e3d 100644 --- a/frontends/dashboard/src/analytics/components/SingleQueryInfo/ResultCard.tsx +++ b/frontends/dashboard/src/analytics/components/SingleQueryInfo/ResultCard.tsx @@ -1,11 +1,11 @@ -import { SearchQueryEvent } from "shared/types"; +import { ScoreChunkDTO } from "shared/types"; import { FullScreenModal, JsonInput } from "shared/ui"; import { IoCode } from "solid-icons/io"; import { createMemo, createSignal, Show } from "solid-js"; import { z } from "zod"; interface ResultCardProps { - result: SearchQueryEvent["results"][0]; + result: ScoreChunkDTO; } const usefulMetadataSchema = z.object({ @@ -43,7 +43,7 @@ export const ResultCard = (props: ResultCardProps) => {
- +
Score: {props?.result?.score?.toFixed(5)}
diff --git a/frontends/dashboard/src/analytics/components/SingleQueryInfo/index.tsx b/frontends/dashboard/src/analytics/components/SingleQueryInfo/index.tsx index 3830b7669b..b84fc13b99 100644 --- a/frontends/dashboard/src/analytics/components/SingleQueryInfo/index.tsx +++ b/frontends/dashboard/src/analytics/components/SingleQueryInfo/index.tsx @@ -10,6 +10,7 @@ import { DataSquare } from "./DataSquare"; import { DatasetContext } from "../../../contexts/DatasetContext"; import { UserContext } from "../../../contexts/UserContext"; import { IoArrowBackOutline } from "solid-icons/io"; +import { isScoreChunkDTO } from "shared/types"; interface SingleQueryProps { queryId: string; @@ -59,13 +60,25 @@ export const SingleQuery = (props: SingleQueryProps) => { label="Dataset" value={datasetName() || props.data.dataset_id} /> - - - - + + + + 0.0}> + + + 0.0}> + + + 0 || + props.data.query_rating.note) + } + > {
- -
- No Data.
} - each={props.data.results} - > - {(result) => } - - -
- -
    - props.data.request_params[key], - )} - > - {(key) => ( -
  • - {key}: - {props.data.request_params[key] as string}{" "} -
  • - )} -
    -
-
+ + +
+ No Data.
} + each={props.data.results} + > + {(result) => { + if (isScoreChunkDTO(result)) { + return ; + } else { + return ( +
+
+                          {JSON.stringify(result, null, 2)}
+                        
+
+ ); + } + }} + + +
+
+ + +
    + props.data.request_params[key], + )} + > + {(key) => ( +
  • + {key}: + {props.data.request_params[key] as string}{" "} +
  • + )} +
    +
+
+
); }; diff --git a/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx b/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx index 2e3dfcdf3b..f9d3668729 100644 --- a/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx +++ b/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx @@ -1,4 +1,7 @@ -import { createQuery } from "@tanstack/solid-query"; +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { createQuery, CreateQueryResult } from "@tanstack/solid-query"; import { getRagQuery, getSearchQuery } from "../../api/analytics"; import { createMemo, For, Show, useContext } from "solid-js"; import { format } from "date-fns"; @@ -10,6 +13,7 @@ import { DataSquare } from "../SingleQueryInfo/DataSquare"; import { DatasetContext } from "../../../contexts/DatasetContext"; import { UserContext } from "../../../contexts/UserContext"; import { IoArrowBackOutline } from "solid-icons/io"; +import { isScoreChunkDTO, SearchQueryEvent } from "shared/types"; interface SingleRAGQueryProps { queryId: string; @@ -24,19 +28,22 @@ export const SingleRAGQuery = (props: SingleRAGQueryProps) => { }, })); - const search_query = createQuery(() => ({ - queryKey: ["single_query", rag_query.data?.search_id], - queryFn: () => { - return getSearchQuery( - dataset.datasetId(), - rag_query.data?.search_id ?? "", - ); - }, - })); + let search_query: CreateQueryResult | undefined; + if (rag_query.data?.search_id !== "00000000-0000-0000-0000-000000000000") { + search_query = createQuery(() => ({ + queryKey: ["single_query", rag_query.data?.search_id], + queryFn: () => { + return getSearchQuery( + dataset.datasetId(), + rag_query.data?.search_id ?? "", + ); + }, + })); + } const DataDisplay = (props: { rag_data: NonNullable; - search_data: NonNullable; + search_data?: SearchQueryEvent; }) => { const datasetName = createMemo(() => { const userContext = useContext(UserContext); @@ -72,15 +79,34 @@ export const SingleRAGQuery = (props: SingleRAGQueryProps) => { label="Dataset" value={datasetName() || props.rag_data.dataset_id} /> - - - + + + + 0.0}> + + + 0 || + props.rag_data.query_rating.note) + } + > { - -
    -
  • {props.rag_data.llm_response}
  • -
-
- -
- No Data.
} - each={props.search_data.results} - > - {(result) => } - - -
- -
    - props.search_data.request_params[key], - )} - > - {(key) => ( -
  • - {key}: - {props.search_data.request_params[key] as string}{" "} -
  • - )} -
    -
-
+ + +
    +
  • {props.rag_data.llm_response}
  • +
+
+
+ + +
+ No Data.
} + each={ + props.search_data + ? props.search_data.results + : props.rag_data.results + } + > + {(result) => { + if (isScoreChunkDTO(result)) { + return ; + } else { + return ( +
+
+                          {JSON.stringify(result, null, 2)}
+                        
+
+ ); + } + }} + + +
+
+ + +
    + props.search_data?.request_params[key])} + > + {(key) => ( +
  • + {key}: + {props.search_data?.request_params[key] as string}{" "} +
  • + )} +
    +
+
+
); }; @@ -126,11 +174,7 @@ export const SingleRAGQuery = (props: SingleRAGQueryProps) => { return ( {(rag_data) => ( - - {(search_data) => ( - - )} - + )} ); diff --git a/frontends/dashboard/src/analytics/components/SingleRecommendationInfo/index.tsx b/frontends/dashboard/src/analytics/components/SingleRecommendationInfo/index.tsx index 21130c26f4..0188072675 100644 --- a/frontends/dashboard/src/analytics/components/SingleRecommendationInfo/index.tsx +++ b/frontends/dashboard/src/analytics/components/SingleRecommendationInfo/index.tsx @@ -10,6 +10,7 @@ import { DataSquare } from "../SingleQueryInfo/DataSquare"; import { DatasetContext } from "../../../contexts/DatasetContext"; import { UserContext } from "../../../contexts/UserContext"; import { IoArrowBackOutline } from "solid-icons/io"; +import { isScoreChunkDTO } from "shared/types"; interface SingleRecommendationQueryProps { queryId: string; @@ -88,42 +89,76 @@ export const SingleRecommendationQuery = ( label="Dataset" value={datasetName() || props.recommendation_data.dataset_id} /> - - + + + + 0.0}> + + - -
- No Data.
} - each={props.recommendation_data.results} - > - {(result) => } - - -
- -
    - props.recommendation_data.request_params[key])} - > - {(key) => ( -
  • - {key}: - {props.recommendation_data.request_params[key] as string}{" "} -
  • - )} -
    -
-
+ + +
+ No Data.
} + each={props.recommendation_data.results} + > + {(result) => { + if (isScoreChunkDTO({ metadata: [result] })) { + return ; + } else { + return ( +
+
+                          {JSON.stringify(result, null, 2)}
+                        
+
+ ); + } + }} + + +
+
+ + +
    + props.recommendation_data.request_params[key], + )} + > + {(key) => ( +
  • + {key}: + { + props.recommendation_data.request_params[key] as string + }{" "} +
  • + )} +
    +
+
+
); }; diff --git a/frontends/shared/types.ts b/frontends/shared/types.ts index 6053bc3449..401ff6d347 100644 --- a/frontends/shared/types.ts +++ b/frontends/shared/types.ts @@ -476,11 +476,7 @@ export interface SearchQueryEvent { request_params: Record; latency: number; top_score: number; - results: { - highlights?: unknown; - metadata: ChunkMetadataStringTagSet[]; - score?: number; - }[]; + results: ScoreChunkDTO[] | object[]; dataset_id: string; created_at: string; query_rating?: { @@ -489,6 +485,31 @@ export interface SearchQueryEvent { }; } +export interface ScoreChunkDTO { + highlights?: unknown; + metadata: ChunkMetadataStringTagSet[]; + score?: number; +} + +export function isScoreChunkDTO(data: unknown): data is ScoreChunkDTO { + if (typeof data !== "object" || data === null) { + return false; + } + + if (!Array.isArray((data as ScoreChunkDTO).metadata)) { + return false; + } + + if ( + (data as ScoreChunkDTO).score !== undefined && + typeof (data as ScoreChunkDTO).score !== "number" + ) { + return false; + } + + return true; +} + export interface RecommendationEvent { created_at: string; dataset_id: string; diff --git a/server/ch_migrations/1728342969_create_json_results_for_rag/down.sql b/server/ch_migrations/1728342969_create_json_results_for_rag/down.sql new file mode 100644 index 0000000000..2ef7b2222d --- /dev/null +++ b/server/ch_migrations/1728342969_create_json_results_for_rag/down.sql @@ -0,0 +1 @@ +ALTER TABLE rag_queries DROP COLUMN IF EXISTS json_results; \ No newline at end of file diff --git a/server/ch_migrations/1728342969_create_json_results_for_rag/up.sql b/server/ch_migrations/1728342969_create_json_results_for_rag/up.sql new file mode 100644 index 0000000000..99abbbbe2f --- /dev/null +++ b/server/ch_migrations/1728342969_create_json_results_for_rag/up.sql @@ -0,0 +1 @@ +ALTER TABLE rag_queries ADD COLUMN IF NOT EXISTS json_results Array(String); \ No newline at end of file diff --git a/server/src/data/models.rs b/server/src/data/models.rs index 55ca10777f..74398336c4 100644 --- a/server/src/data/models.rs +++ b/server/src/data/models.rs @@ -3709,11 +3709,25 @@ pub struct SearchQueryRating { pub note: Option, } +#[derive(Debug, Serialize, Deserialize, Clone, ToSchema, Display)] +#[serde(rename_all = "snake_case")] +pub enum ClickhouseSearchTypes { + #[display(fmt = "search")] + Search, + #[display(fmt = "search_over_groups")] + SearchOverGroups, + #[display(fmt = "autocomplete")] + Autocomplete, + #[display(fmt = "rag")] + #[serde(rename = "rag")] + RAG, +} + #[derive(Debug, Serialize, Deserialize, ToSchema)] #[schema(title = "SearchQueryEvent")] pub struct SearchQueryEvent { pub id: uuid::Uuid, - pub search_type: String, + pub search_type: ClickhouseSearchTypes, pub query: String, pub request_params: serde_json::Value, pub latency: f32, @@ -3729,7 +3743,7 @@ impl Default for SearchQueryEvent { fn default() -> Self { SearchQueryEvent { id: uuid::Uuid::new_v4(), - search_type: "search".to_string(), + search_type: ClickhouseSearchTypes::Search, query: "".to_string(), request_params: serde_json::Value::String("".to_string()), latency: 0.0, @@ -4002,6 +4016,18 @@ pub enum SearchResultType { GroupSearch(GroupScoreChunk), } +impl From for ClickhouseSearchTypes { + fn from(search_type: String) -> Self { + match search_type.as_str() { + "search" => ClickhouseSearchTypes::Search, + "search_over_groups" => ClickhouseSearchTypes::SearchOverGroups, + "autocomplete" => ClickhouseSearchTypes::Autocomplete, + "rag" => ClickhouseSearchTypes::RAG, + _ => ClickhouseSearchTypes::Search, + } + } +} + impl From for SearchQueryEvent { fn from(clickhouse_response: SearchQueryEventClickhouse) -> SearchQueryEvent { let query_rating = if !clickhouse_response.query_rating.is_empty() { @@ -4012,7 +4038,7 @@ impl From for SearchQueryEvent { SearchQueryEvent { id: uuid::Uuid::from_bytes(*clickhouse_response.id.as_bytes()), - search_type: clickhouse_response.search_type, + search_type: clickhouse_response.search_type.into(), query: clickhouse_response .query .replace("|q", "?") @@ -4042,14 +4068,23 @@ impl From for SearchQueryEvent { } } +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Display)] +#[serde(rename_all = "snake_case")] +pub enum ClickhouseRagTypes { + #[display(fmt = "chosen_chunks")] + ChosenChunks, + #[display(fmt = "all_chunks")] + AllChunks, +} + #[derive(Debug, Serialize, Deserialize, ToSchema)] #[schema(title = "RagQueryEvent")] pub struct RagQueryEvent { pub id: uuid::Uuid, - pub rag_type: String, + pub rag_type: ClickhouseRagTypes, pub user_message: String, pub search_id: uuid::Uuid, - pub results: Vec, + pub results: Vec, pub dataset_id: uuid::Uuid, pub llm_response: String, pub query_rating: Option, @@ -4057,22 +4092,42 @@ pub struct RagQueryEvent { pub user_id: String, } +impl From for ClickhouseRagTypes { + fn from(rag_type: String) -> Self { + match rag_type.as_str() { + "chosen_chunks" => ClickhouseRagTypes::ChosenChunks, + "all_chunks" => ClickhouseRagTypes::AllChunks, + _ => ClickhouseRagTypes::ChosenChunks, + } + } +} + impl RagQueryEventClickhouse { pub async fn from_clickhouse(self, pool: web::Data) -> RagQueryEvent { let chunk_ids = self .results - .into_iter() - .map(|r| r.parse::().unwrap_or_default()) - .collect::>(); + .iter() + .filter_map(|x| x.parse::().ok()) + .collect_vec(); - let chunks = get_metadata_from_ids_query(chunk_ids, self.dataset_id, pool) - .await - .unwrap_or(vec![]); + let results = if !chunk_ids.is_empty() { + let chunks = get_metadata_from_ids_query(chunk_ids, self.dataset_id, pool) + .await + .unwrap_or(vec![]); - let chunk_string_tag_sets = chunks - .into_iter() - .map(ChunkMetadataStringTagSet::from) - .collect::>(); + chunks + .into_iter() + .map(|chunk| serde_json::to_value(chunk).unwrap_or_default()) + .collect::>() + } else { + self.json_results + .iter() + .map(|r| { + serde_json::from_str(&r.replace("|q", "?").replace('\n', "")) + .unwrap_or_default() + }) + .collect::>() + }; let query_rating = if !self.query_rating.is_empty() { Some(serde_json::from_str(&self.query_rating).unwrap_or_default()) @@ -4082,10 +4137,10 @@ impl RagQueryEventClickhouse { RagQueryEvent { id: uuid::Uuid::from_bytes(*self.id.as_bytes()), - rag_type: self.rag_type, + rag_type: self.rag_type.into(), user_message: self.user_message, search_id: uuid::Uuid::from_bytes(*self.search_id.as_bytes()), - results: chunk_string_tag_sets, + results, query_rating, dataset_id: uuid::Uuid::from_bytes(*self.dataset_id.as_bytes()), llm_response: self.llm_response, @@ -4104,6 +4159,7 @@ pub struct RagQueryEventClickhouse { #[serde(with = "clickhouse::serde::uuid")] pub search_id: uuid::Uuid, pub results: Vec, + pub json_results: Vec, pub query_rating: String, pub llm_response: String, #[serde(with = "clickhouse::serde::uuid")] @@ -4158,10 +4214,20 @@ pub struct RecommendationEventClickhouse { pub user_id: String, } +#[derive(Debug, Serialize, Deserialize, ToSchema, Display, Clone, Default)] +#[serde(rename = "snake_case")] +pub enum ClickhouseRecommendationTypes { + #[display(fmt = "chunk")] + #[default] + Chunk, + #[display(fmt = "group")] + Group, +} + #[derive(Debug, Serialize, Deserialize, ToSchema, Default)] pub struct RecommendationEvent { pub id: uuid::Uuid, - pub recommendation_type: String, + pub recommendation_type: ClickhouseRecommendationTypes, pub positive_ids: Vec, pub negative_ids: Vec, pub positive_tracking_ids: Vec, @@ -4174,11 +4240,21 @@ pub struct RecommendationEvent { pub user_id: String, } +impl From for ClickhouseRecommendationTypes { + fn from(recommendation_type: String) -> Self { + match recommendation_type.as_str() { + "chunk" => ClickhouseRecommendationTypes::Chunk, + "group" => ClickhouseRecommendationTypes::Group, + _ => ClickhouseRecommendationTypes::Chunk, + } + } +} + impl From for RecommendationEvent { fn from(clickhouse_response: RecommendationEventClickhouse) -> RecommendationEvent { RecommendationEvent { id: uuid::Uuid::from_bytes(*clickhouse_response.id.as_bytes()), - recommendation_type: clickhouse_response.recommendation_type, + recommendation_type: clickhouse_response.recommendation_type.into(), positive_ids: clickhouse_response .positive_ids .iter() @@ -4669,9 +4745,16 @@ pub struct EventDataClickhouse { pub updated_at: OffsetDateTime, } -impl EventDataClickhouse { - pub fn from_event_data(event: EventTypes, dataset_id: uuid::Uuid) -> Self { - match event { +pub enum EventDataTypes { + EventDataClickhouse(EventDataClickhouse), + SearchQueryEventClickhouse(SearchQueryEventClickhouse), + RagQueryEventClickhouse(RagQueryEventClickhouse), + RecommendationEventClickhouse(RecommendationEventClickhouse), +} + +impl EventTypes { + pub fn to_event_data(self, dataset_id: uuid::Uuid) -> EventDataTypes { + match self { EventTypes::AddToCart { event_name, request_id, @@ -4679,7 +4762,7 @@ impl EventDataClickhouse { user_id, metadata, is_conversion, - } => EventDataClickhouse { + } => EventDataTypes::EventDataClickhouse(EventDataClickhouse { id: uuid::Uuid::new_v4(), event_type: "add_to_cart".to_string(), event_name, @@ -4691,7 +4774,7 @@ impl EventDataClickhouse { dataset_id, created_at: OffsetDateTime::now_utc(), updated_at: OffsetDateTime::now_utc(), - }, + }), EventTypes::Purchase { event_name, request_id, @@ -4700,7 +4783,7 @@ impl EventDataClickhouse { is_conversion, value, currency, - } => EventDataClickhouse { + } => EventDataTypes::EventDataClickhouse(EventDataClickhouse { id: uuid::Uuid::new_v4(), event_type: "purchase".to_string(), event_name, @@ -4716,14 +4799,14 @@ impl EventDataClickhouse { dataset_id, created_at: OffsetDateTime::now_utc(), updated_at: OffsetDateTime::now_utc(), - }, + }), EventTypes::View { event_name, request_id, items, user_id, metadata, - } => EventDataClickhouse { + } => EventDataTypes::EventDataClickhouse(EventDataClickhouse { id: uuid::Uuid::new_v4(), event_type: "view".to_string(), event_name, @@ -4735,14 +4818,14 @@ impl EventDataClickhouse { dataset_id, created_at: OffsetDateTime::now_utc(), updated_at: OffsetDateTime::now_utc(), - }, + }), EventTypes::Click { event_name, request_id, clicked_items: clicked_item, user_id, is_conversion, - } => EventDataClickhouse { + } => EventDataTypes::EventDataClickhouse(EventDataClickhouse { id: uuid::Uuid::new_v4(), event_type: "click".to_string(), event_name, @@ -4754,14 +4837,14 @@ impl EventDataClickhouse { dataset_id, created_at: OffsetDateTime::now_utc(), updated_at: OffsetDateTime::now_utc(), - }, + }), EventTypes::FilterClicked { event_name, request_id, items, user_id, is_conversion, - } => EventDataClickhouse { + } => EventDataTypes::EventDataClickhouse(EventDataClickhouse { id: uuid::Uuid::new_v4(), event_type: "filter_clicked".to_string(), event_name, @@ -4773,7 +4856,98 @@ impl EventDataClickhouse { dataset_id, created_at: OffsetDateTime::now_utc(), updated_at: OffsetDateTime::now_utc(), - }, + }), + EventTypes::Search { + search_type, + query, + request_params, + latency, + top_score, + results, + query_rating, + user_id, + } => EventDataTypes::SearchQueryEventClickhouse(SearchQueryEventClickhouse { + id: uuid::Uuid::new_v4(), + search_type: search_type + .unwrap_or(ClickhouseSearchTypes::Search) + .to_string(), + query, + request_params: serde_json::to_string(&request_params).unwrap_or_default(), + latency: latency.unwrap_or(0.0), + top_score: top_score.unwrap_or(0.0), + results: results + .unwrap_or_default() + .iter() + .map(|r| r.to_string()) + .collect(), + dataset_id, + created_at: OffsetDateTime::now_utc(), + query_rating: serde_json::to_string(&query_rating).unwrap_or_default(), + user_id: user_id.unwrap_or_default(), + }), + EventTypes::RAG { + rag_type, + user_message, + search_id, + results, + query_rating, + llm_response, + user_id, + } => EventDataTypes::RagQueryEventClickhouse(RagQueryEventClickhouse { + id: uuid::Uuid::new_v4(), + rag_type: rag_type + .unwrap_or(ClickhouseRagTypes::ChosenChunks) + .to_string(), + user_message, + search_id: search_id.unwrap_or_default(), + results: vec![String::new()], + json_results: results + .unwrap_or_default() + .iter() + .map(|r| r.to_string()) + .collect(), + query_rating: serde_json::to_string(&query_rating).unwrap_or_default(), + llm_response: llm_response.unwrap_or_default(), + dataset_id, + created_at: OffsetDateTime::now_utc(), + user_id: user_id.unwrap_or_default(), + }), + EventTypes::Recommendation { + recommendation_type, + positive_ids, + negative_ids, + positive_tracking_ids, + negative_tracking_ids, + request_params, + top_score, + results, + user_id, + } => EventDataTypes::RecommendationEventClickhouse(RecommendationEventClickhouse { + id: uuid::Uuid::new_v4(), + recommendation_type: recommendation_type.unwrap_or_default().to_string(), + positive_ids: positive_ids + .unwrap_or_default() + .iter() + .map(|id| id.to_string()) + .collect(), + negative_ids: negative_ids + .unwrap_or_default() + .iter() + .map(|id| id.to_string()) + .collect(), + positive_tracking_ids: positive_tracking_ids.unwrap_or_default().clone(), + negative_tracking_ids: negative_tracking_ids.unwrap_or_default().clone(), + request_params: serde_json::to_string(&request_params).unwrap_or_default(), + results: results + .unwrap_or_default() + .iter() + .map(|r| r.to_string()) + .collect(), + top_score: top_score.unwrap_or(0.0), + dataset_id, + created_at: OffsetDateTime::now_utc(), + user_id: user_id.unwrap_or_default(), + }), } } } @@ -5529,6 +5703,64 @@ pub enum EventTypes { /// Whether the event is a conversion event is_conversion: Option, }, + #[display(fmt = "search")] + Search { + /// The search type: search, rag, or search_over_groups + search_type: Option, + /// The search query + query: String, + /// The request params of the search + request_params: Option, + /// Latency of the search + latency: Option, + /// The top score of the search + top_score: Option, + /// The results of the search + results: Option>, + /// The rating of the query + query_rating: Option, + /// The user id of the user who made the search + user_id: Option, + }, + #[display(fmt = "rag")] + #[serde(rename = "rag")] + RAG { + /// The Type of RAG event: chosen_chunks, all_chunks + rag_type: Option, + /// The user message + user_message: String, + /// The search id to associate the RAG event with a search + search_id: Option, + /// The results of the RAG event + results: Option>, + /// The rating of the query + query_rating: Option, + /// The response from the LLM + llm_response: Option, + /// The user id of the user who made the RAG event + user_id: Option, + }, + #[display(fmt = "recommendation")] + Recommendation { + /// The Type of Recommendation event: chunk, group + recommendation_type: Option, + /// Positive ids used for the recommendation + positive_ids: Option>, + /// Negative ids used for the recommendation + negative_ids: Option>, + /// Positive tracking ids used for the recommendation + positive_tracking_ids: Option>, + /// Negative tracking ids used for the recommendation + negative_tracking_ids: Option>, + /// The request params of the recommendation + request_params: Option, + /// The results of the Recommendation event + results: Option>, + /// Top score of the recommendation + top_score: Option, + /// The user id of the user who made the recommendation + user_id: Option, + }, } impl From for EventTypes { diff --git a/server/src/handlers/analytics_handler.rs b/server/src/handlers/analytics_handler.rs index 66210c58e3..334c2443a9 100644 --- a/server/src/handlers/analytics_handler.rs +++ b/server/src/handlers/analytics_handler.rs @@ -2,13 +2,16 @@ use super::auth_handler::AdminOnly; use crate::{ data::models::{ CTRAnalytics, CTRAnalyticsResponse, CTRType, ClusterAnalytics, ClusterAnalyticsResponse, - DatasetAndOrgWithSubAndPlan, DateRange, EventDataClickhouse, EventTypes, - GetEventsRequestBody, OrganizationWithSubAndPlan, Pool, RAGAnalytics, RAGAnalyticsResponse, + DatasetAndOrgWithSubAndPlan, DateRange, EventDataTypes, EventTypes, GetEventsRequestBody, + OrganizationWithSubAndPlan, Pool, RAGAnalytics, RAGAnalyticsResponse, RecommendationAnalytics, RecommendationAnalyticsResponse, SearchAnalytics, SearchAnalyticsResponse, TopDatasetsRequestTypes, }, errors::ServiceError, - operators::analytics_operator::*, + operators::{ + analytics_operator::*, + clickhouse_operator::{ClickHouseEvent, EventQueue}, + }, }; use actix_web::{web, HttpResponse}; use serde::{Deserialize, Serialize}; @@ -512,11 +515,12 @@ pub async fn send_ctr_data( clickhouse_client: web::Data, dataset_org_plan_sub: DatasetAndOrgWithSubAndPlan, ) -> Result { - let event_data = EventDataClickhouse::from_event_data( - data.into_inner().into(), - dataset_org_plan_sub.dataset.id, - ); - send_event_data_query(event_data, clickhouse_client.get_ref()).await?; + let event_data = + EventTypes::from(data.into_inner()).to_event_data(dataset_org_plan_sub.dataset.id); + + if let EventDataTypes::EventDataClickhouse(event_data) = event_data { + send_event_data_query(event_data, clickhouse_client.get_ref()).await?; + } Ok(HttpResponse::NoContent().finish()) } @@ -546,13 +550,33 @@ pub async fn send_event_data( _user: AdminOnly, data: web::Json, clickhouse_client: web::Data, - + event_queue: web::Data, dataset_org_plan_sub: DatasetAndOrgWithSubAndPlan, ) -> Result { - let event_data = - EventDataClickhouse::from_event_data(data.into_inner(), dataset_org_plan_sub.dataset.id); + let event_data = data + .into_inner() + .to_event_data(dataset_org_plan_sub.dataset.id); - send_event_data_query(event_data, clickhouse_client.get_ref()).await?; + match event_data { + EventDataTypes::EventDataClickhouse(event_data) => { + send_event_data_query(event_data, clickhouse_client.get_ref()).await?; + } + EventDataTypes::SearchQueryEventClickhouse(event_data) => { + event_queue + .send(ClickHouseEvent::SearchQueryEvent(event_data)) + .await; + } + EventDataTypes::RagQueryEventClickhouse(event_data) => { + event_queue + .send(ClickHouseEvent::RagQueryEvent(event_data)) + .await; + } + EventDataTypes::RecommendationEventClickhouse(event_data) => { + event_queue + .send(ClickHouseEvent::RecommendationEvent(event_data)) + .await; + } + } Ok(HttpResponse::NoContent().finish()) } diff --git a/server/src/handlers/chunk_handler.rs b/server/src/handlers/chunk_handler.rs index 872088becc..8988a0210a 100644 --- a/server/src/handlers/chunk_handler.rs +++ b/server/src/handlers/chunk_handler.rs @@ -2600,11 +2600,11 @@ pub async fn generate_off_chunks( created_at: time::OffsetDateTime::now_utc(), dataset_id: dataset_org_plan_sub.dataset.id, search_id: uuid::Uuid::nil(), - results: data - .chunk_ids + results: vec![], + json_results: chunks .clone() .into_iter() - .map(|s| s.to_string()) + .map(|x| serde_json::to_string(&x).unwrap_or_default()) .collect(), user_message: prompt, query_rating: String::new(), @@ -2638,11 +2638,11 @@ pub async fn generate_off_chunks( created_at: time::OffsetDateTime::now_utc(), dataset_id: dataset_org_plan_sub.dataset.id, search_id: uuid::Uuid::nil(), - results: data - .chunk_ids + results: vec![], + json_results: chunks .clone() .into_iter() - .map(|s| s.to_string()) + .map(|x| serde_json::to_string(&x).unwrap_or_default()) .collect(), user_message: prompt, rag_type: "chosen_chunks".to_string(), diff --git a/server/src/lib.rs b/server/src/lib.rs index 455ee379fb..51f4283a92 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -387,6 +387,9 @@ impl Modify for SecurityAddon { data::models::UsageGraphPoint, data::models::SearchResultType, data::models::RoleProxy, + data::models::ClickhouseRagTypes, + data::models::ClickhouseSearchTypes, + data::models::ClickhouseRecommendationTypes, data::models::RAGUsageResponse, data::models::TopDatasetsRequestTypes, data::models::TopDatasetsResponse, diff --git a/server/src/operators/message_operator.rs b/server/src/operators/message_operator.rs index 7f652ff492..4258c7ac31 100644 --- a/server/src/operators/message_operator.rs +++ b/server/src/operators/message_operator.rs @@ -437,22 +437,12 @@ pub async fn stream_response( ) .collect::>(); - let chunk_ids = result_chunks + let chunk_data = result_chunks .score_chunks - .iter() - .filter_map(|score_chunk_dto| { - score_chunk_dto - .metadata - .clone() - .into_iter() - .map(|metadata| match metadata { - ChunkMetadataTypes::ID(chunk_metadata) => chunk_metadata.id, - ChunkMetadataTypes::Metadata(chunk_metadata) => chunk_metadata.id, - ChunkMetadataTypes::Content(chunk_metadata) => chunk_metadata.id, - }) - .next() - }) - .collect::>(); + .clone() + .into_iter() + .map(|x| serde_json::to_string(&x).unwrap_or_default()) + .collect(); let mut chunk_metadatas_stringified = serde_json::to_string(&chunk_metadatas).expect("Failed to serialize citation chunks"); @@ -650,11 +640,8 @@ pub async fn stream_response( created_at: time::OffsetDateTime::now_utc(), dataset_id: dataset.id, search_id: clickhouse_search_event.id, - results: chunk_ids - .clone() - .into_iter() - .map(|s| s.to_string()) - .collect(), + results: vec![], + json_results: chunk_data, user_message: query.clone(), query_rating: String::new(), rag_type: "chosen_chunks".to_string(), @@ -714,11 +701,8 @@ pub async fn stream_response( created_at: time::OffsetDateTime::now_utc(), dataset_id: dataset.id, search_id: clickhouse_search_event.id, - results: chunk_ids - .clone() - .into_iter() - .map(|s| s.to_string()) - .collect(), + results: vec![], + json_results: chunk_data, user_message: query.clone(), query_rating: String::new(), rag_type: "all_chunks".to_string(), From 194342874fe3de57332be55ae777699b5657457d Mon Sep 17 00:00:00 2001 From: Dens Sumesh Date: Tue, 8 Oct 2024 12:30:36 -0700 Subject: [PATCH 022/159] feature: show first 3 lines of results --- .../SingleQueryInfo/ArbitraryResultCard.tsx | 37 +++++++++++++++++++ .../components/SingleQueryInfo/index.tsx | 9 +---- .../components/SingleRagInfo/index.tsx | 22 ++++++----- .../SingleRecommendationInfo/index.tsx | 13 ++----- server/src/data/models.rs | 4 +- server/src/operators/analytics_operator.rs | 4 ++ 6 files changed, 62 insertions(+), 27 deletions(-) create mode 100644 frontends/dashboard/src/analytics/components/SingleQueryInfo/ArbitraryResultCard.tsx diff --git a/frontends/dashboard/src/analytics/components/SingleQueryInfo/ArbitraryResultCard.tsx b/frontends/dashboard/src/analytics/components/SingleQueryInfo/ArbitraryResultCard.tsx new file mode 100644 index 0000000000..4552b9efe9 --- /dev/null +++ b/frontends/dashboard/src/analytics/components/SingleQueryInfo/ArbitraryResultCard.tsx @@ -0,0 +1,37 @@ +import { FullScreenModal, JsonInput } from "shared/ui"; +import { IoCode } from "solid-icons/io"; +import { createSignal, Show } from "solid-js"; + +interface ArbitraryCardProps { + result: object; +} + +export const ArbitraryResultCard = (props: ArbitraryCardProps) => { + const [showingJson, setShowingJson] = createSignal(false); + + return ( + +
+ + + props.result} class="min-w-[60vw]" readonly /> + +
+
+ ); +}; diff --git a/frontends/dashboard/src/analytics/components/SingleQueryInfo/index.tsx b/frontends/dashboard/src/analytics/components/SingleQueryInfo/index.tsx index b84fc13b99..61a70b7271 100644 --- a/frontends/dashboard/src/analytics/components/SingleQueryInfo/index.tsx +++ b/frontends/dashboard/src/analytics/components/SingleQueryInfo/index.tsx @@ -11,6 +11,7 @@ import { DatasetContext } from "../../../contexts/DatasetContext"; import { UserContext } from "../../../contexts/UserContext"; import { IoArrowBackOutline } from "solid-icons/io"; import { isScoreChunkDTO } from "shared/types"; +import { ArbitraryResultCard } from "./ArbitraryResultCard"; interface SingleQueryProps { queryId: string; @@ -97,13 +98,7 @@ export const SingleQuery = (props: SingleQueryProps) => { if (isScoreChunkDTO(result)) { return ; } else { - return ( -
-
-                          {JSON.stringify(result, null, 2)}
-                        
-
- ); + return ; } }} diff --git a/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx b/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx index f9d3668729..928c57d533 100644 --- a/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx +++ b/frontends/dashboard/src/analytics/components/SingleRagInfo/index.tsx @@ -14,6 +14,7 @@ import { DatasetContext } from "../../../contexts/DatasetContext"; import { UserContext } from "../../../contexts/UserContext"; import { IoArrowBackOutline } from "solid-icons/io"; import { isScoreChunkDTO, SearchQueryEvent } from "shared/types"; +import { ArbitraryResultCard } from "../SingleQueryInfo/ArbitraryResultCard"; interface SingleRAGQueryProps { queryId: string; @@ -29,7 +30,11 @@ export const SingleRAGQuery = (props: SingleRAGQueryProps) => { })); let search_query: CreateQueryResult | undefined; - if (rag_query.data?.search_id !== "00000000-0000-0000-0000-000000000000") { + if ( + rag_query.data?.search_id !== undefined && + rag_query.data?.search_id !== "00000000-0000-0000-0000-000000000000" + ) { + console.log("looking for search id", rag_query.data?.search_id); search_query = createQuery(() => ({ queryKey: ["single_query", rag_query.data?.search_id], queryFn: () => { @@ -121,7 +126,12 @@ export const SingleRAGQuery = (props: SingleRAGQueryProps) => {
- +
{ if (isScoreChunkDTO(result)) { return ; } else { - return ( -
-
-                          {JSON.stringify(result, null, 2)}
-                        
-
- ); + return ; } }}
diff --git a/frontends/dashboard/src/analytics/components/SingleRecommendationInfo/index.tsx b/frontends/dashboard/src/analytics/components/SingleRecommendationInfo/index.tsx index 0188072675..2a84246e91 100644 --- a/frontends/dashboard/src/analytics/components/SingleRecommendationInfo/index.tsx +++ b/frontends/dashboard/src/analytics/components/SingleRecommendationInfo/index.tsx @@ -11,6 +11,7 @@ import { DatasetContext } from "../../../contexts/DatasetContext"; import { UserContext } from "../../../contexts/UserContext"; import { IoArrowBackOutline } from "solid-icons/io"; import { isScoreChunkDTO } from "shared/types"; +import { ArbitraryResultCard } from "../SingleQueryInfo/ArbitraryResultCard"; interface SingleRecommendationQueryProps { queryId: string; @@ -121,16 +122,10 @@ export const SingleRecommendationQuery = ( each={props.recommendation_data.results} > {(result) => { - if (isScoreChunkDTO({ metadata: [result] })) { - return ; + if (isScoreChunkDTO(result)) { + return ; } else { - return ( -
-
-                          {JSON.stringify(result, null, 2)}
-                        
-
- ); + return ; } }} diff --git a/server/src/data/models.rs b/server/src/data/models.rs index 74398336c4..8754a8291f 100644 --- a/server/src/data/models.rs +++ b/server/src/data/models.rs @@ -4882,7 +4882,7 @@ impl EventTypes { .collect(), dataset_id, created_at: OffsetDateTime::now_utc(), - query_rating: serde_json::to_string(&query_rating).unwrap_or_default(), + query_rating: serde_json::to_string(&query_rating).unwrap_or("".to_string()), user_id: user_id.unwrap_or_default(), }), EventTypes::RAG { @@ -4906,7 +4906,7 @@ impl EventTypes { .iter() .map(|r| r.to_string()) .collect(), - query_rating: serde_json::to_string(&query_rating).unwrap_or_default(), + query_rating: serde_json::to_string(&query_rating).unwrap_or("".to_string()), llm_response: llm_response.unwrap_or_default(), dataset_id, created_at: OffsetDateTime::now_utc(), diff --git a/server/src/operators/analytics_operator.rs b/server/src/operators/analytics_operator.rs index 407435fa2c..2d0e830d9c 100644 --- a/server/src/operators/analytics_operator.rs +++ b/server/src/operators/analytics_operator.rs @@ -1447,6 +1447,10 @@ pub async fn set_rag_query_rating_query( let stringified_data = serde_json::to_string(&rating).unwrap_or_default(); + println!("stringified_data: {:?}", stringified_data); + println!("data.query_id: {:?}", data.query_id); + println!("dataset_id: {:?}", dataset_id); + clickhouse_client .query( "ALTER TABLE default.rag_queries From afc7497b7f53f5c097f2dee148603da5ca13bdb7 Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 15:01:02 -0700 Subject: [PATCH 023/159] feat: add glasskube argocd app Signed-off-by: Philip Miglinci --- glasskube/argocd/glasskube-application.yaml | 19 + .../argocd/glasskube/applicationset.yaml | 29 + glasskube/argocd/glasskube/glasskube.yaml | 10296 ++++++++++++++++ 3 files changed, 10344 insertions(+) create mode 100644 glasskube/argocd/glasskube-application.yaml create mode 100644 glasskube/argocd/glasskube/applicationset.yaml create mode 100644 glasskube/argocd/glasskube/glasskube.yaml diff --git a/glasskube/argocd/glasskube-application.yaml b/glasskube/argocd/glasskube-application.yaml new file mode 100644 index 0000000000..a502b5f80d --- /dev/null +++ b/glasskube/argocd/glasskube-application.yaml @@ -0,0 +1,19 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: glasskube + namespace: argocd +spec: + destination: + server: https://kubernetes.default.svc + project: default + source: + path: glasskube/argocd/glasskube + repoURL: https://github.com/pmig/trieve + targetRevision: pmig/gcp-hn-migration + syncPolicy: + syncOptions: + - ApplyOutOfSyncOnly=true + automated: + prune: true + selfHeal: true diff --git a/glasskube/argocd/glasskube/applicationset.yaml b/glasskube/argocd/glasskube/applicationset.yaml new file mode 100644 index 0000000000..15560f19eb --- /dev/null +++ b/glasskube/argocd/glasskube/applicationset.yaml @@ -0,0 +1,29 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: applications + namespace: argocd +spec: + goTemplate: true + goTemplateOptions: ["missingkey=error"] + generators: + - git: + repoURL: https://github.com/pmig/trieve + revision: pmig/gcp-hn-migration + directories: + - path: 'glasskube/packages/*' + template: + metadata: + name: '{{ .path.basename }}' + spec: + project: default + source: + repoURL: https://github.com/pmig/trieve + targetRevision: pmig/gcp-hn-migration + path: '{{.path.path}}' + destination: + server: https://kubernetes.default.svc + syncPolicy: + automated: + prune: true + selfHeal: true diff --git a/glasskube/argocd/glasskube/glasskube.yaml b/glasskube/argocd/glasskube/glasskube.yaml new file mode 100644 index 0000000000..b8b6f498cb --- /dev/null +++ b/glasskube/argocd/glasskube/glasskube.yaml @@ -0,0 +1,10296 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: flux-system + +--- +apiVersion: v1 +kind: Namespace +metadata: + annotations: + packages.glasskube.dev/telemetry-enabled: "true" + labels: + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: system + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: namespace + app.kubernetes.io/part-of: glasskube + control-plane: controller-manager + name: glasskube-system + +--- +apiVersion: v1 +kind: ResourceQuota +metadata: + name: critical-pods + namespace: flux-system +spec: + hard: + pods: "1000" + scopeSelector: + matchExpressions: + - operator: In + scopeName: PriorityClass + values: + - system-node-critical + - system-cluster-critical + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + labels: + app.kubernetes.io/component: source-controller + name: buckets.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: Bucket + listKind: BucketList + plural: buckets + singular: bucket + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.endpoint + name: Endpoint + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta1 Bucket is deprecated, upgrade to v1beta2 + name: v1beta1 + schema: + openAPIV3Schema: + description: Bucket is the Schema for the buckets API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BucketSpec defines the desired state of an S3 compatible + bucket + properties: + accessFrom: + description: AccessFrom defines an Access Control List for allowing + cross-namespace references to this object. + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + bucketName: + description: The bucket name. + type: string + endpoint: + description: The bucket endpoint address. + type: string + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS S3 HTTP endpoint. + type: boolean + interval: + description: The interval at which to check for bucket updates. + type: string + provider: + default: generic + description: The S3 compatible storage provider name, default ('generic'). + enum: + - generic + - aws + - gcp + type: string + region: + description: The bucket region. + type: string + secretRef: + description: |- + The name of the secret containing authentication credentials + for the Bucket. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + timeout: + default: 60s + description: The timeout for download operations, defaults to 60s. + type: string + required: + - bucketName + - endpoint + - interval + type: object + status: + default: + observedGeneration: -1 + description: BucketStatus defines the observed state of a bucket + properties: + artifact: + description: Artifact represents the output of the last successful + Bucket sync. + properties: + checksum: + description: Checksum is the SHA256 checksum of the artifact. + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of this + artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: |- + Revision is a human readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm + chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the Bucket. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + url: + description: URL is the download link for the artifact output of the + last Bucket sync. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.endpoint + name: Endpoint + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1beta2 + schema: + openAPIV3Schema: + description: Bucket is the Schema for the buckets API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + BucketSpec specifies the required configuration to produce an Artifact for + an object storage bucket. + properties: + accessFrom: + description: |- + AccessFrom specifies an Access Control List for allowing cross-namespace + references to this object. + NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + bucketName: + description: BucketName is the name of the object storage bucket. + type: string + endpoint: + description: Endpoint is the object storage address the BucketName + is located at. + type: string + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP Endpoint. + type: boolean + interval: + description: |- + Interval at which the Bucket Endpoint is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + prefix: + description: Prefix to use for server-side filtering of files in the + Bucket. + type: string + provider: + default: generic + description: |- + Provider of the object storage bucket. + Defaults to 'generic', which expects an S3 (API) compatible object + storage. + enum: + - generic + - aws + - gcp + - azure + type: string + region: + description: Region of the Endpoint where the BucketName is located + in. + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials + for the Bucket. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + Bucket. + type: boolean + timeout: + default: 60s + description: Timeout for fetch operations, defaults to 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + required: + - bucketName + - endpoint + - interval + type: object + status: + default: + observedGeneration: -1 + description: BucketStatus records the observed state of a Bucket. + properties: + artifact: + description: Artifact represents the last successful Bucket reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the Bucket. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation of + the Bucket object. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + BucketStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: clusterpackages.packages.glasskube.dev +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: glasskube-webhook-service + namespace: glasskube-system + path: /convert + conversionReviewVersions: + - v1 + group: packages.glasskube.dev + names: + kind: ClusterPackage + listKind: ClusterPackageList + plural: clusterpackages + shortNames: + - clpkg + singular: clusterpackage + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.packageInfo.version + name: Desired version + type: string + - jsonPath: .status.version + name: Installed version + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterPackage is the Schema for the clusterpackages API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PackageSpec defines the desired state + properties: + packageInfo: + properties: + name: + description: Name of the package to install + type: string + repositoryName: + description: RepositoryName is the name of the repository to pull + the package from (optional) + type: string + version: + description: Version of the package to install + type: string + required: + - name + - version + type: object + values: + additionalProperties: + maxProperties: 1 + minProperties: 1 + properties: + value: + type: string + valueFrom: + maxProperties: 1 + minProperties: 1 + properties: + configMapRef: + properties: + key: + type: string + name: + type: string + namespace: + type: string + required: + - key + - name + type: object + packageRef: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + secretRef: + properties: + key: + type: string + name: + type: string + namespace: + type: string + required: + - key + - name + type: object + type: object + type: object + type: object + required: + - packageInfo + type: object + status: + description: PackageStatus defines the observed state + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + ownedPackageInfos: + items: + properties: + group: + type: string + kind: + type: string + markedForDeletion: + type: boolean + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - version + type: object + type: array + ownedPackages: + items: + properties: + group: + type: string + kind: + type: string + markedForDeletion: + type: boolean + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - version + type: object + type: array + ownedResources: + items: + properties: + group: + type: string + kind: + type: string + markedForDeletion: + type: boolean + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - version + type: object + type: array + version: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + labels: + app.kubernetes.io/component: source-controller + name: gitrepositories.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: GitRepository + listKind: GitRepositoryList + plural: gitrepositories + shortNames: + - gitrepo + singular: gitrepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: GitRepository is the Schema for the gitrepositories API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + GitRepositorySpec specifies the required configuration to produce an + Artifact for a Git repository. + properties: + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + include: + description: |- + Include specifies a list of GitRepository resources which Artifacts + should be included in the Artifact produced for this GitRepository. + items: + description: |- + GitRepositoryInclude specifies a local reference to a GitRepository which + Artifact (sub-)contents must be included, and where they should be placed. + properties: + fromPath: + description: |- + FromPath specifies the path to copy contents from, defaults to the root + of the Artifact. + type: string + repository: + description: |- + GitRepositoryRef specifies the GitRepository which Artifact contents + must be included. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: |- + ToPath specifies the path to copy contents to, defaults to the name of + the GitRepositoryRef. + type: string + required: + - repository + type: object + type: array + interval: + description: |- + Interval at which the GitRepository URL is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the Git server. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + recurseSubmodules: + description: |- + RecurseSubmodules enables the initialization of all submodules within + the GitRepository as cloned from the URL, using their default settings. + type: boolean + ref: + description: |- + Reference specifies the Git reference to resolve and monitor for + changes, defaults to the 'master' branch. + properties: + branch: + description: Branch to check out, defaults to 'master' if no other + field is defined. + type: string + commit: + description: |- + Commit SHA to check out, takes precedence over all reference fields. + + + This can be combined with Branch to shallow clone the branch, in which + the commit is expected to exist. + type: string + name: + description: |- + Name of the reference to check out; takes precedence over Branch, Tag and SemVer. + + + It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description + Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" + type: string + semver: + description: SemVer tag expression to check out, takes precedence + over Tag. + type: string + tag: + description: Tag to check out, takes precedence over Branch. + type: string + type: object + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials for + the GitRepository. + For HTTPS repositories the Secret must contain 'username' and 'password' + fields for basic auth or 'bearerToken' field for token auth. + For SSH repositories the Secret must contain 'identity' + and 'known_hosts' fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + GitRepository. + type: boolean + timeout: + default: 60s + description: Timeout for Git operations like cloning, defaults to + 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + url: + description: URL specifies the Git repository URL, it can be an HTTP/S + or SSH address. + pattern: ^(http|https|ssh)://.*$ + type: string + verify: + description: |- + Verification specifies the configuration to verify the Git commit + signature(s). + properties: + mode: + default: HEAD + description: |- + Mode specifies which Git object(s) should be verified. + + + The variants "head" and "HEAD" both imply the same thing, i.e. verify + the commit that the HEAD of the Git repository points to. The variant + "head" solely exists to ensure backwards compatibility. + enum: + - head + - HEAD + - Tag + - TagAndHEAD + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing the public keys of trusted Git + authors. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - secretRef + type: object + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: GitRepositoryStatus records the observed state of a Git repository. + properties: + artifact: + description: Artifact represents the last successful GitRepository + reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the GitRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + includedArtifacts: + description: |- + IncludedArtifacts contains a list of the last successfully included + Artifacts as instructed by GitRepositorySpec.Include. + items: + description: Artifact represents the output of a Source reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of + ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI + annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - lastUpdateTime + - path + - revision + - url + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the GitRepository + object. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + observedInclude: + description: |- + ObservedInclude is the observed list of GitRepository resources used to + produce the current Artifact. + items: + description: |- + GitRepositoryInclude specifies a local reference to a GitRepository which + Artifact (sub-)contents must be included, and where they should be placed. + properties: + fromPath: + description: |- + FromPath specifies the path to copy contents from, defaults to the root + of the Artifact. + type: string + repository: + description: |- + GitRepositoryRef specifies the GitRepository which Artifact contents + must be included. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: |- + ToPath specifies the path to copy contents to, defaults to the name of + the GitRepositoryRef. + type: string + required: + - repository + type: object + type: array + observedRecurseSubmodules: + description: |- + ObservedRecurseSubmodules is the observed resource submodules + configuration used to produce the current Artifact. + type: boolean + sourceVerificationMode: + description: |- + SourceVerificationMode is the last used verification mode indicating + which Git object(s) have been verified. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta1 GitRepository is deprecated, upgrade to v1 + name: v1beta1 + schema: + openAPIV3Schema: + description: GitRepository is the Schema for the gitrepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: GitRepositorySpec defines the desired state of a Git repository. + properties: + accessFrom: + description: AccessFrom defines an Access Control List for allowing + cross-namespace references to this object. + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + gitImplementation: + default: go-git + description: |- + Determines which git client library to use. + Defaults to go-git, valid values are ('go-git', 'libgit2'). + enum: + - go-git + - libgit2 + type: string + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + include: + description: Extra git repositories to map into the repository + items: + description: GitRepositoryInclude defines a source with a from and + to path. + properties: + fromPath: + description: The path to copy contents from, defaults to the + root directory. + type: string + repository: + description: Reference to a GitRepository to include. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: The path to copy contents to, defaults to the name + of the source ref. + type: string + required: + - repository + type: object + type: array + interval: + description: The interval at which to check for repository updates. + type: string + recurseSubmodules: + description: |- + When enabled, after the clone is created, initializes all submodules within, + using their default settings. + This option is available only when using the 'go-git' GitImplementation. + type: boolean + ref: + description: |- + The Git reference to checkout and monitor for changes, defaults to + master branch. + properties: + branch: + description: The Git branch to checkout, defaults to master. + type: string + commit: + description: The Git commit SHA to checkout, if specified Tag + filters will be ignored. + type: string + semver: + description: The Git tag semver expression, takes precedence over + Tag. + type: string + tag: + description: The Git tag to checkout, takes precedence over Branch. + type: string + type: object + secretRef: + description: |- + The secret name containing the Git credentials. + For HTTPS repositories the secret must contain username and password + fields. + For SSH repositories the secret must contain identity and known_hosts + fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + timeout: + default: 60s + description: The timeout for remote Git operations like cloning, defaults + to 60s. + type: string + url: + description: The repository URL, can be a HTTP/S or SSH address. + pattern: ^(http|https|ssh)://.*$ + type: string + verify: + description: Verify OpenPGP signature for the Git commit HEAD points + to. + properties: + mode: + description: Mode describes what git object should be verified, + currently ('head'). + enum: + - head + type: string + secretRef: + description: The secret name containing the public keys of all + trusted Git authors. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - mode + type: object + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: GitRepositoryStatus defines the observed state of a Git repository. + properties: + artifact: + description: Artifact represents the output of the last successful + repository sync. + properties: + checksum: + description: Checksum is the SHA256 checksum of the artifact. + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of this + artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: |- + Revision is a human readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm + chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the GitRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + includedArtifacts: + description: IncludedArtifacts represents the included artifacts from + the last successful repository sync. + items: + description: Artifact represents the output of a source synchronisation. + properties: + checksum: + description: Checksum is the SHA256 checksum of the artifact. + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of this + artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: |- + Revision is a human readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm + chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + url: + description: |- + URL is the download link for the artifact output of the last repository + sync. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 GitRepository is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: GitRepository is the Schema for the gitrepositories API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + GitRepositorySpec specifies the required configuration to produce an + Artifact for a Git repository. + properties: + accessFrom: + description: |- + AccessFrom specifies an Access Control List for allowing cross-namespace + references to this object. + NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + gitImplementation: + default: go-git + description: |- + GitImplementation specifies which Git client library implementation to + use. Defaults to 'go-git', valid values are ('go-git', 'libgit2'). + Deprecated: gitImplementation is deprecated now that 'go-git' is the + only supported implementation. + enum: + - go-git + - libgit2 + type: string + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + include: + description: |- + Include specifies a list of GitRepository resources which Artifacts + should be included in the Artifact produced for this GitRepository. + items: + description: |- + GitRepositoryInclude specifies a local reference to a GitRepository which + Artifact (sub-)contents must be included, and where they should be placed. + properties: + fromPath: + description: |- + FromPath specifies the path to copy contents from, defaults to the root + of the Artifact. + type: string + repository: + description: |- + GitRepositoryRef specifies the GitRepository which Artifact contents + must be included. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: |- + ToPath specifies the path to copy contents to, defaults to the name of + the GitRepositoryRef. + type: string + required: + - repository + type: object + type: array + interval: + description: Interval at which to check the GitRepository for updates. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + recurseSubmodules: + description: |- + RecurseSubmodules enables the initialization of all submodules within + the GitRepository as cloned from the URL, using their default settings. + type: boolean + ref: + description: |- + Reference specifies the Git reference to resolve and monitor for + changes, defaults to the 'master' branch. + properties: + branch: + description: Branch to check out, defaults to 'master' if no other + field is defined. + type: string + commit: + description: |- + Commit SHA to check out, takes precedence over all reference fields. + + + This can be combined with Branch to shallow clone the branch, in which + the commit is expected to exist. + type: string + name: + description: |- + Name of the reference to check out; takes precedence over Branch, Tag and SemVer. + + + It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description + Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" + type: string + semver: + description: SemVer tag expression to check out, takes precedence + over Tag. + type: string + tag: + description: Tag to check out, takes precedence over Branch. + type: string + type: object + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials for + the GitRepository. + For HTTPS repositories the Secret must contain 'username' and 'password' + fields for basic auth or 'bearerToken' field for token auth. + For SSH repositories the Secret must contain 'identity' + and 'known_hosts' fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + GitRepository. + type: boolean + timeout: + default: 60s + description: Timeout for Git operations like cloning, defaults to + 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + url: + description: URL specifies the Git repository URL, it can be an HTTP/S + or SSH address. + pattern: ^(http|https|ssh)://.*$ + type: string + verify: + description: |- + Verification specifies the configuration to verify the Git commit + signature(s). + properties: + mode: + description: Mode specifies what Git object should be verified, + currently ('head'). + enum: + - head + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing the public keys of trusted Git + authors. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - mode + - secretRef + type: object + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: GitRepositoryStatus records the observed state of a Git repository. + properties: + artifact: + description: Artifact represents the last successful GitRepository + reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the GitRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + contentConfigChecksum: + description: |- + ContentConfigChecksum is a checksum of all the configurations related to + the content of the source artifact: + - .spec.ignore + - .spec.recurseSubmodules + - .spec.included and the checksum of the included artifacts + observed in .status.observedGeneration version of the object. This can + be used to determine if the content of the included repository has + changed. + It has the format of `:`, for example: `sha256:`. + + + Deprecated: Replaced with explicit fields for observed artifact content + config in the status. + type: string + includedArtifacts: + description: |- + IncludedArtifacts contains a list of the last successfully included + Artifacts as instructed by GitRepositorySpec.Include. + items: + description: Artifact represents the output of a Source reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of + ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI + annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - lastUpdateTime + - path + - revision + - url + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the GitRepository + object. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + observedInclude: + description: |- + ObservedInclude is the observed list of GitRepository resources used to + to produce the current Artifact. + items: + description: |- + GitRepositoryInclude specifies a local reference to a GitRepository which + Artifact (sub-)contents must be included, and where they should be placed. + properties: + fromPath: + description: |- + FromPath specifies the path to copy contents from, defaults to the root + of the Artifact. + type: string + repository: + description: |- + GitRepositoryRef specifies the GitRepository which Artifact contents + must be included. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: |- + ToPath specifies the path to copy contents to, defaults to the name of + the GitRepositoryRef. + type: string + required: + - repository + type: object + type: array + observedRecurseSubmodules: + description: |- + ObservedRecurseSubmodules is the observed resource submodules + configuration used to produce the current Artifact. + type: boolean + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + GitRepositoryStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + labels: + app.kubernetes.io/component: source-controller + name: helmcharts.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: HelmChart + listKind: HelmChartList + plural: helmcharts + shortNames: + - hc + singular: helmchart + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.chart + name: Chart + type: string + - jsonPath: .spec.version + name: Version + type: string + - jsonPath: .spec.sourceRef.kind + name: Source Kind + type: string + - jsonPath: .spec.sourceRef.name + name: Source Name + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: HelmChart is the Schema for the helmcharts API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmChartSpec specifies the desired state of a Helm chart. + properties: + chart: + description: |- + Chart is the name or path the Helm chart is available at in the + SourceRef. + type: string + ignoreMissingValuesFiles: + description: |- + IgnoreMissingValuesFiles controls whether to silently ignore missing values + files rather than failing. + type: boolean + interval: + description: |- + Interval at which the HelmChart SourceRef is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + reconcileStrategy: + default: ChartVersion + description: |- + ReconcileStrategy determines what enables the creation of a new artifact. + Valid values are ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: SourceRef is the reference to the Source the chart is + available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: |- + Kind of the referent, valid values are ('HelmRepository', 'GitRepository', + 'Bucket'). + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + type: string + required: + - kind + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + source. + type: boolean + valuesFiles: + description: |- + ValuesFiles is an alternative list of values files to use as the chart + values (values.yaml is not included by default), expected to be a + relative path in the SourceRef. + Values files are merged in the order of this list with the last file + overriding the first. Ignored when omitted. + items: + type: string + type: array + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + This field is only supported when using HelmRepository source with spec.type 'oci'. + Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified. + properties: + matchOIDCIdentity: + description: |- + MatchOIDCIdentity specifies the identity matching criteria to use + while verifying an OCI artifact which was signed using Cosign keyless + signing. The artifact's identity is deemed to be verified if any of the + specified matchers match against the identity. + items: + description: |- + OIDCIdentityMatch specifies options for verifying the certificate identity, + i.e. the issuer and the subject of the certificate. + properties: + issuer: + description: |- + Issuer specifies the regex pattern to match against to verify + the OIDC issuer in the Fulcio certificate. The pattern must be a + valid Go regular expression. + type: string + subject: + description: |- + Subject specifies the regex pattern to match against to verify + the identity subject in the Fulcio certificate. The pattern must + be a valid Go regular expression. + type: string + required: + - issuer + - subject + type: object + type: array + provider: + default: cosign + description: Provider specifies the technology used to sign the + OCI Artifact. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + version: + default: '*' + description: |- + Version is the chart version semver expression, ignored for charts from + GitRepository and Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: HelmChartStatus records the observed state of the HelmChart. + properties: + artifact: + description: Artifact represents the output of the last successful + reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmChart. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedChartName: + description: |- + ObservedChartName is the last observed chart name as specified by the + resolved chart reference. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the HelmChart + object. + format: int64 + type: integer + observedSourceArtifactRevision: + description: |- + ObservedSourceArtifactRevision is the last observed Artifact.Revision + of the HelmChartSpec.SourceRef. + type: string + observedValuesFiles: + description: |- + ObservedValuesFiles are the observed value files of the last successful + reconciliation. + It matches the chart in the last successfully reconciled artifact. + items: + type: string + type: array + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + BucketStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.chart + name: Chart + type: string + - jsonPath: .spec.version + name: Version + type: string + - jsonPath: .spec.sourceRef.kind + name: Source Kind + type: string + - jsonPath: .spec.sourceRef.name + name: Source Name + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta1 HelmChart is deprecated, upgrade to v1 + name: v1beta1 + schema: + openAPIV3Schema: + description: HelmChart is the Schema for the helmcharts API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmChartSpec defines the desired state of a Helm chart. + properties: + accessFrom: + description: AccessFrom defines an Access Control List for allowing + cross-namespace references to this object. + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + chart: + description: The name or path the Helm chart is available at in the + SourceRef. + type: string + interval: + description: The interval at which to check the Source for updates. + type: string + reconcileStrategy: + default: ChartVersion + description: |- + Determines what enables the creation of a new artifact. Valid values are + ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: The reference to the Source the chart is available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: |- + Kind of the referent, valid values are ('HelmRepository', 'GitRepository', + 'Bucket'). + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + type: string + required: + - kind + - name + type: object + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + valuesFile: + description: |- + Alternative values file to use as the default chart values, expected to + be a relative path in the SourceRef. Deprecated in favor of ValuesFiles, + for backwards compatibility the file defined here is merged before the + ValuesFiles items. Ignored when omitted. + type: string + valuesFiles: + description: |- + Alternative list of values files to use as the chart values (values.yaml + is not included by default), expected to be a relative path in the SourceRef. + Values files are merged in the order of this list with the last file overriding + the first. Ignored when omitted. + items: + type: string + type: array + version: + default: '*' + description: |- + The chart version semver expression, ignored for charts from GitRepository + and Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: HelmChartStatus defines the observed state of the HelmChart. + properties: + artifact: + description: Artifact represents the output of the last successful + chart sync. + properties: + checksum: + description: Checksum is the SHA256 checksum of the artifact. + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of this + artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: |- + Revision is a human readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm + chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmChart. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + url: + description: URL is the download link for the last chart pulled. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.chart + name: Chart + type: string + - jsonPath: .spec.version + name: Version + type: string + - jsonPath: .spec.sourceRef.kind + name: Source Kind + type: string + - jsonPath: .spec.sourceRef.name + name: Source Name + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 HelmChart is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: HelmChart is the Schema for the helmcharts API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmChartSpec specifies the desired state of a Helm chart. + properties: + accessFrom: + description: |- + AccessFrom specifies an Access Control List for allowing cross-namespace + references to this object. + NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + chart: + description: |- + Chart is the name or path the Helm chart is available at in the + SourceRef. + type: string + ignoreMissingValuesFiles: + description: |- + IgnoreMissingValuesFiles controls whether to silently ignore missing values + files rather than failing. + type: boolean + interval: + description: |- + Interval at which the HelmChart SourceRef is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + reconcileStrategy: + default: ChartVersion + description: |- + ReconcileStrategy determines what enables the creation of a new artifact. + Valid values are ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: SourceRef is the reference to the Source the chart is + available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: |- + Kind of the referent, valid values are ('HelmRepository', 'GitRepository', + 'Bucket'). + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + type: string + required: + - kind + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + source. + type: boolean + valuesFile: + description: |- + ValuesFile is an alternative values file to use as the default chart + values, expected to be a relative path in the SourceRef. Deprecated in + favor of ValuesFiles, for backwards compatibility the file specified here + is merged before the ValuesFiles items. Ignored when omitted. + type: string + valuesFiles: + description: |- + ValuesFiles is an alternative list of values files to use as the chart + values (values.yaml is not included by default), expected to be a + relative path in the SourceRef. + Values files are merged in the order of this list with the last file + overriding the first. Ignored when omitted. + items: + type: string + type: array + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + This field is only supported when using HelmRepository source with spec.type 'oci'. + Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified. + properties: + matchOIDCIdentity: + description: |- + MatchOIDCIdentity specifies the identity matching criteria to use + while verifying an OCI artifact which was signed using Cosign keyless + signing. The artifact's identity is deemed to be verified if any of the + specified matchers match against the identity. + items: + description: |- + OIDCIdentityMatch specifies options for verifying the certificate identity, + i.e. the issuer and the subject of the certificate. + properties: + issuer: + description: |- + Issuer specifies the regex pattern to match against to verify + the OIDC issuer in the Fulcio certificate. The pattern must be a + valid Go regular expression. + type: string + subject: + description: |- + Subject specifies the regex pattern to match against to verify + the identity subject in the Fulcio certificate. The pattern must + be a valid Go regular expression. + type: string + required: + - issuer + - subject + type: object + type: array + provider: + default: cosign + description: Provider specifies the technology used to sign the + OCI Artifact. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + version: + default: '*' + description: |- + Version is the chart version semver expression, ignored for charts from + GitRepository and Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: HelmChartStatus records the observed state of the HelmChart. + properties: + artifact: + description: Artifact represents the output of the last successful + reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmChart. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedChartName: + description: |- + ObservedChartName is the last observed chart name as specified by the + resolved chart reference. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the HelmChart + object. + format: int64 + type: integer + observedSourceArtifactRevision: + description: |- + ObservedSourceArtifactRevision is the last observed Artifact.Revision + of the HelmChartSpec.SourceRef. + type: string + observedValuesFiles: + description: |- + ObservedValuesFiles are the observed value files of the last successful + reconciliation. + It matches the chart in the last successfully reconciled artifact. + items: + type: string + type: array + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + BucketStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + labels: + app.kubernetes.io/component: helm-controller + name: helmreleases.helm.toolkit.fluxcd.io +spec: + group: helm.toolkit.fluxcd.io + names: + kind: HelmRelease + listKind: HelmReleaseList + plural: helmreleases + shortNames: + - hr + singular: helmrelease + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v2 + schema: + openAPIV3Schema: + description: HelmRelease is the Schema for the helmreleases API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmReleaseSpec defines the desired state of a Helm release. + properties: + chart: + description: |- + Chart defines the template of the v1.HelmChart that should be created + for this HelmRelease. + properties: + metadata: + description: ObjectMeta holds the template for metadata like labels + and annotations. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + type: object + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + type: object + type: object + spec: + description: Spec holds the template for the v1.HelmChartSpec + for this HelmRelease. + properties: + chart: + description: The name or path the Helm chart is available + at in the SourceRef. + maxLength: 2048 + minLength: 1 + type: string + ignoreMissingValuesFiles: + description: IgnoreMissingValuesFiles controls whether to + silently ignore missing values files rather than failing. + type: boolean + interval: + description: |- + Interval at which to check the v1.Source for updates. Defaults to + 'HelmReleaseSpec.Interval'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + reconcileStrategy: + default: ChartVersion + description: |- + Determines what enables the creation of a new artifact. Valid values are + ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: The name and namespace of the v1.Source the chart + is available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace of the referent. + maxLength: 63 + minLength: 1 + type: string + required: + - name + type: object + valuesFiles: + description: |- + Alternative list of values files to use as the chart values (values.yaml + is not included by default), expected to be a relative path in the SourceRef. + Values files are merged in the order of this list with the last file overriding + the first. Ignored when omitted. + items: + type: string + type: array + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + This field is only supported for OCI sources. + Chart dependencies, which are not bundled in the umbrella chart artifact, + are not verified. + properties: + provider: + default: cosign + description: Provider specifies the technology used to + sign the OCI Helm chart. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + version: + default: '*' + description: |- + Version semver expression, ignored for charts from v1.GitRepository and + v1beta2.Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - sourceRef + type: object + required: + - spec + type: object + chartRef: + description: |- + ChartRef holds a reference to a source controller resource containing the + Helm chart artifact. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - OCIRepository + - HelmChart + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace of the referent, defaults to the namespace of the Kubernetes + resource object that contains the reference. + maxLength: 63 + minLength: 1 + type: string + required: + - kind + - name + type: object + dependsOn: + description: |- + DependsOn may contain a meta.NamespacedObjectReference slice with + references to HelmRelease resources that must be ready before this HelmRelease + can be reconciled. + items: + description: |- + NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any + namespace. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. + type: string + required: + - name + type: object + type: array + driftDetection: + description: |- + DriftDetection holds the configuration for detecting and handling + differences between the manifest in the Helm storage and the resources + currently existing in the cluster. + properties: + ignore: + description: |- + Ignore contains a list of rules for specifying which changes to ignore + during diffing. + items: + description: |- + IgnoreRule defines a rule to selectively disregard specific changes during + the drift detection process. + properties: + paths: + description: |- + Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from + consideration in a Kubernetes object. + items: + type: string + type: array + target: + description: |- + Target is a selector for specifying Kubernetes objects to which this + rule applies. + If Target is not set, the Paths will be ignored for all Kubernetes + objects within the manifest of the Helm release. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - paths + type: object + type: array + mode: + description: |- + Mode defines how differences should be handled between the Helm manifest + and the manifest currently applied to the cluster. + If not explicitly set, it defaults to DiffModeDisabled. + enum: + - enabled + - warn + - disabled + type: string + type: object + install: + description: Install holds the configuration for Helm install actions + for this HelmRelease. + properties: + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Create` and if omitted + CRDs are installed but not updated. + + + Skip: do neither install nor replace (update) any CRDs. + + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + + By default, CRDs are applied (installed) during Helm install action. + With this option users can opt in to CRD replace existing CRDs on Helm + install actions, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + createNamespace: + description: |- + CreateNamespace tells the Helm install action to create the + HelmReleaseSpec.TargetNamespace if it does not exist yet. + On uninstall, the namespace will not be garbage collected. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm install action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm install action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + install has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + install has been performed. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm install + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an install action but fail. Defaults to + 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false'. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using an uninstall, is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + type: object + replace: + description: |- + Replace tells the Helm install action to re-use the 'ReleaseName', but only + if that name is a deleted release which remains in the history. + type: boolean + skipCRDs: + description: |- + SkipCRDs tells the Helm install action to not install any CRDs. By default, + CRDs are installed if not already present. + + + Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm install action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + interval: + description: Interval at which to reconcile the Helm release. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: |- + KubeConfig for reconciling the HelmRelease on a remote cluster. + When used in combination with HelmReleaseSpec.ServiceAccountName, + forces the controller to act on behalf of that Service Account at the + target cluster. + If the --default-service-account flag is set, its value will be used as + a controller level fallback for when HelmReleaseSpec.ServiceAccountName + is empty. + properties: + secretRef: + description: |- + SecretRef holds the name of a secret that contains a key with + the kubeconfig file as the value. If no key is set, the key will default + to 'value'. + It is recommended that the kubeconfig is self-contained, and the secret + is regularly updated if credentials such as a cloud-access-token expire. + Cloud specific `cmd-path` auth helpers will not function without adding + binaries and credentials to the Pod that is responsible for reconciling + Kubernetes resources. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific + default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + required: + - secretRef + type: object + maxHistory: + description: |- + MaxHistory is the number of revisions saved by Helm for this HelmRelease. + Use '0' for an unlimited number of revisions; defaults to '5'. + type: integer + persistentClient: + description: |- + PersistentClient tells the controller to use a persistent Kubernetes + client for this release. When enabled, the client will be reused for the + duration of the reconciliation, instead of being created and destroyed + for each (step of a) Helm action. + + + This can improve performance, but may cause issues with some Helm charts + that for example do create Custom Resource Definitions during installation + outside Helm's CRD lifecycle hooks, which are then not observed to be + available by e.g. post-install hooks. + + + If not set, it defaults to true. + type: boolean + postRenderers: + description: |- + PostRenderers holds an array of Helm PostRenderers, which will be applied in order + of their definition. + items: + description: PostRenderer contains a Helm PostRenderer specification. + properties: + kustomize: + description: Kustomization to apply as PostRenderer. + properties: + images: + description: |- + Images is a list of (image name, new name, new tag or digest) + for changing image names, tags or digests. This can also be achieved with a + patch, but this operator is simpler to specify. + items: + description: Image contains an image name, a new name, + a new tag or digest, which will replace the original + name and tag. + properties: + digest: + description: |- + Digest is the value used to replace the original image tag. + If digest is present NewTag value is ignored. + type: string + name: + description: Name is a tag-less image name. + type: string + newName: + description: NewName is the value used to replace + the original name. + type: string + newTag: + description: NewTag is the value used to replace the + original tag. + type: string + required: + - name + type: object + type: array + patches: + description: |- + Strategic merge and JSON patches, defined as inline YAML objects, + capable of targeting objects based on kind, label and annotation selectors. + items: + description: |- + Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should + be applied to. + properties: + patch: + description: |- + Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with + an array of operation objects. + type: string + target: + description: Target points to the resources that the + patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + type: object + type: array + type: object + type: object + type: array + releaseName: + description: |- + ReleaseName used for the Helm release. Defaults to a composition of + '[TargetNamespace-]Name'. + maxLength: 53 + minLength: 1 + type: string + rollback: + description: Rollback holds the configuration for Helm rollback actions + for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + rollback action when it fails. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + rollback has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + rollback has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement + strategy. + type: boolean + recreate: + description: Recreate performs pod restarts for the resource if + applicable. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm rollback action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + serviceAccountName: + description: |- + The name of the Kubernetes service account to impersonate + when reconciling this HelmRelease. + maxLength: 253 + minLength: 1 + type: string + storageNamespace: + description: |- + StorageNamespace used for the Helm storage. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + suspend: + description: |- + Suspend tells the controller to suspend reconciliation for this HelmRelease, + it does not apply to already started reconciliations. Defaults to false. + type: boolean + targetNamespace: + description: |- + TargetNamespace to target when performing operations for the HelmRelease. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + test: + description: Test holds the configuration for Helm test actions for + this HelmRelease. + properties: + enable: + description: |- + Enable enables Helm test actions for this HelmRelease after an Helm install + or upgrade action has been performed. + type: boolean + filters: + description: Filters is a list of tests to run or exclude from + running. + items: + description: Filter holds the configuration for individual Helm + test filters. + properties: + exclude: + description: Exclude specifies whether the named test should + be excluded. + type: boolean + name: + description: Name is the name of the test. + maxLength: 253 + minLength: 1 + type: string + required: + - name + type: object + type: array + ignoreFailures: + description: |- + IgnoreFailures tells the controller to skip remediation when the Helm tests + are run but fail. Can be overwritten for tests run after install or upgrade + actions in 'Install.IgnoreTestFailures' and 'Upgrade.IgnoreTestFailures'. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation during + the performance of a Helm test action. Defaults to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like Jobs + for hooks) during the performance of a Helm action. Defaults to '5m0s'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + uninstall: + description: Uninstall holds the configuration for Helm uninstall + actions for this HelmRelease. + properties: + deletionPropagation: + default: background + description: |- + DeletionPropagation specifies the deletion propagation policy when + a Helm uninstall is performed. + enum: + - background + - foreground + - orphan + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables waiting for all the resources to be deleted after + a Helm uninstall is performed. + type: boolean + keepHistory: + description: |- + KeepHistory tells Helm to remove all associated resources and mark the + release as deleted, but retain the release history. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm uninstall action. Defaults + to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + upgrade: + description: Upgrade holds the configuration for Helm upgrade actions + for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + upgrade action when it fails. + type: boolean + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Skip` and if omitted + CRDs are neither installed nor upgraded. + + + Skip: do neither install nor replace (update) any CRDs. + + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + + By default, CRDs are not applied during Helm upgrade action. With this + option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm upgrade action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm upgrade action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + upgrade has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + upgrade has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement + strategy. + type: boolean + preserveValues: + description: |- + PreserveValues will make Helm reuse the last release's values and merge in + overrides from 'Values'. Setting this flag makes the HelmRelease + non-declarative. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm upgrade + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an upgrade action but fail. + Defaults to 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false' unless 'Retries' is greater than 0. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using 'Strategy', is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + strategy: + description: Strategy to use for failure remediation. Defaults + to 'rollback'. + enum: + - rollback + - uninstall + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm upgrade action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + values: + description: Values holds the values for this Helm release. + x-kubernetes-preserve-unknown-fields: true + valuesFrom: + description: |- + ValuesFrom holds references to resources containing Helm values for this HelmRelease, + and information about how they should be merged. + items: + description: |- + ValuesReference contains a reference to a resource containing Helm values, + and optionally the key they can be found at. + properties: + kind: + description: Kind of the values referent, valid values are ('Secret', + 'ConfigMap'). + enum: + - Secret + - ConfigMap + type: string + name: + description: |- + Name of the values referent. Should reside in the same namespace as the + referring resource. + maxLength: 253 + minLength: 1 + type: string + optional: + description: |- + Optional marks this ValuesReference as optional. When set, a not found error + for the values reference is ignored, but any ValuesKey, TargetPath or + transient error will still result in a reconciliation failure. + type: boolean + targetPath: + description: |- + TargetPath is the YAML dot notation path the value should be merged at. When + set, the ValuesKey is expected to be a single flat value. Defaults to 'None', + which results in the values getting merged at the root. + maxLength: 250 + pattern: ^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$ + type: string + valuesKey: + description: |- + ValuesKey is the data key where the values.yaml or a specific value can be + found at. Defaults to 'values.yaml'. + maxLength: 253 + pattern: ^[\-._a-zA-Z0-9]+$ + type: string + required: + - kind + - name + type: object + type: array + required: + - interval + type: object + x-kubernetes-validations: + - message: either chart or chartRef must be set + rule: (has(self.chart) && !has(self.chartRef)) || (!has(self.chart) + && has(self.chartRef)) + status: + default: + observedGeneration: -1 + description: HelmReleaseStatus defines the observed state of a HelmRelease. + properties: + conditions: + description: Conditions holds the conditions for the HelmRelease. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + failures: + description: |- + Failures is the reconciliation failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + helmChart: + description: |- + HelmChart is the namespaced name of the HelmChart resource created by + the controller for the HelmRelease. + type: string + history: + description: |- + History holds the history of Helm releases performed for this HelmRelease + up to the last successfully completed release. + items: + description: |- + Snapshot captures a point-in-time copy of the status information for a Helm release, + as managed by the controller. + properties: + apiVersion: + description: |- + APIVersion is the API version of the Snapshot. + Provisional: when the calculation method of the Digest field is changed, + this field will be used to distinguish between the old and new methods. + type: string + appVersion: + description: AppVersion is the chart app version of the release + object in storage. + type: string + chartName: + description: ChartName is the chart name of the release object + in storage. + type: string + chartVersion: + description: |- + ChartVersion is the chart version of the release object in + storage. + type: string + configDigest: + description: |- + ConfigDigest is the checksum of the config (better known as + "values") of the release object in storage. + It has the format of `:`. + type: string + deleted: + description: Deleted is when the release was deleted. + format: date-time + type: string + digest: + description: |- + Digest is the checksum of the release object in storage. + It has the format of `:`. + type: string + firstDeployed: + description: FirstDeployed is when the release was first deployed. + format: date-time + type: string + lastDeployed: + description: LastDeployed is when the release was last deployed. + format: date-time + type: string + name: + description: Name is the name of the release. + type: string + namespace: + description: Namespace is the namespace the release is deployed + to. + type: string + ociDigest: + description: OCIDigest is the digest of the OCI artifact associated + with the release. + type: string + status: + description: Status is the current state of the release. + type: string + testHooks: + additionalProperties: + description: |- + TestHookStatus holds the status information for a test hook as observed + to be run by the controller. + properties: + lastCompleted: + description: LastCompleted is the time the test hook last + completed. + format: date-time + type: string + lastStarted: + description: LastStarted is the time the test hook was + last started. + format: date-time + type: string + phase: + description: Phase the test hook was observed to be in. + type: string + type: object + description: |- + TestHooks is the list of test hooks for the release as observed to be + run by the controller. + type: object + version: + description: Version is the version of the release object in + storage. + type: integer + required: + - chartName + - chartVersion + - configDigest + - digest + - firstDeployed + - lastDeployed + - name + - namespace + - status + - version + type: object + type: array + installFailures: + description: |- + InstallFailures is the install failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + lastAttemptedConfigDigest: + description: |- + LastAttemptedConfigDigest is the digest for the config (better known as + "values") of the last reconciliation attempt. + type: string + lastAttemptedGeneration: + description: |- + LastAttemptedGeneration is the last generation the controller attempted + to reconcile. + format: int64 + type: integer + lastAttemptedReleaseAction: + description: |- + LastAttemptedReleaseAction is the last release action performed for this + HelmRelease. It is used to determine the active remediation strategy. + enum: + - install + - upgrade + type: string + lastAttemptedRevision: + description: |- + LastAttemptedRevision is the Source revision of the last reconciliation + attempt. For OCIRepository sources, the 12 first characters of the digest are + appended to the chart version e.g. "1.2.3+1234567890ab". + type: string + lastAttemptedRevisionDigest: + description: |- + LastAttemptedRevisionDigest is the digest of the last reconciliation attempt. + This is only set for OCIRepository sources. + type: string + lastAttemptedValuesChecksum: + description: |- + LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last + reconciliation attempt. + Deprecated: Use LastAttemptedConfigDigest instead. + type: string + lastHandledForceAt: + description: |- + LastHandledForceAt holds the value of the most recent force request + value, so a change of the annotation value can be detected. + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastHandledResetAt: + description: |- + LastHandledResetAt holds the value of the most recent reset request + value, so a change of the annotation value can be detected. + type: string + lastReleaseRevision: + description: |- + LastReleaseRevision is the revision of the last successful Helm release. + Deprecated: Use History instead. + type: integer + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + observedPostRenderersDigest: + description: |- + ObservedPostRenderersDigest is the digest for the post-renderers of + the last successful reconciliation attempt. + type: string + storageNamespace: + description: |- + StorageNamespace is the namespace of the Helm release storage for the + current release. + maxLength: 63 + minLength: 1 + type: string + upgradeFailures: + description: |- + UpgradeFailures is the upgrade failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v2beta1 HelmRelease is deprecated, upgrade to v2 + name: v2beta1 + schema: + openAPIV3Schema: + description: HelmRelease is the Schema for the helmreleases API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmReleaseSpec defines the desired state of a Helm release. + properties: + chart: + description: |- + Chart defines the template of the v1beta2.HelmChart that should be created + for this HelmRelease. + properties: + metadata: + description: ObjectMeta holds the template for metadata like labels + and annotations. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + type: object + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + type: object + type: object + spec: + description: Spec holds the template for the v1beta2.HelmChartSpec + for this HelmRelease. + properties: + chart: + description: The name or path the Helm chart is available + at in the SourceRef. + type: string + interval: + description: |- + Interval at which to check the v1beta2.Source for updates. Defaults to + 'HelmReleaseSpec.Interval'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + reconcileStrategy: + default: ChartVersion + description: |- + Determines what enables the creation of a new artifact. Valid values are + ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: The name and namespace of the v1beta2.Source + the chart is available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace of the referent. + maxLength: 63 + minLength: 1 + type: string + required: + - name + type: object + valuesFile: + description: |- + Alternative values file to use as the default chart values, expected to + be a relative path in the SourceRef. Deprecated in favor of ValuesFiles, + for backwards compatibility the file defined here is merged before the + ValuesFiles items. Ignored when omitted. + type: string + valuesFiles: + description: |- + Alternative list of values files to use as the chart values (values.yaml + is not included by default), expected to be a relative path in the SourceRef. + Values files are merged in the order of this list with the last file overriding + the first. Ignored when omitted. + items: + type: string + type: array + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + This field is only supported for OCI sources. + Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified. + properties: + provider: + default: cosign + description: Provider specifies the technology used to + sign the OCI Helm chart. + enum: + - cosign + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + version: + default: '*' + description: |- + Version semver expression, ignored for charts from v1beta2.GitRepository and + v1beta2.Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - sourceRef + type: object + required: + - spec + type: object + chartRef: + description: |- + ChartRef holds a reference to a source controller resource containing the + Helm chart artifact. + + + Note: this field is provisional to the v2 API, and not actively used + by v2beta1 HelmReleases. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - OCIRepository + - HelmChart + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace of the referent, defaults to the namespace of the Kubernetes + resource object that contains the reference. + maxLength: 63 + minLength: 1 + type: string + required: + - kind + - name + type: object + dependsOn: + description: |- + DependsOn may contain a meta.NamespacedObjectReference slice with + references to HelmRelease resources that must be ready before this HelmRelease + can be reconciled. + items: + description: |- + NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any + namespace. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. + type: string + required: + - name + type: object + type: array + driftDetection: + description: |- + DriftDetection holds the configuration for detecting and handling + differences between the manifest in the Helm storage and the resources + currently existing in the cluster. + + + Note: this field is provisional to the v2beta2 API, and not actively used + by v2beta1 HelmReleases. + properties: + ignore: + description: |- + Ignore contains a list of rules for specifying which changes to ignore + during diffing. + items: + description: |- + IgnoreRule defines a rule to selectively disregard specific changes during + the drift detection process. + properties: + paths: + description: |- + Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from + consideration in a Kubernetes object. + items: + type: string + type: array + target: + description: |- + Target is a selector for specifying Kubernetes objects to which this + rule applies. + If Target is not set, the Paths will be ignored for all Kubernetes + objects within the manifest of the Helm release. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - paths + type: object + type: array + mode: + description: |- + Mode defines how differences should be handled between the Helm manifest + and the manifest currently applied to the cluster. + If not explicitly set, it defaults to DiffModeDisabled. + enum: + - enabled + - warn + - disabled + type: string + type: object + install: + description: Install holds the configuration for Helm install actions + for this HelmRelease. + properties: + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Create` and if omitted + CRDs are installed but not updated. + + + Skip: do neither install nor replace (update) any CRDs. + + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + + By default, CRDs are applied (installed) during Helm install action. + With this option users can opt-in to CRD replace existing CRDs on Helm + install actions, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + createNamespace: + description: |- + CreateNamespace tells the Helm install action to create the + HelmReleaseSpec.TargetNamespace if it does not exist yet. + On uninstall, the namespace will not be garbage collected. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm install action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm install action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + install has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + install has been performed. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm install + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an install action but fail. Defaults to + 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false'. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using an uninstall, is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + type: object + replace: + description: |- + Replace tells the Helm install action to re-use the 'ReleaseName', but only + if that name is a deleted release which remains in the history. + type: boolean + skipCRDs: + description: |- + SkipCRDs tells the Helm install action to not install any CRDs. By default, + CRDs are installed if not already present. + + + Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm install action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + interval: + description: |- + Interval at which to reconcile the Helm release. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: |- + KubeConfig for reconciling the HelmRelease on a remote cluster. + When used in combination with HelmReleaseSpec.ServiceAccountName, + forces the controller to act on behalf of that Service Account at the + target cluster. + If the --default-service-account flag is set, its value will be used as + a controller level fallback for when HelmReleaseSpec.ServiceAccountName + is empty. + properties: + secretRef: + description: |- + SecretRef holds the name of a secret that contains a key with + the kubeconfig file as the value. If no key is set, the key will default + to 'value'. + It is recommended that the kubeconfig is self-contained, and the secret + is regularly updated if credentials such as a cloud-access-token expire. + Cloud specific `cmd-path` auth helpers will not function without adding + binaries and credentials to the Pod that is responsible for reconciling + Kubernetes resources. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific + default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + required: + - secretRef + type: object + maxHistory: + description: |- + MaxHistory is the number of revisions saved by Helm for this HelmRelease. + Use '0' for an unlimited number of revisions; defaults to '10'. + type: integer + persistentClient: + description: |- + PersistentClient tells the controller to use a persistent Kubernetes + client for this release. When enabled, the client will be reused for the + duration of the reconciliation, instead of being created and destroyed + for each (step of a) Helm action. + + + This can improve performance, but may cause issues with some Helm charts + that for example do create Custom Resource Definitions during installation + outside Helm's CRD lifecycle hooks, which are then not observed to be + available by e.g. post-install hooks. + + + If not set, it defaults to true. + type: boolean + postRenderers: + description: |- + PostRenderers holds an array of Helm PostRenderers, which will be applied in order + of their definition. + items: + description: PostRenderer contains a Helm PostRenderer specification. + properties: + kustomize: + description: Kustomization to apply as PostRenderer. + properties: + images: + description: |- + Images is a list of (image name, new name, new tag or digest) + for changing image names, tags or digests. This can also be achieved with a + patch, but this operator is simpler to specify. + items: + description: Image contains an image name, a new name, + a new tag or digest, which will replace the original + name and tag. + properties: + digest: + description: |- + Digest is the value used to replace the original image tag. + If digest is present NewTag value is ignored. + type: string + name: + description: Name is a tag-less image name. + type: string + newName: + description: NewName is the value used to replace + the original name. + type: string + newTag: + description: NewTag is the value used to replace the + original tag. + type: string + required: + - name + type: object + type: array + patches: + description: |- + Strategic merge and JSON patches, defined as inline YAML objects, + capable of targeting objects based on kind, label and annotation selectors. + items: + description: |- + Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should + be applied to. + properties: + patch: + description: |- + Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with + an array of operation objects. + type: string + target: + description: Target points to the resources that the + patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + type: object + type: array + patchesJson6902: + description: JSON 6902 patches, defined as inline YAML objects. + items: + description: JSON6902Patch contains a JSON6902 patch and + the target the patch should be applied to. + properties: + patch: + description: Patch contains the JSON6902 patch document + with an array of operation objects. + items: + description: |- + JSON6902 is a JSON6902 operation object. + https://datatracker.ietf.org/doc/html/rfc6902#section-4 + properties: + from: + description: |- + From contains a JSON-pointer value that references a location within the target document where the operation is + performed. The meaning of the value depends on the value of Op, and is NOT taken into account by all operations. + type: string + op: + description: |- + Op indicates the operation to perform. Its value MUST be one of "add", "remove", "replace", "move", "copy", or + "test". + https://datatracker.ietf.org/doc/html/rfc6902#section-4 + enum: + - test + - remove + - add + - replace + - move + - copy + type: string + path: + description: |- + Path contains the JSON-pointer value that references a location within the target document where the operation + is performed. The meaning of the value depends on the value of Op. + type: string + value: + description: |- + Value contains a valid JSON structure. The meaning of the value depends on the value of Op, and is NOT taken into + account by all operations. + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + target: + description: Target points to the resources that the + patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + - target + type: object + type: array + patchesStrategicMerge: + description: Strategic merge patches, defined as inline + YAML objects. + items: + x-kubernetes-preserve-unknown-fields: true + type: array + type: object + type: object + type: array + releaseName: + description: |- + ReleaseName used for the Helm release. Defaults to a composition of + '[TargetNamespace-]Name'. + maxLength: 53 + minLength: 1 + type: string + rollback: + description: Rollback holds the configuration for Helm rollback actions + for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + rollback action when it fails. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + rollback has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + rollback has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement + strategy. + type: boolean + recreate: + description: Recreate performs pod restarts for the resource if + applicable. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm rollback action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + serviceAccountName: + description: |- + The name of the Kubernetes service account to impersonate + when reconciling this HelmRelease. + type: string + storageNamespace: + description: |- + StorageNamespace used for the Helm storage. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + suspend: + description: |- + Suspend tells the controller to suspend reconciliation for this HelmRelease, + it does not apply to already started reconciliations. Defaults to false. + type: boolean + targetNamespace: + description: |- + TargetNamespace to target when performing operations for the HelmRelease. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + test: + description: Test holds the configuration for Helm test actions for + this HelmRelease. + properties: + enable: + description: |- + Enable enables Helm test actions for this HelmRelease after an Helm install + or upgrade action has been performed. + type: boolean + ignoreFailures: + description: |- + IgnoreFailures tells the controller to skip remediation when the Helm tests + are run but fail. Can be overwritten for tests run after install or upgrade + actions in 'Install.IgnoreTestFailures' and 'Upgrade.IgnoreTestFailures'. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation during + the performance of a Helm test action. Defaults to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like Jobs + for hooks) during the performance of a Helm action. Defaults to '5m0s'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + uninstall: + description: Uninstall holds the configuration for Helm uninstall + actions for this HelmRelease. + properties: + deletionPropagation: + default: background + description: |- + DeletionPropagation specifies the deletion propagation policy when + a Helm uninstall is performed. + enum: + - background + - foreground + - orphan + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables waiting for all the resources to be deleted after + a Helm uninstall is performed. + type: boolean + keepHistory: + description: |- + KeepHistory tells Helm to remove all associated resources and mark the + release as deleted, but retain the release history. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm uninstall action. Defaults + to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + upgrade: + description: Upgrade holds the configuration for Helm upgrade actions + for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + upgrade action when it fails. + type: boolean + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Skip` and if omitted + CRDs are neither installed nor upgraded. + + + Skip: do neither install nor replace (update) any CRDs. + + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + + By default, CRDs are not applied during Helm upgrade action. With this + option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm upgrade action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm upgrade action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + upgrade has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + upgrade has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement + strategy. + type: boolean + preserveValues: + description: |- + PreserveValues will make Helm reuse the last release's values and merge in + overrides from 'Values'. Setting this flag makes the HelmRelease + non-declarative. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm upgrade + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an upgrade action but fail. + Defaults to 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false' unless 'Retries' is greater than 0. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using 'Strategy', is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + strategy: + description: Strategy to use for failure remediation. Defaults + to 'rollback'. + enum: + - rollback + - uninstall + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm upgrade action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + values: + description: Values holds the values for this Helm release. + x-kubernetes-preserve-unknown-fields: true + valuesFrom: + description: |- + ValuesFrom holds references to resources containing Helm values for this HelmRelease, + and information about how they should be merged. + items: + description: |- + ValuesReference contains a reference to a resource containing Helm values, + and optionally the key they can be found at. + properties: + kind: + description: Kind of the values referent, valid values are ('Secret', + 'ConfigMap'). + enum: + - Secret + - ConfigMap + type: string + name: + description: |- + Name of the values referent. Should reside in the same namespace as the + referring resource. + maxLength: 253 + minLength: 1 + type: string + optional: + description: |- + Optional marks this ValuesReference as optional. When set, a not found error + for the values reference is ignored, but any ValuesKey, TargetPath or + transient error will still result in a reconciliation failure. + type: boolean + targetPath: + description: |- + TargetPath is the YAML dot notation path the value should be merged at. When + set, the ValuesKey is expected to be a single flat value. Defaults to 'None', + which results in the values getting merged at the root. + maxLength: 250 + pattern: ^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$ + type: string + valuesKey: + description: |- + ValuesKey is the data key where the values.yaml or a specific value can be + found at. Defaults to 'values.yaml'. + When set, must be a valid Data Key, consisting of alphanumeric characters, + '-', '_' or '.'. + maxLength: 253 + pattern: ^[\-._a-zA-Z0-9]+$ + type: string + required: + - kind + - name + type: object + type: array + required: + - interval + type: object + status: + default: + observedGeneration: -1 + description: HelmReleaseStatus defines the observed state of a HelmRelease. + properties: + conditions: + description: Conditions holds the conditions for the HelmRelease. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + failures: + description: |- + Failures is the reconciliation failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + helmChart: + description: |- + HelmChart is the namespaced name of the HelmChart resource created by + the controller for the HelmRelease. + type: string + history: + description: |- + History holds the history of Helm releases performed for this HelmRelease + up to the last successfully completed release. + + + Note: this field is provisional to the v2beta2 API, and not actively used + by v2beta1 HelmReleases. + items: + description: |- + Snapshot captures a point-in-time copy of the status information for a Helm release, + as managed by the controller. + properties: + apiVersion: + description: |- + APIVersion is the API version of the Snapshot. + Provisional: when the calculation method of the Digest field is changed, + this field will be used to distinguish between the old and new methods. + type: string + appVersion: + description: AppVersion is the chart app version of the release + object in storage. + type: string + chartName: + description: ChartName is the chart name of the release object + in storage. + type: string + chartVersion: + description: |- + ChartVersion is the chart version of the release object in + storage. + type: string + configDigest: + description: |- + ConfigDigest is the checksum of the config (better known as + "values") of the release object in storage. + It has the format of `:`. + type: string + deleted: + description: Deleted is when the release was deleted. + format: date-time + type: string + digest: + description: |- + Digest is the checksum of the release object in storage. + It has the format of `:`. + type: string + firstDeployed: + description: FirstDeployed is when the release was first deployed. + format: date-time + type: string + lastDeployed: + description: LastDeployed is when the release was last deployed. + format: date-time + type: string + name: + description: Name is the name of the release. + type: string + namespace: + description: Namespace is the namespace the release is deployed + to. + type: string + ociDigest: + description: OCIDigest is the digest of the OCI artifact associated + with the release. + type: string + status: + description: Status is the current state of the release. + type: string + testHooks: + additionalProperties: + description: |- + TestHookStatus holds the status information for a test hook as observed + to be run by the controller. + properties: + lastCompleted: + description: LastCompleted is the time the test hook last + completed. + format: date-time + type: string + lastStarted: + description: LastStarted is the time the test hook was + last started. + format: date-time + type: string + phase: + description: Phase the test hook was observed to be in. + type: string + type: object + description: |- + TestHooks is the list of test hooks for the release as observed to be + run by the controller. + type: object + version: + description: Version is the version of the release object in + storage. + type: integer + required: + - chartName + - chartVersion + - configDigest + - digest + - firstDeployed + - lastDeployed + - name + - namespace + - status + - version + type: object + type: array + installFailures: + description: |- + InstallFailures is the install failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + lastAppliedRevision: + description: LastAppliedRevision is the revision of the last successfully + applied source. + type: string + lastAttemptedConfigDigest: + description: |- + LastAttemptedConfigDigest is the digest for the config (better known as + "values") of the last reconciliation attempt. + + + Note: this field is provisional to the v2beta2 API, and not actively used + by v2beta1 HelmReleases. + type: string + lastAttemptedGeneration: + description: |- + LastAttemptedGeneration is the last generation the controller attempted + to reconcile. + + + Note: this field is provisional to the v2beta2 API, and not actively used + by v2beta1 HelmReleases. + format: int64 + type: integer + lastAttemptedReleaseAction: + description: |- + LastAttemptedReleaseAction is the last release action performed for this + HelmRelease. It is used to determine the active remediation strategy. + + + Note: this field is provisional to the v2beta2 API, and not actively used + by v2beta1 HelmReleases. + type: string + lastAttemptedRevision: + description: LastAttemptedRevision is the revision of the last reconciliation + attempt. + type: string + lastAttemptedValuesChecksum: + description: |- + LastAttemptedValuesChecksum is the SHA1 checksum of the values of the last + reconciliation attempt. + type: string + lastHandledForceAt: + description: |- + LastHandledForceAt holds the value of the most recent force request + value, so a change of the annotation value can be detected. + + + Note: this field is provisional to the v2beta2 API, and not actively used + by v2beta1 HelmReleases. + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastHandledResetAt: + description: |- + LastHandledResetAt holds the value of the most recent reset request + value, so a change of the annotation value can be detected. + + + Note: this field is provisional to the v2beta2 API, and not actively used + by v2beta1 HelmReleases. + type: string + lastReleaseRevision: + description: LastReleaseRevision is the revision of the last successful + Helm release. + type: integer + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + observedPostRenderersDigest: + description: |- + ObservedPostRenderersDigest is the digest for the post-renderers of + the last successful reconciliation attempt. + type: string + storageNamespace: + description: |- + StorageNamespace is the namespace of the Helm release storage for the + current release. + + + Note: this field is provisional to the v2beta2 API, and not actively used + by v2beta1 HelmReleases. + type: string + upgradeFailures: + description: |- + UpgradeFailures is the upgrade failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v2beta2 HelmRelease is deprecated, upgrade to v2 + name: v2beta2 + schema: + openAPIV3Schema: + description: HelmRelease is the Schema for the helmreleases API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmReleaseSpec defines the desired state of a Helm release. + properties: + chart: + description: |- + Chart defines the template of the v1beta2.HelmChart that should be created + for this HelmRelease. + properties: + metadata: + description: ObjectMeta holds the template for metadata like labels + and annotations. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + type: object + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + type: object + type: object + spec: + description: Spec holds the template for the v1beta2.HelmChartSpec + for this HelmRelease. + properties: + chart: + description: The name or path the Helm chart is available + at in the SourceRef. + maxLength: 2048 + minLength: 1 + type: string + ignoreMissingValuesFiles: + description: IgnoreMissingValuesFiles controls whether to + silently ignore missing values files rather than failing. + type: boolean + interval: + description: |- + Interval at which to check the v1.Source for updates. Defaults to + 'HelmReleaseSpec.Interval'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + reconcileStrategy: + default: ChartVersion + description: |- + Determines what enables the creation of a new artifact. Valid values are + ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: The name and namespace of the v1.Source the chart + is available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace of the referent. + maxLength: 63 + minLength: 1 + type: string + required: + - name + type: object + valuesFile: + description: |- + Alternative values file to use as the default chart values, expected to + be a relative path in the SourceRef. Deprecated in favor of ValuesFiles, + for backwards compatibility the file defined here is merged before the + ValuesFiles items. Ignored when omitted. + type: string + valuesFiles: + description: |- + Alternative list of values files to use as the chart values (values.yaml + is not included by default), expected to be a relative path in the SourceRef. + Values files are merged in the order of this list with the last file overriding + the first. Ignored when omitted. + items: + type: string + type: array + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + This field is only supported for OCI sources. + Chart dependencies, which are not bundled in the umbrella chart artifact, + are not verified. + properties: + provider: + default: cosign + description: Provider specifies the technology used to + sign the OCI Helm chart. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + version: + default: '*' + description: |- + Version semver expression, ignored for charts from v1beta2.GitRepository and + v1beta2.Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - sourceRef + type: object + required: + - spec + type: object + chartRef: + description: |- + ChartRef holds a reference to a source controller resource containing the + Helm chart artifact. + + + Note: this field is provisional to the v2 API, and not actively used + by v2beta2 HelmReleases. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - OCIRepository + - HelmChart + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace of the referent, defaults to the namespace of the Kubernetes + resource object that contains the reference. + maxLength: 63 + minLength: 1 + type: string + required: + - kind + - name + type: object + dependsOn: + description: |- + DependsOn may contain a meta.NamespacedObjectReference slice with + references to HelmRelease resources that must be ready before this HelmRelease + can be reconciled. + items: + description: |- + NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any + namespace. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it + acts as LocalObjectReference. + type: string + required: + - name + type: object + type: array + driftDetection: + description: |- + DriftDetection holds the configuration for detecting and handling + differences between the manifest in the Helm storage and the resources + currently existing in the cluster. + properties: + ignore: + description: |- + Ignore contains a list of rules for specifying which changes to ignore + during diffing. + items: + description: |- + IgnoreRule defines a rule to selectively disregard specific changes during + the drift detection process. + properties: + paths: + description: |- + Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from + consideration in a Kubernetes object. + items: + type: string + type: array + target: + description: |- + Target is a selector for specifying Kubernetes objects to which this + rule applies. + If Target is not set, the Paths will be ignored for all Kubernetes + objects within the manifest of the Helm release. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - paths + type: object + type: array + mode: + description: |- + Mode defines how differences should be handled between the Helm manifest + and the manifest currently applied to the cluster. + If not explicitly set, it defaults to DiffModeDisabled. + enum: + - enabled + - warn + - disabled + type: string + type: object + install: + description: Install holds the configuration for Helm install actions + for this HelmRelease. + properties: + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Create` and if omitted + CRDs are installed but not updated. + + + Skip: do neither install nor replace (update) any CRDs. + + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + + By default, CRDs are applied (installed) during Helm install action. + With this option users can opt in to CRD replace existing CRDs on Helm + install actions, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + createNamespace: + description: |- + CreateNamespace tells the Helm install action to create the + HelmReleaseSpec.TargetNamespace if it does not exist yet. + On uninstall, the namespace will not be garbage collected. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm install action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm install action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + install has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + install has been performed. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm install + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an install action but fail. Defaults to + 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false'. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using an uninstall, is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + type: object + replace: + description: |- + Replace tells the Helm install action to re-use the 'ReleaseName', but only + if that name is a deleted release which remains in the history. + type: boolean + skipCRDs: + description: |- + SkipCRDs tells the Helm install action to not install any CRDs. By default, + CRDs are installed if not already present. + + + Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm install action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + interval: + description: Interval at which to reconcile the Helm release. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: |- + KubeConfig for reconciling the HelmRelease on a remote cluster. + When used in combination with HelmReleaseSpec.ServiceAccountName, + forces the controller to act on behalf of that Service Account at the + target cluster. + If the --default-service-account flag is set, its value will be used as + a controller level fallback for when HelmReleaseSpec.ServiceAccountName + is empty. + properties: + secretRef: + description: |- + SecretRef holds the name of a secret that contains a key with + the kubeconfig file as the value. If no key is set, the key will default + to 'value'. + It is recommended that the kubeconfig is self-contained, and the secret + is regularly updated if credentials such as a cloud-access-token expire. + Cloud specific `cmd-path` auth helpers will not function without adding + binaries and credentials to the Pod that is responsible for reconciling + Kubernetes resources. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific + default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + required: + - secretRef + type: object + maxHistory: + description: |- + MaxHistory is the number of revisions saved by Helm for this HelmRelease. + Use '0' for an unlimited number of revisions; defaults to '5'. + type: integer + persistentClient: + description: |- + PersistentClient tells the controller to use a persistent Kubernetes + client for this release. When enabled, the client will be reused for the + duration of the reconciliation, instead of being created and destroyed + for each (step of a) Helm action. + + + This can improve performance, but may cause issues with some Helm charts + that for example do create Custom Resource Definitions during installation + outside Helm's CRD lifecycle hooks, which are then not observed to be + available by e.g. post-install hooks. + + + If not set, it defaults to true. + type: boolean + postRenderers: + description: |- + PostRenderers holds an array of Helm PostRenderers, which will be applied in order + of their definition. + items: + description: PostRenderer contains a Helm PostRenderer specification. + properties: + kustomize: + description: Kustomization to apply as PostRenderer. + properties: + images: + description: |- + Images is a list of (image name, new name, new tag or digest) + for changing image names, tags or digests. This can also be achieved with a + patch, but this operator is simpler to specify. + items: + description: Image contains an image name, a new name, + a new tag or digest, which will replace the original + name and tag. + properties: + digest: + description: |- + Digest is the value used to replace the original image tag. + If digest is present NewTag value is ignored. + type: string + name: + description: Name is a tag-less image name. + type: string + newName: + description: NewName is the value used to replace + the original name. + type: string + newTag: + description: NewTag is the value used to replace the + original tag. + type: string + required: + - name + type: object + type: array + patches: + description: |- + Strategic merge and JSON patches, defined as inline YAML objects, + capable of targeting objects based on kind, label and annotation selectors. + items: + description: |- + Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should + be applied to. + properties: + patch: + description: |- + Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with + an array of operation objects. + type: string + target: + description: Target points to the resources that the + patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + type: object + type: array + patchesJson6902: + description: |- + JSON 6902 patches, defined as inline YAML objects. + Deprecated: use Patches instead. + items: + description: JSON6902Patch contains a JSON6902 patch and + the target the patch should be applied to. + properties: + patch: + description: Patch contains the JSON6902 patch document + with an array of operation objects. + items: + description: |- + JSON6902 is a JSON6902 operation object. + https://datatracker.ietf.org/doc/html/rfc6902#section-4 + properties: + from: + description: |- + From contains a JSON-pointer value that references a location within the target document where the operation is + performed. The meaning of the value depends on the value of Op, and is NOT taken into account by all operations. + type: string + op: + description: |- + Op indicates the operation to perform. Its value MUST be one of "add", "remove", "replace", "move", "copy", or + "test". + https://datatracker.ietf.org/doc/html/rfc6902#section-4 + enum: + - test + - remove + - add + - replace + - move + - copy + type: string + path: + description: |- + Path contains the JSON-pointer value that references a location within the target document where the operation + is performed. The meaning of the value depends on the value of Op. + type: string + value: + description: |- + Value contains a valid JSON structure. The meaning of the value depends on the value of Op, and is NOT taken into + account by all operations. + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + target: + description: Target points to the resources that the + patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + - target + type: object + type: array + patchesStrategicMerge: + description: |- + Strategic merge patches, defined as inline YAML objects. + Deprecated: use Patches instead. + items: + x-kubernetes-preserve-unknown-fields: true + type: array + type: object + type: object + type: array + releaseName: + description: |- + ReleaseName used for the Helm release. Defaults to a composition of + '[TargetNamespace-]Name'. + maxLength: 53 + minLength: 1 + type: string + rollback: + description: Rollback holds the configuration for Helm rollback actions + for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + rollback action when it fails. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + rollback has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + rollback has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement + strategy. + type: boolean + recreate: + description: Recreate performs pod restarts for the resource if + applicable. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm rollback action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + serviceAccountName: + description: |- + The name of the Kubernetes service account to impersonate + when reconciling this HelmRelease. + maxLength: 253 + minLength: 1 + type: string + storageNamespace: + description: |- + StorageNamespace used for the Helm storage. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + suspend: + description: |- + Suspend tells the controller to suspend reconciliation for this HelmRelease, + it does not apply to already started reconciliations. Defaults to false. + type: boolean + targetNamespace: + description: |- + TargetNamespace to target when performing operations for the HelmRelease. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + test: + description: Test holds the configuration for Helm test actions for + this HelmRelease. + properties: + enable: + description: |- + Enable enables Helm test actions for this HelmRelease after an Helm install + or upgrade action has been performed. + type: boolean + filters: + description: Filters is a list of tests to run or exclude from + running. + items: + description: Filter holds the configuration for individual Helm + test filters. + properties: + exclude: + description: Exclude specifies whether the named test should + be excluded. + type: boolean + name: + description: Name is the name of the test. + maxLength: 253 + minLength: 1 + type: string + required: + - name + type: object + type: array + ignoreFailures: + description: |- + IgnoreFailures tells the controller to skip remediation when the Helm tests + are run but fail. Can be overwritten for tests run after install or upgrade + actions in 'Install.IgnoreTestFailures' and 'Upgrade.IgnoreTestFailures'. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation during + the performance of a Helm test action. Defaults to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like Jobs + for hooks) during the performance of a Helm action. Defaults to '5m0s'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + uninstall: + description: Uninstall holds the configuration for Helm uninstall + actions for this HelmRelease. + properties: + deletionPropagation: + default: background + description: |- + DeletionPropagation specifies the deletion propagation policy when + a Helm uninstall is performed. + enum: + - background + - foreground + - orphan + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables waiting for all the resources to be deleted after + a Helm uninstall is performed. + type: boolean + keepHistory: + description: |- + KeepHistory tells Helm to remove all associated resources and mark the + release as deleted, but retain the release history. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm uninstall action. Defaults + to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + upgrade: + description: Upgrade holds the configuration for Helm upgrade actions + for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + upgrade action when it fails. + type: boolean + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Skip` and if omitted + CRDs are neither installed nor upgraded. + + + Skip: do neither install nor replace (update) any CRDs. + + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + + By default, CRDs are not applied during Helm upgrade action. With this + option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the + Helm upgrade action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm upgrade action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + upgrade has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + upgrade has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement + strategy. + type: boolean + preserveValues: + description: |- + PreserveValues will make Helm reuse the last release's values and merge in + overrides from 'Values'. Setting this flag makes the HelmRelease + non-declarative. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm upgrade + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an upgrade action but fail. + Defaults to 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false' unless 'Retries' is greater than 0. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using 'Strategy', is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + strategy: + description: Strategy to use for failure remediation. Defaults + to 'rollback'. + enum: + - rollback + - uninstall + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm upgrade action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + values: + description: Values holds the values for this Helm release. + x-kubernetes-preserve-unknown-fields: true + valuesFrom: + description: |- + ValuesFrom holds references to resources containing Helm values for this HelmRelease, + and information about how they should be merged. + items: + description: |- + ValuesReference contains a reference to a resource containing Helm values, + and optionally the key they can be found at. + properties: + kind: + description: Kind of the values referent, valid values are ('Secret', + 'ConfigMap'). + enum: + - Secret + - ConfigMap + type: string + name: + description: |- + Name of the values referent. Should reside in the same namespace as the + referring resource. + maxLength: 253 + minLength: 1 + type: string + optional: + description: |- + Optional marks this ValuesReference as optional. When set, a not found error + for the values reference is ignored, but any ValuesKey, TargetPath or + transient error will still result in a reconciliation failure. + type: boolean + targetPath: + description: |- + TargetPath is the YAML dot notation path the value should be merged at. When + set, the ValuesKey is expected to be a single flat value. Defaults to 'None', + which results in the values getting merged at the root. + maxLength: 250 + pattern: ^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$ + type: string + valuesKey: + description: |- + ValuesKey is the data key where the values.yaml or a specific value can be + found at. Defaults to 'values.yaml'. + maxLength: 253 + pattern: ^[\-._a-zA-Z0-9]+$ + type: string + required: + - kind + - name + type: object + type: array + required: + - interval + type: object + x-kubernetes-validations: + - message: either chart or chartRef must be set + rule: (has(self.chart) && !has(self.chartRef)) || (!has(self.chart) + && has(self.chartRef)) + status: + default: + observedGeneration: -1 + description: HelmReleaseStatus defines the observed state of a HelmRelease. + properties: + conditions: + description: Conditions holds the conditions for the HelmRelease. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + failures: + description: |- + Failures is the reconciliation failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + helmChart: + description: |- + HelmChart is the namespaced name of the HelmChart resource created by + the controller for the HelmRelease. + type: string + history: + description: |- + History holds the history of Helm releases performed for this HelmRelease + up to the last successfully completed release. + items: + description: |- + Snapshot captures a point-in-time copy of the status information for a Helm release, + as managed by the controller. + properties: + apiVersion: + description: |- + APIVersion is the API version of the Snapshot. + Provisional: when the calculation method of the Digest field is changed, + this field will be used to distinguish between the old and new methods. + type: string + appVersion: + description: AppVersion is the chart app version of the release + object in storage. + type: string + chartName: + description: ChartName is the chart name of the release object + in storage. + type: string + chartVersion: + description: |- + ChartVersion is the chart version of the release object in + storage. + type: string + configDigest: + description: |- + ConfigDigest is the checksum of the config (better known as + "values") of the release object in storage. + It has the format of `:`. + type: string + deleted: + description: Deleted is when the release was deleted. + format: date-time + type: string + digest: + description: |- + Digest is the checksum of the release object in storage. + It has the format of `:`. + type: string + firstDeployed: + description: FirstDeployed is when the release was first deployed. + format: date-time + type: string + lastDeployed: + description: LastDeployed is when the release was last deployed. + format: date-time + type: string + name: + description: Name is the name of the release. + type: string + namespace: + description: Namespace is the namespace the release is deployed + to. + type: string + ociDigest: + description: OCIDigest is the digest of the OCI artifact associated + with the release. + type: string + status: + description: Status is the current state of the release. + type: string + testHooks: + additionalProperties: + description: |- + TestHookStatus holds the status information for a test hook as observed + to be run by the controller. + properties: + lastCompleted: + description: LastCompleted is the time the test hook last + completed. + format: date-time + type: string + lastStarted: + description: LastStarted is the time the test hook was + last started. + format: date-time + type: string + phase: + description: Phase the test hook was observed to be in. + type: string + type: object + description: |- + TestHooks is the list of test hooks for the release as observed to be + run by the controller. + type: object + version: + description: Version is the version of the release object in + storage. + type: integer + required: + - chartName + - chartVersion + - configDigest + - digest + - firstDeployed + - lastDeployed + - name + - namespace + - status + - version + type: object + type: array + installFailures: + description: |- + InstallFailures is the install failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + lastAppliedRevision: + description: |- + LastAppliedRevision is the revision of the last successfully applied + source. + Deprecated: the revision can now be found in the History. + type: string + lastAttemptedConfigDigest: + description: |- + LastAttemptedConfigDigest is the digest for the config (better known as + "values") of the last reconciliation attempt. + type: string + lastAttemptedGeneration: + description: |- + LastAttemptedGeneration is the last generation the controller attempted + to reconcile. + format: int64 + type: integer + lastAttemptedReleaseAction: + description: |- + LastAttemptedReleaseAction is the last release action performed for this + HelmRelease. It is used to determine the active remediation strategy. + enum: + - install + - upgrade + type: string + lastAttemptedRevision: + description: |- + LastAttemptedRevision is the Source revision of the last reconciliation + attempt. For OCIRepository sources, the 12 first characters of the digest are + appended to the chart version e.g. "1.2.3+1234567890ab". + type: string + lastAttemptedRevisionDigest: + description: |- + LastAttemptedRevisionDigest is the digest of the last reconciliation attempt. + This is only set for OCIRepository sources. + type: string + lastAttemptedValuesChecksum: + description: |- + LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last + reconciliation attempt. + Deprecated: Use LastAttemptedConfigDigest instead. + type: string + lastHandledForceAt: + description: |- + LastHandledForceAt holds the value of the most recent force request + value, so a change of the annotation value can be detected. + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastHandledResetAt: + description: |- + LastHandledResetAt holds the value of the most recent reset request + value, so a change of the annotation value can be detected. + type: string + lastReleaseRevision: + description: |- + LastReleaseRevision is the revision of the last successful Helm release. + Deprecated: Use History instead. + type: integer + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + observedPostRenderersDigest: + description: |- + ObservedPostRenderersDigest is the digest for the post-renderers of + the last successful reconciliation attempt. + type: string + storageNamespace: + description: |- + StorageNamespace is the namespace of the Helm release storage for the + current release. + maxLength: 63 + minLength: 1 + type: string + upgradeFailures: + description: |- + UpgradeFailures is the upgrade failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + type: object + type: object + served: true + storage: false + subresources: + status: {} + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + labels: + app.kubernetes.io/component: source-controller + name: helmrepositories.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: HelmRepository + listKind: HelmRepositoryList + plural: helmrepositories + shortNames: + - helmrepo + singular: helmrepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: HelmRepository is the Schema for the helmrepositories API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + HelmRepositorySpec specifies the required configuration to produce an + Artifact for a Helm repository index YAML. + properties: + accessFrom: + description: |- + AccessFrom specifies an Access Control List for allowing cross-namespace + references to this object. + NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + + It takes precedence over the values specified in the Secret referred + to by `.spec.secretRef`. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + insecure: + description: |- + Insecure allows connecting to a non-TLS HTTP container registry. + This field is only taken into account if the .spec.type field is set to 'oci'. + type: boolean + interval: + description: |- + Interval at which the HelmRepository URL is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + passCredentials: + description: |- + PassCredentials allows the credentials from the SecretRef to be passed + on to a host that does not match the host as defined in URL. + This may be required if the host of the advertised chart URLs in the + index differ from the defined URL. + Enabling this should be done with caution, as it can potentially result + in credentials getting stolen in a MITM-attack. + type: boolean + provider: + default: generic + description: |- + Provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + This field is optional, and only taken into account if the .spec.type field is set to 'oci'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials + for the HelmRepository. + For HTTP/S basic auth the secret must contain 'username' and 'password' + fields. + Support for TLS auth using the 'certFile' and 'keyFile', and/or 'caFile' + keys is deprecated. Please use `.spec.certSecretRef` instead. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + HelmRepository. + type: boolean + timeout: + description: |- + Timeout is used for the index fetch operation for an HTTPS helm repository, + and for remote OCI Repository operations like pulling for an OCI helm + chart by the associated HelmChart. + Its default value is 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + type: + description: |- + Type of the HelmRepository. + When this field is set to "oci", the URL field value must be prefixed with "oci://". + enum: + - default + - oci + type: string + url: + description: |- + URL of the Helm repository, a valid URL contains at least a protocol and + host. + pattern: ^(http|https|oci)://.*$ + type: string + required: + - url + type: object + status: + default: + observedGeneration: -1 + description: HelmRepositoryStatus records the observed state of the HelmRepository. + properties: + artifact: + description: Artifact represents the last successful HelmRepository + reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the HelmRepository + object. + format: int64 + type: integer + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + HelmRepositoryStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta1 HelmRepository is deprecated, upgrade to v1 + name: v1beta1 + schema: + openAPIV3Schema: + description: HelmRepository is the Schema for the helmrepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmRepositorySpec defines the reference to a Helm repository. + properties: + accessFrom: + description: AccessFrom defines an Access Control List for allowing + cross-namespace references to this object. + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + interval: + description: The interval at which to check the upstream for updates. + type: string + passCredentials: + description: |- + PassCredentials allows the credentials from the SecretRef to be passed on to + a host that does not match the host as defined in URL. + This may be required if the host of the advertised chart URLs in the index + differ from the defined URL. + Enabling this should be done with caution, as it can potentially result in + credentials getting stolen in a MITM-attack. + type: boolean + secretRef: + description: |- + The name of the secret containing authentication credentials for the Helm + repository. + For HTTP/S basic auth the secret must contain username and + password fields. + For TLS the secret must contain a certFile and keyFile, and/or + caFile fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + timeout: + default: 60s + description: The timeout of index downloading, defaults to 60s. + type: string + url: + description: The Helm repository URL, a valid URL contains at least + a protocol and host. + type: string + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: HelmRepositoryStatus defines the observed state of the HelmRepository. + properties: + artifact: + description: Artifact represents the output of the last successful + repository sync. + properties: + checksum: + description: Checksum is the SHA256 checksum of the artifact. + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of this + artifact. + format: date-time + type: string + path: + description: Path is the relative file path of this artifact. + type: string + revision: + description: |- + Revision is a human readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm index timestamp, a Helm + chart version, etc. + type: string + url: + description: URL is the HTTP address of this artifact. + type: string + required: + - path + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + url: + description: URL is the download link for the last index fetched. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 HelmRepository is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: HelmRepository is the Schema for the helmrepositories API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + HelmRepositorySpec specifies the required configuration to produce an + Artifact for a Helm repository index YAML. + properties: + accessFrom: + description: |- + AccessFrom specifies an Access Control List for allowing cross-namespace + references to this object. + NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + + It takes precedence over the values specified in the Secret referred + to by `.spec.secretRef`. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + insecure: + description: |- + Insecure allows connecting to a non-TLS HTTP container registry. + This field is only taken into account if the .spec.type field is set to 'oci'. + type: boolean + interval: + description: |- + Interval at which the HelmRepository URL is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + passCredentials: + description: |- + PassCredentials allows the credentials from the SecretRef to be passed + on to a host that does not match the host as defined in URL. + This may be required if the host of the advertised chart URLs in the + index differ from the defined URL. + Enabling this should be done with caution, as it can potentially result + in credentials getting stolen in a MITM-attack. + type: boolean + provider: + default: generic + description: |- + Provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + This field is optional, and only taken into account if the .spec.type field is set to 'oci'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials + for the HelmRepository. + For HTTP/S basic auth the secret must contain 'username' and 'password' + fields. + Support for TLS auth using the 'certFile' and 'keyFile', and/or 'caFile' + keys is deprecated. Please use `.spec.certSecretRef` instead. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + HelmRepository. + type: boolean + timeout: + description: |- + Timeout is used for the index fetch operation for an HTTPS helm repository, + and for remote OCI Repository operations like pulling for an OCI helm + chart by the associated HelmChart. + Its default value is 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + type: + description: |- + Type of the HelmRepository. + When this field is set to "oci", the URL field value must be prefixed with "oci://". + enum: + - default + - oci + type: string + url: + description: |- + URL of the Helm repository, a valid URL contains at least a protocol and + host. + pattern: ^(http|https|oci)://.*$ + type: string + required: + - url + type: object + status: + default: + observedGeneration: -1 + description: HelmRepositoryStatus records the observed state of the HelmRepository. + properties: + artifact: + description: Artifact represents the last successful HelmRepository + reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the HelmRepository + object. + format: int64 + type: integer + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + HelmRepositoryStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + labels: + app.kubernetes.io/component: source-controller + name: ocirepositories.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: OCIRepository + listKind: OCIRepositoryList + plural: ocirepositories + shortNames: + - ocirepo + singular: ocirepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta2 + schema: + openAPIV3Schema: + description: OCIRepository is the Schema for the ocirepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: OCIRepositorySpec defines the desired state of OCIRepository + properties: + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + + Note: Support for the `caFile`, `certFile` and `keyFile` keys have + been deprecated. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP container + registry. + type: boolean + interval: + description: |- + Interval at which the OCIRepository URL is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + layerSelector: + description: |- + LayerSelector specifies which layer should be extracted from the OCI artifact. + When not specified, the first layer found in the artifact is selected. + properties: + mediaType: + description: |- + MediaType specifies the OCI media type of the layer + which should be extracted from the OCI Artifact. The + first layer matching this type is selected. + type: string + operation: + description: |- + Operation specifies how the selected layer should be processed. + By default, the layer compressed content is extracted to storage. + When the operation is set to 'copy', the layer compressed content + is persisted to storage as it is. + enum: + - extract + - copy + type: string + type: object + provider: + default: generic + description: |- + The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + ref: + description: |- + The OCI reference to pull and monitor for changes, + defaults to the latest tag. + properties: + digest: + description: |- + Digest is the image digest to pull, takes precedence over SemVer. + The value should be in the format 'sha256:'. + type: string + semver: + description: |- + SemVer is the range of tags to pull selecting the latest within + the range, takes precedence over Tag. + type: string + semverFilter: + description: SemverFilter is a regex pattern to filter the tags + within the SemVer range. + type: string + tag: + description: Tag is the image tag to pull, defaults to latest. + type: string + type: object + secretRef: + description: |- + SecretRef contains the secret name containing the registry login + credentials to resolve image metadata. + The secret must be of type kubernetes.io/dockerconfigjson. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate + the image pull if the service account has attached pull secrets. For more information: + https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account + type: string + suspend: + description: This flag tells the controller to suspend the reconciliation + of this source. + type: boolean + timeout: + default: 60s + description: The timeout for remote OCI Repository operations like + pulling, defaults to 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + url: + description: |- + URL is a reference to an OCI artifact repository hosted + on a remote container registry. + pattern: ^oci://.*$ + type: string + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + properties: + matchOIDCIdentity: + description: |- + MatchOIDCIdentity specifies the identity matching criteria to use + while verifying an OCI artifact which was signed using Cosign keyless + signing. The artifact's identity is deemed to be verified if any of the + specified matchers match against the identity. + items: + description: |- + OIDCIdentityMatch specifies options for verifying the certificate identity, + i.e. the issuer and the subject of the certificate. + properties: + issuer: + description: |- + Issuer specifies the regex pattern to match against to verify + the OIDC issuer in the Fulcio certificate. The pattern must be a + valid Go regular expression. + type: string + subject: + description: |- + Subject specifies the regex pattern to match against to verify + the identity subject in the Fulcio certificate. The pattern must + be a valid Go regular expression. + type: string + required: + - issuer + - subject + type: object + type: array + provider: + default: cosign + description: Provider specifies the technology used to sign the + OCI Artifact. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: OCIRepositoryStatus defines the observed state of OCIRepository + properties: + artifact: + description: Artifact represents the output of the last successful + OCI Repository sync. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the OCIRepository. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + contentConfigChecksum: + description: |- + ContentConfigChecksum is a checksum of all the configurations related to + the content of the source artifact: + - .spec.ignore + - .spec.layerSelector + observed in .status.observedGeneration version of the object. This can + be used to determine if the content configuration has changed and the + artifact needs to be rebuilt. + It has the format of `:`, for example: `sha256:`. + + + Deprecated: Replaced with explicit fields for observed artifact content + config in the status. + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + observedLayerSelector: + description: |- + ObservedLayerSelector is the observed layer selector used for constructing + the source artifact. + properties: + mediaType: + description: |- + MediaType specifies the OCI media type of the layer + which should be extracted from the OCI Artifact. The + first layer matching this type is selected. + type: string + operation: + description: |- + Operation specifies how the selected layer should be processed. + By default, the layer compressed content is extracted to storage. + When the operation is set to 'copy', the layer compressed content + is persisted to storage as it is. + enum: + - extract + - copy + type: string + type: object + url: + description: URL is the download link for the artifact output of the + last OCI Repository sync. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: packageinfos.packages.glasskube.dev +spec: + group: packages.glasskube.dev + names: + kind: PackageInfo + listKind: PackageInfoList + plural: packageinfos + shortNames: + - pkgi + singular: packageinfo + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.version + name: Desired version + type: string + - jsonPath: .status.version + name: Current version + type: string + - jsonPath: .status.lastUpdateTimestamp + name: Last Updated + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: PackageInfo is the Schema for the packageinfos API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PackageInfoSpec defines the desired state of PackageInfo + properties: + name: + type: string + repositoryUrl: + type: string + version: + type: string + required: + - name + type: object + status: + description: PackageInfoStatus defines the observed state of PackageInfo + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastUpdateTimestamp: + format: date-time + type: string + manifest: + properties: + components: + items: + properties: + installedName: + type: string + name: + type: string + values: + additionalProperties: + properties: + value: + type: string + type: object + description: Specify configuration for this component + type: object + version: + type: string + required: + - name + type: object + type: array + defaultNamespace: + description: DefaultNamespace to install the package. May be overridden. + type: string + dependencies: + items: + properties: + name: + type: string + version: + type: string + required: + - name + type: object + type: array + entrypoints: + items: + description: PackageEntrypoint defines a service port a user + may use to access the package + properties: + localPort: + description: LocalPort to use for port mapping + format: int32 + type: integer + name: + description: |- + Name of this entrypoint. Used for "glasskube open [package-name] [entrypoint-name]" if more + than one entrypoint exists. Optional if the package only has one entrypoint. + type: string + port: + description: Port of the service to bind to + format: int32 + type: integer + scheme: + type: string + serviceName: + description: ServiceName is the name of a service that is + part of + type: string + required: + - port + - serviceName + type: object + type: array + helm: + description: Helm instructs the controller to create a helm release + when installing this package. + properties: + chartName: + description: ChartName is the name of the chart that represents + this package. + type: string + chartVersion: + description: ChartVersion of the chart that should be installed. + type: string + repositoryUrl: + description: |- + RepositoryUrl is the remote URL of the helm repository. This is the same URL you would use + if you use "helm repo add ...". + type: string + values: + description: Values that should be used for the helm release + x-kubernetes-preserve-unknown-fields: true + required: + - chartName + - chartVersion + - repositoryUrl + type: object + iconUrl: + type: string + kustomize: + description: Kustomize instructs the controller to apply a kustomization + when installing this package [PLACEHOLDER]. + type: object + longDescription: + type: string + manifests: + items: + properties: + defaultNamespace: + description: |- + DefaultNamespace, if set to a non-empty string, is used for resources that are of a namespaced + kind and do not have a namespace set. + If at least one such a resource exists, the namespace is created implicitly. + type: string + url: + description: |- + Url is the location of the manifest. + Typically, this should be a full https URL, but local paths are also supporeted. + If this field is set to a local path (e.g. a relative path like "./manifest.yaml" or just "manifest.yaml") it + will be resolved relative to the packages "package.yaml" file. + type: string + required: + - url + type: object + type: array + name: + type: string + references: + items: + properties: + label: + type: string + url: + type: string + required: + - label + - url + type: object + type: array + scope: + description: Scope is optional (default is Cluster) + enum: + - Cluster + - Namespaced + type: string + shortDescription: + type: string + transformations: + items: + properties: + source: + properties: + path: + type: string + resource: + description: |- + TypedLocalObjectReference contains enough information to let you locate the + typed referenced object inside the same namespace. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + targets: + items: + properties: + chartName: + type: string + patch: + properties: + op: + type: string + path: + type: string + required: + - op + - path + type: object + resource: + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string + required: + - kind + - name + type: object + valueTemplate: + type: string + required: + - patch + type: object + x-kubernetes-validations: + - message: ValueDefinitionTarget must have either resource + or chartName but not both + rule: has(self.resource) != has(self.chartName) + type: array + required: + - source + - targets + type: object + type: array + transitiveResources: + items: + description: |- + TypedLocalObjectReference contains enough information to let you locate the + typed referenced object inside the same namespace. + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + type: array + valueDefinitions: + additionalProperties: + properties: + constraints: + properties: + max: + type: integer + maxLength: + type: integer + min: + type: integer + minLength: + type: integer + pattern: + type: string + required: + type: boolean + type: object + defaultValue: + type: string + metadata: + properties: + description: + type: string + hints: + items: + type: string + type: array + label: + type: string + type: object + options: + items: + type: string + type: array + targets: + items: + properties: + chartName: + type: string + patch: + properties: + op: + type: string + path: + type: string + required: + - op + - path + type: object + resource: + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: |- + Namespace is the namespace of resource being referenced + Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled. + type: string + required: + - kind + - name + type: object + valueTemplate: + type: string + required: + - patch + type: object + x-kubernetes-validations: + - message: ValueDefinitionTarget must have either resource + or chartName but not both + rule: has(self.resource) != has(self.chartName) + type: array + type: + enum: + - boolean + - text + - number + - options + type: string + required: + - targets + - type + type: object + type: object + required: + - name + type: object + resolvedUrl: + type: string + version: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: packagerepositories.packages.glasskube.dev +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: glasskube-webhook-service + namespace: glasskube-system + path: /convert + conversionReviewVersions: + - v1 + group: packages.glasskube.dev + names: + kind: PackageRepository + listKind: PackageRepositoryList + plural: packagerepositories + singular: packagerepository + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: PackageRepository is the Schema for the packagerepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PackageRepositorySpec defines the desired state of PackageRepository + properties: + auth: + properties: + basic: + properties: + password: + type: string + passwordSecretRef: + description: SecretKeySelector selects a key of a Secret. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + username: + type: string + usernameSecretRef: + description: SecretKeySelector selects a key of a Secret. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearer: + properties: + token: + type: string + tokenSecretRef: + description: SecretKeySelector selects a key of a Secret. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + type: object + url: + type: string + required: + - url + type: object + status: + description: PackageRepositoryStatus defines the observed state of PackageRepository + properties: + conditions: + description: |- + INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + Important: Run "make" to regenerate code after modifying this file + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.1 + name: packages.packages.glasskube.dev +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: glasskube-webhook-service + namespace: glasskube-system + path: /convert + conversionReviewVersions: + - v1 + group: packages.glasskube.dev + names: + kind: Package + listKind: PackageList + plural: packages + shortNames: + - pkg + singular: package + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.packageInfo.version + name: Desired version + type: string + - jsonPath: .status.version + name: Installed version + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Package is the Schema for the packages API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PackageSpec defines the desired state + properties: + packageInfo: + properties: + name: + description: Name of the package to install + type: string + repositoryName: + description: RepositoryName is the name of the repository to pull + the package from (optional) + type: string + version: + description: Version of the package to install + type: string + required: + - name + - version + type: object + values: + additionalProperties: + maxProperties: 1 + minProperties: 1 + properties: + value: + type: string + valueFrom: + maxProperties: 1 + minProperties: 1 + properties: + configMapRef: + properties: + key: + type: string + name: + type: string + namespace: + type: string + required: + - key + - name + type: object + packageRef: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + secretRef: + properties: + key: + type: string + name: + type: string + namespace: + type: string + required: + - key + - name + type: object + type: object + type: object + type: object + required: + - packageInfo + type: object + status: + description: PackageStatus defines the observed state + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + ownedPackageInfos: + items: + properties: + group: + type: string + kind: + type: string + markedForDeletion: + type: boolean + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - version + type: object + type: array + ownedPackages: + items: + properties: + group: + type: string + kind: + type: string + markedForDeletion: + type: boolean + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - version + type: object + type: array + ownedResources: + items: + properties: + group: + type: string + kind: + type: string + markedForDeletion: + type: boolean + name: + type: string + namespace: + type: string + version: + type: string + required: + - group + - kind + - name + - version + type: object + type: array + version: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: helm-controller + name: helm-controller + namespace: flux-system + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: source-controller + name: source-controller + namespace: flux-system + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: controller-manager-sa + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: serviceaccount + app.kubernetes.io/part-of: glasskube + name: glasskube-controller-manager + namespace: glasskube-system + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: controller-manager-sa + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: serviceaccount + app.kubernetes.io/part-of: glasskube + name: glasskube-webhook-cert + namespace: glasskube-system + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: leader-election-role + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: role + app.kubernetes.io/part-of: glasskube + name: glasskube-leader-election-role + namespace: glasskube-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: glasskube-webhook-cert-role + namespace: glasskube-system +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - patch + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: crd-controller +rules: +- apiGroups: + - source.toolkit.fluxcd.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - kustomize.toolkit.fluxcd.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - helm.toolkit.fluxcd.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - notification.toolkit.fluxcd.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - image.toolkit.fluxcd.io + resources: + - '*' + verbs: + - '*' +- apiGroups: + - "" + resources: + - namespaces + - secrets + - configmaps + - serviceaccounts + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- nonResourceURLs: + - /livez/ping + verbs: + - head + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + rbac.authorization.k8s.io/aggregate-to-admin: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + name: flux-edit +rules: +- apiGroups: + - notification.toolkit.fluxcd.io + - source.toolkit.fluxcd.io + - helm.toolkit.fluxcd.io + - image.toolkit.fluxcd.io + - kustomize.toolkit.fluxcd.io + resources: + - '*' + verbs: + - create + - delete + - deletecollection + - patch + - update + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + rbac.authorization.k8s.io/aggregate-to-admin: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-view: "true" + name: flux-view +rules: +- apiGroups: + - notification.toolkit.fluxcd.io + - source.toolkit.fluxcd.io + - helm.toolkit.fluxcd.io + - image.toolkit.fluxcd.io + - kustomize.toolkit.fluxcd.io + resources: + - '*' + verbs: + - get + - list + - watch + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: glasskube + name: glasskube-clusterpackage-editor-role +rules: +- apiGroups: + - packages.glasskube.dev + resources: + - clusterpackages + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - packages.glasskube.dev + resources: + - clusterpackages/status + verbs: + - get + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: glasskube + name: glasskube-clusterpackage-viewer-role +rules: +- apiGroups: + - packages.glasskube.dev + resources: + - clusterpackages + verbs: + - get + - list + - watch +- apiGroups: + - packages.glasskube.dev + resources: + - clusterpackages/status + verbs: + - get + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: metrics-reader + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: clusterrole + app.kubernetes.io/part-of: glasskube + name: glasskube-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: glasskube + name: glasskube-packagerepository-editor-role +rules: +- apiGroups: + - packages.glasskube.dev + resources: + - packagerepositories + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - packages.glasskube.dev + resources: + - packagerepositories/status + verbs: + - get + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: glasskube + name: glasskube-packagerepository-viewer-role +rules: +- apiGroups: + - packages.glasskube.dev + resources: + - packagerepositories + verbs: + - get + - list + - watch +- apiGroups: + - packages.glasskube.dev + resources: + - packagerepositories/status + verbs: + - get + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: proxy-role + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: clusterrole + app.kubernetes.io/part-of: glasskube + name: glasskube-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: glasskube-webhook-cert-role +rules: +- apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + verbs: + - patch + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: leader-election-rolebinding + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: rolebinding + app.kubernetes.io/part-of: glasskube + name: glasskube-leader-election-rolebinding + namespace: glasskube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: glasskube-leader-election-role +subjects: +- kind: ServiceAccount + name: glasskube-controller-manager + namespace: glasskube-system + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: glasskube-webhook-cert-rolebinding + namespace: glasskube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: glasskube-webhook-cert-role +subjects: +- kind: ServiceAccount + name: glasskube-webhook-cert + namespace: glasskube-system + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cluster-reconciler +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: kustomize-controller + namespace: flux-system +- kind: ServiceAccount + name: helm-controller + namespace: flux-system + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: crd-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: crd-controller +subjects: +- kind: ServiceAccount + name: kustomize-controller + namespace: flux-system +- kind: ServiceAccount + name: helm-controller + namespace: flux-system +- kind: ServiceAccount + name: source-controller + namespace: flux-system +- kind: ServiceAccount + name: notification-controller + namespace: flux-system +- kind: ServiceAccount + name: image-reflector-controller + namespace: flux-system +- kind: ServiceAccount + name: image-automation-controller + namespace: flux-system + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: manager-rolebinding + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/part-of: glasskube + name: glasskube-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: glasskube-controller-manager + namespace: glasskube-system + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: proxy-rolebinding + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/part-of: glasskube + name: glasskube-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: glasskube-proxy-role +subjects: +- kind: ServiceAccount + name: glasskube-controller-manager + namespace: glasskube-system + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: glasskube-webhook-cert-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: glasskube-webhook-cert-role +subjects: +- kind: ServiceAccount + name: glasskube-webhook-cert + namespace: glasskube-system + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: source-controller + control-plane: controller + name: source-controller + namespace: flux-system +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: http + selector: + app: source-controller + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: controller-manager-metrics-service + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: service + app.kubernetes.io/part-of: glasskube + control-plane: controller-manager + name: glasskube-controller-manager-metrics-service + namespace: glasskube-system +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: https + selector: + control-plane: controller-manager + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: webhook-service + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: service + app.kubernetes.io/part-of: glasskube + name: glasskube-webhook-service + namespace: glasskube-system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: helm-controller + control-plane: controller + name: helm-controller + namespace: flux-system +spec: + replicas: 1 + selector: + matchLabels: + app: helm-controller + template: + metadata: + annotations: + prometheus.io/port: "8080" + prometheus.io/scrape: "true" + labels: + app: helm-controller + spec: + containers: + - args: + - --events-addr=http://notification-controller/ + - --watch-all-namespaces + - --log-level=info + - --log-encoding=json + - --enable-leader-election + env: + - name: RUNTIME_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: GOMAXPROCS + valueFrom: + resourceFieldRef: + containerName: manager + resource: limits.cpu + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + containerName: manager + resource: limits.memory + image: fluxcd/helm-controller:v1.0.1 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: healthz + name: manager + ports: + - containerPort: 8080 + name: http-prom + protocol: TCP + - containerPort: 9440 + name: healthz + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: healthz + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 100m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /tmp + name: temp + priorityClassName: system-cluster-critical + securityContext: + fsGroup: 1337 + serviceAccountName: helm-controller + terminationGracePeriodSeconds: 600 + volumes: + - emptyDir: {} + name: temp + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: source-controller + control-plane: controller + name: source-controller + namespace: flux-system +spec: + replicas: 1 + selector: + matchLabels: + app: source-controller + strategy: + type: Recreate + template: + metadata: + annotations: + prometheus.io/port: "8080" + prometheus.io/scrape: "true" + labels: + app: source-controller + spec: + containers: + - args: + - --events-addr=http://notification-controller/ + - --watch-all-namespaces + - --log-level=info + - --log-encoding=json + - --enable-leader-election + - --storage-path=/data + - --storage-adv-addr=source-controller + env: + - name: RUNTIME_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: TUF_ROOT + value: /tmp/.sigstore + - name: GOMAXPROCS + valueFrom: + resourceFieldRef: + containerName: manager + resource: limits.cpu + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + containerName: manager + resource: limits.memory + image: fluxcd/source-controller:v1.3.0 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: healthz + name: manager + ports: + - containerPort: 9090 + name: http + protocol: TCP + - containerPort: 8080 + name: http-prom + protocol: TCP + - containerPort: 9440 + name: healthz + protocol: TCP + readinessProbe: + httpGet: + path: / + port: http + resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 50m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /data + name: data + - mountPath: /tmp + name: tmp + priorityClassName: system-cluster-critical + securityContext: + fsGroup: 1337 + serviceAccountName: source-controller + terminationGracePeriodSeconds: 10 + volumes: + - emptyDir: {} + name: data + - emptyDir: {} + name: tmp + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: glasskube + app.kubernetes.io/instance: controller-manager + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: deployment + app.kubernetes.io/part-of: glasskube + control-plane: controller-manager + name: glasskube-controller-manager + namespace: glasskube-system +spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /package-operator + image: ghcr.io/glasskube/package-operator:v0.23.0 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.15.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + securityContext: + runAsNonRoot: true + serviceAccountName: glasskube-controller-manager + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + secretName: glasskube-webhook-tls + +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: glasskube-webhook-cert + namespace: glasskube-system +spec: + jobTemplate: + spec: + template: + spec: + containers: + - command: + - /cert-manager + image: ghcr.io/glasskube/package-operator:v0.23.0 + name: manager + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + restartPolicy: OnFailure + serviceAccountName: glasskube-webhook-cert + terminationGracePeriodSeconds: 3 + schedule: 0 0 1 * * + +--- +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + argocd.argoproj.io/sync-options: Force=true,Replace=true + name: glasskube-webhook-cert-init + namespace: glasskube-system +spec: + template: + spec: + containers: + - command: + - /cert-manager + image: ghcr.io/glasskube/package-operator:v0.23.0 + name: manager + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + restartPolicy: OnFailure + serviceAccountName: glasskube-webhook-cert + +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-egress + namespace: flux-system +spec: + egress: + - {} + ingress: + - from: + - podSelector: {} + podSelector: {} + policyTypes: + - Ingress + - Egress + +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-scraping + namespace: flux-system +spec: + ingress: + - from: + - namespaceSelector: {} + ports: + - port: 8080 + protocol: TCP + podSelector: {} + policyTypes: + - Ingress + +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-webhooks + namespace: flux-system +spec: + ingress: + - from: + - namespaceSelector: {} + podSelector: + matchLabels: + app: notification-controller + policyTypes: + - Ingress + +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: glasskube-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: glasskube-webhook-service + namespace: glasskube-system + path: /validate-packages-glasskube-dev-v1alpha1-clusterpackage + failurePolicy: Fail + name: vclusterpackage.kb.io + rules: + - apiGroups: + - packages.glasskube.dev + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - clusterpackages + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: glasskube-webhook-service + namespace: glasskube-system + path: /validate-packages-glasskube-dev-v1alpha1-package + failurePolicy: Fail + name: vpackage.kb.io + rules: + - apiGroups: + - packages.glasskube.dev + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + - DELETE + resources: + - packages + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: glasskube-webhook-service + namespace: glasskube-system + path: /validate-packages-glasskube-dev-v1alpha1-packagerepository + failurePolicy: Fail + name: vpackagerepository.kb.io + rules: + - apiGroups: + - packages.glasskube.dev + apiVersions: + - v1alpha1 + operations: + - UPDATE + - DELETE + resources: + - packagerepositories + sideEffects: None + +--- +apiVersion: packages.glasskube.dev/v1alpha1 +kind: PackageRepository +metadata: + annotations: + packages.glasskube.dev/default-repository: "true" + name: glasskube +spec: + url: https://packages.dl.glasskube.dev/packages +status: {} + From 92ce2b0c507954bba0a71565803bb8741a862f02 Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 15:22:50 -0700 Subject: [PATCH 024/159] feat: add packages kustomization Signed-off-by: Philip Miglinci --- glasskube/argocd/glasskube/applicationset.yaml | 3 +++ glasskube/packages/kustomization.yaml | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 glasskube/packages/kustomization.yaml diff --git a/glasskube/argocd/glasskube/applicationset.yaml b/glasskube/argocd/glasskube/applicationset.yaml index 15560f19eb..6a6de25bfc 100644 --- a/glasskube/argocd/glasskube/applicationset.yaml +++ b/glasskube/argocd/glasskube/applicationset.yaml @@ -21,6 +21,9 @@ spec: repoURL: https://github.com/pmig/trieve targetRevision: pmig/gcp-hn-migration path: '{{.path.path}}' + kustomize: + components: + - ./ destination: server: https://kubernetes.default.svc syncPolicy: diff --git a/glasskube/packages/kustomization.yaml b/glasskube/packages/kustomization.yaml new file mode 100644 index 0000000000..a480bebc7d --- /dev/null +++ b/glasskube/packages/kustomization.yaml @@ -0,0 +1,5 @@ +namespace: trieve +resources: + - ../dependencies/redis.yaml + - ../dependencies/tika.yaml + From 46ee59bfcf531e50e28c88267095c4c50f457dfc Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 15:28:18 -0700 Subject: [PATCH 025/159] feat: switch glasskube argocd deployment to main branch Signed-off-by: Philip Miglinci --- glasskube/argocd/glasskube/applicationset.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glasskube/argocd/glasskube/applicationset.yaml b/glasskube/argocd/glasskube/applicationset.yaml index 6a6de25bfc..19578f4340 100644 --- a/glasskube/argocd/glasskube/applicationset.yaml +++ b/glasskube/argocd/glasskube/applicationset.yaml @@ -9,7 +9,7 @@ spec: generators: - git: repoURL: https://github.com/pmig/trieve - revision: pmig/gcp-hn-migration + revision: HEAD directories: - path: 'glasskube/packages/*' template: @@ -19,7 +19,7 @@ spec: project: default source: repoURL: https://github.com/pmig/trieve - targetRevision: pmig/gcp-hn-migration + targetRevision: HEAD path: '{{.path.path}}' kustomize: components: From 2733380f60170afbc009454c9e2825ecf404ef3f Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 15:35:00 -0700 Subject: [PATCH 026/159] feat: use tmp deployment branch Signed-off-by: Philip Miglinci --- glasskube/argocd/glasskube-application.yaml | 2 +- glasskube/argocd/glasskube/applicationset.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/glasskube/argocd/glasskube-application.yaml b/glasskube/argocd/glasskube-application.yaml index a502b5f80d..eaea6ed5a9 100644 --- a/glasskube/argocd/glasskube-application.yaml +++ b/glasskube/argocd/glasskube-application.yaml @@ -10,7 +10,7 @@ spec: source: path: glasskube/argocd/glasskube repoURL: https://github.com/pmig/trieve - targetRevision: pmig/gcp-hn-migration + targetRevision: pmig/gcp-trieve syncPolicy: syncOptions: - ApplyOutOfSyncOnly=true diff --git a/glasskube/argocd/glasskube/applicationset.yaml b/glasskube/argocd/glasskube/applicationset.yaml index 19578f4340..dd5bd8b347 100644 --- a/glasskube/argocd/glasskube/applicationset.yaml +++ b/glasskube/argocd/glasskube/applicationset.yaml @@ -9,7 +9,7 @@ spec: generators: - git: repoURL: https://github.com/pmig/trieve - revision: HEAD + revision: pmig/gcp-trieve directories: - path: 'glasskube/packages/*' template: @@ -19,7 +19,7 @@ spec: project: default source: repoURL: https://github.com/pmig/trieve - targetRevision: HEAD + targetRevision: pmig/gcp-trieve path: '{{.path.path}}' kustomize: components: From 22175f06ebf8e826567f4ff769afc27a9bd59a39 Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 15:42:52 -0700 Subject: [PATCH 027/159] feat: remove kustomization Signed-off-by: Philip Miglinci --- glasskube/argocd/glasskube/applicationset.yaml | 3 --- glasskube/packages/tika.yaml | 10 ++++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 glasskube/packages/tika.yaml diff --git a/glasskube/argocd/glasskube/applicationset.yaml b/glasskube/argocd/glasskube/applicationset.yaml index dd5bd8b347..2cf452869e 100644 --- a/glasskube/argocd/glasskube/applicationset.yaml +++ b/glasskube/argocd/glasskube/applicationset.yaml @@ -21,9 +21,6 @@ spec: repoURL: https://github.com/pmig/trieve targetRevision: pmig/gcp-trieve path: '{{.path.path}}' - kustomize: - components: - - ./ destination: server: https://kubernetes.default.svc syncPolicy: diff --git a/glasskube/packages/tika.yaml b/glasskube/packages/tika.yaml new file mode 100644 index 0000000000..1bdc4172bc --- /dev/null +++ b/glasskube/packages/tika.yaml @@ -0,0 +1,10 @@ +apiVersion: packages.glasskube.dev/v1alpha1 +kind: Package +metadata: + name: trieve-tika + namespace: trieve +spec: + packageInfo: + name: tika + repositoryName: glasskube + version: v2.9.2+2 \ No newline at end of file From 596740f767f123ad3f29f973cb3404518119eb0f Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 15:46:55 -0700 Subject: [PATCH 028/159] feat: move package into folder Signed-off-by: Philip Miglinci --- glasskube/packages/{ => tika}/tika.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename glasskube/packages/{ => tika}/tika.yaml (100%) diff --git a/glasskube/packages/tika.yaml b/glasskube/packages/tika/tika.yaml similarity index 100% rename from glasskube/packages/tika.yaml rename to glasskube/packages/tika/tika.yaml From 0e29e5a308863a0677a878548d54cc8348a9bbf4 Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 15:59:05 -0700 Subject: [PATCH 029/159] feat: delete applicationset Signed-off-by: Philip Miglinci --- glasskube/argocd/dependency-application.yaml | 21 ++++++++++++++ .../argocd/glasskube/applicationset.yaml | 29 ------------------- 2 files changed, 21 insertions(+), 29 deletions(-) create mode 100644 glasskube/argocd/dependency-application.yaml delete mode 100644 glasskube/argocd/glasskube/applicationset.yaml diff --git a/glasskube/argocd/dependency-application.yaml b/glasskube/argocd/dependency-application.yaml new file mode 100644 index 0000000000..d98fcb6959 --- /dev/null +++ b/glasskube/argocd/dependency-application.yaml @@ -0,0 +1,21 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: glasskube-dependencies + namespace: argocd +spec: + destination: + server: https://kubernetes.default.svc + project: default + source: + path: glasskube/dependencies + repoURL: https://github.com/pmig/trieve + targetRevision: pmig/gcp-trieve + kustomize: + namespace: trieve + syncPolicy: + syncOptions: + - ApplyOutOfSyncOnly=true + automated: + prune: true + selfHeal: true diff --git a/glasskube/argocd/glasskube/applicationset.yaml b/glasskube/argocd/glasskube/applicationset.yaml deleted file mode 100644 index 2cf452869e..0000000000 --- a/glasskube/argocd/glasskube/applicationset.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: ApplicationSet -metadata: - name: applications - namespace: argocd -spec: - goTemplate: true - goTemplateOptions: ["missingkey=error"] - generators: - - git: - repoURL: https://github.com/pmig/trieve - revision: pmig/gcp-trieve - directories: - - path: 'glasskube/packages/*' - template: - metadata: - name: '{{ .path.basename }}' - spec: - project: default - source: - repoURL: https://github.com/pmig/trieve - targetRevision: pmig/gcp-trieve - path: '{{.path.path}}' - destination: - server: https://kubernetes.default.svc - syncPolicy: - automated: - prune: true - selfHeal: true From 9b5620eaba90847c95b0f933160eca6275bdd08f Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 18:52:54 -0700 Subject: [PATCH 030/159] feat: add trieve hackernews application Signed-off-by: Philip Miglinci --- ...aml => trieve-hackernews-application.yaml} | 9 ++-- .../argocd/trieve-hackernews/application.yaml | 41 +++++++++++++++++++ glasskube/packages/kustomization.yaml | 5 --- glasskube/packages/tika/tika.yaml | 10 ----- .../components/gpu/deployment-patch.yaml | 9 ++++ .../components/gpu/kustomization.yaml | 9 ++++ glasskube/trieve-gcp/kustomization.yaml | 5 +++ 7 files changed, 69 insertions(+), 19 deletions(-) rename glasskube/argocd/{dependency-application.yaml => trieve-hackernews-application.yaml} (69%) create mode 100644 glasskube/argocd/trieve-hackernews/application.yaml delete mode 100644 glasskube/packages/kustomization.yaml delete mode 100644 glasskube/packages/tika/tika.yaml create mode 100644 glasskube/trieve-gcp/embeddings/components/gpu/deployment-patch.yaml create mode 100644 glasskube/trieve-gcp/embeddings/components/gpu/kustomization.yaml create mode 100644 glasskube/trieve-gcp/kustomization.yaml diff --git a/glasskube/argocd/dependency-application.yaml b/glasskube/argocd/trieve-hackernews-application.yaml similarity index 69% rename from glasskube/argocd/dependency-application.yaml rename to glasskube/argocd/trieve-hackernews-application.yaml index d98fcb6959..2960744ed5 100644 --- a/glasskube/argocd/dependency-application.yaml +++ b/glasskube/argocd/trieve-hackernews-application.yaml @@ -1,18 +1,19 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: - name: glasskube-dependencies + name: trieve-hackernews namespace: argocd spec: destination: server: https://kubernetes.default.svc project: default source: - path: glasskube/dependencies - repoURL: https://github.com/pmig/trieve - targetRevision: pmig/gcp-trieve + path: glasskube/trieve-gcp + repoURL: https://github.com/pmig/trieve-gcp + targetRevision: HEAD kustomize: namespace: trieve + syncPolicy: syncOptions: - ApplyOutOfSyncOnly=true diff --git a/glasskube/argocd/trieve-hackernews/application.yaml b/glasskube/argocd/trieve-hackernews/application.yaml new file mode 100644 index 0000000000..6516427332 --- /dev/null +++ b/glasskube/argocd/trieve-hackernews/application.yaml @@ -0,0 +1,41 @@ +apiVersion: packages.glasskube.dev/v1alpha1 +kind: Package +metadata: + name: hackernews + namespace: trieve +spec: + packageInfo: + name: trieve-gcp + repositoryName: trieve + version: v0.11.8+3 + values: + cpuMode: + value: 'false' + domain: + value: trieve-hackernews.glasskube.com + keyForAnthropicApi: + value: ' ' + keyForJinaCodeApi: + value: ' ' + keyForLlmApi: + value: ' ' + keyForOpenaiApi: + value: ' ' + s3AccessKey: + value: '' + s3AwsRegion: + value: '' + s3BucketName: + value: '' + s3Endpoint: + value: '' + s3SecretKey: + value: '' + smtpEmailAddress: + value: ' ' + smtpServerUrl: + value: ' ' + smtpUsername: + value: ' ' + smtpUsernamePassword: + value: ' ' \ No newline at end of file diff --git a/glasskube/packages/kustomization.yaml b/glasskube/packages/kustomization.yaml deleted file mode 100644 index a480bebc7d..0000000000 --- a/glasskube/packages/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -namespace: trieve -resources: - - ../dependencies/redis.yaml - - ../dependencies/tika.yaml - diff --git a/glasskube/packages/tika/tika.yaml b/glasskube/packages/tika/tika.yaml deleted file mode 100644 index 1bdc4172bc..0000000000 --- a/glasskube/packages/tika/tika.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: packages.glasskube.dev/v1alpha1 -kind: Package -metadata: - name: trieve-tika - namespace: trieve -spec: - packageInfo: - name: tika - repositoryName: glasskube - version: v2.9.2+2 \ No newline at end of file diff --git a/glasskube/trieve-gcp/embeddings/components/gpu/deployment-patch.yaml b/glasskube/trieve-gcp/embeddings/components/gpu/deployment-patch.yaml new file mode 100644 index 0000000000..f90e758d1c --- /dev/null +++ b/glasskube/trieve-gcp/embeddings/components/gpu/deployment-patch.yaml @@ -0,0 +1,9 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: not-used +spec: + template: + spec: + nodeSelector: + cloud.google.com/gke-accelerator: nvidia-l4 diff --git a/glasskube/trieve-gcp/embeddings/components/gpu/kustomization.yaml b/glasskube/trieve-gcp/embeddings/components/gpu/kustomization.yaml new file mode 100644 index 0000000000..84e58e84d8 --- /dev/null +++ b/glasskube/trieve-gcp/embeddings/components/gpu/kustomization.yaml @@ -0,0 +1,9 @@ +kind: Component +images: + - name: ghcr.io/huggingface/text-embeddings-inference + newTag: 89-1.2 +patches: + - target: + kind: Deployment + labelSelector: "trieve.ai/embedding" + path: deployment-patch.yaml diff --git a/glasskube/trieve-gcp/kustomization.yaml b/glasskube/trieve-gcp/kustomization.yaml new file mode 100644 index 0000000000..ede37c1fe8 --- /dev/null +++ b/glasskube/trieve-gcp/kustomization.yaml @@ -0,0 +1,5 @@ +resources: + - ../trieve +components: + - ../trieve/embeddings/components/gpu + - ./embeddings/components/gpu From 8c0187d17281a223f0c77115e3ab62e037cffaca Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 18:53:44 -0700 Subject: [PATCH 031/159] feat: move to HEAD Signed-off-by: Philip Miglinci --- glasskube/argocd/glasskube-application.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glasskube/argocd/glasskube-application.yaml b/glasskube/argocd/glasskube-application.yaml index eaea6ed5a9..512fc0fcdb 100644 --- a/glasskube/argocd/glasskube-application.yaml +++ b/glasskube/argocd/glasskube-application.yaml @@ -10,7 +10,7 @@ spec: source: path: glasskube/argocd/glasskube repoURL: https://github.com/pmig/trieve - targetRevision: pmig/gcp-trieve + targetRevision: HEAD syncPolicy: syncOptions: - ApplyOutOfSyncOnly=true From 5b0fd1325be47e0988a73b67b73b0ac2de32295f Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 19:45:17 -0700 Subject: [PATCH 032/159] feat: set argocd source to pmig/trieve HEAD Signed-off-by: Philip Miglinci --- glasskube/argocd/trieve-hackernews-application.yaml | 5 ++--- glasskube/argocd/trieve-hackernews/application.yaml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/glasskube/argocd/trieve-hackernews-application.yaml b/glasskube/argocd/trieve-hackernews-application.yaml index 2960744ed5..13af594ff1 100644 --- a/glasskube/argocd/trieve-hackernews-application.yaml +++ b/glasskube/argocd/trieve-hackernews-application.yaml @@ -8,12 +8,11 @@ spec: server: https://kubernetes.default.svc project: default source: - path: glasskube/trieve-gcp - repoURL: https://github.com/pmig/trieve-gcp + path: glasskube/argocd/trieve-hackernews + repoURL: https://github.com/pmig/trieve targetRevision: HEAD kustomize: namespace: trieve - syncPolicy: syncOptions: - ApplyOutOfSyncOnly=true diff --git a/glasskube/argocd/trieve-hackernews/application.yaml b/glasskube/argocd/trieve-hackernews/application.yaml index 6516427332..3a70a0639f 100644 --- a/glasskube/argocd/trieve-hackernews/application.yaml +++ b/glasskube/argocd/trieve-hackernews/application.yaml @@ -12,7 +12,7 @@ spec: cpuMode: value: 'false' domain: - value: trieve-hackernews.glasskube.com + value: glasskube.withtrieve.com keyForAnthropicApi: value: ' ' keyForJinaCodeApi: From 9480d6d7b9f6fa9f550327c5b29cfafbc6adb3fc Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Wed, 2 Oct 2024 19:55:27 -0700 Subject: [PATCH 033/159] feat: add kustomization Signed-off-by: Philip Miglinci --- glasskube/argocd/trieve-hackernews/kustomization.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 glasskube/argocd/trieve-hackernews/kustomization.yaml diff --git a/glasskube/argocd/trieve-hackernews/kustomization.yaml b/glasskube/argocd/trieve-hackernews/kustomization.yaml new file mode 100644 index 0000000000..3c21b4dc10 --- /dev/null +++ b/glasskube/argocd/trieve-hackernews/kustomization.yaml @@ -0,0 +1,3 @@ +namespace: trieve +resources: + - application.yaml \ No newline at end of file From 08cfa7e28a249959c41fd092a5cd99773c76a61e Mon Sep 17 00:00:00 2001 From: Jakob Steiner Date: Thu, 3 Oct 2024 16:42:13 +0200 Subject: [PATCH 034/159] chore: change to single ingress for all services and add gcp managed certificate Signed-off-by: Jakob Steiner --- glasskube/trieve-gcp/ingress/ingress.yaml | 68 +++++++++++++++++++ .../trieve-gcp/ingress/kustomization.yaml | 22 ++++++ .../ingress/managed-certificate.yaml | 12 ++++ glasskube/trieve-gcp/kustomization.yaml | 1 + 4 files changed, 103 insertions(+) create mode 100644 glasskube/trieve-gcp/ingress/ingress.yaml create mode 100644 glasskube/trieve-gcp/ingress/kustomization.yaml create mode 100644 glasskube/trieve-gcp/ingress/managed-certificate.yaml diff --git a/glasskube/trieve-gcp/ingress/ingress.yaml b/glasskube/trieve-gcp/ingress/ingress.yaml new file mode 100644 index 0000000000..b593cd3621 --- /dev/null +++ b/glasskube/trieve-gcp/ingress/ingress.yaml @@ -0,0 +1,68 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: trieve + annotations: + networking.gke.io/managed-certificates: trieve-cert +spec: + rules: + - host: auth.localtrieve.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: trieve-keycloak-service + port: + number: 8080 + - host: api.localtrieve.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: trieve-server + port: + name: server + - host: analytics.localtrieve.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: trieve-ui-analytics + port: + name: http + - host: chat.localtrieve.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: trieve-ui-chat + port: + name: http + - host: dashboard.localtrieve.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: trieve-ui-dashboard + port: + name: http + - host: search.localtrieve.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: trieve-ui-search + port: + name: http diff --git a/glasskube/trieve-gcp/ingress/kustomization.yaml b/glasskube/trieve-gcp/ingress/kustomization.yaml new file mode 100644 index 0000000000..4112232154 --- /dev/null +++ b/glasskube/trieve-gcp/ingress/kustomization.yaml @@ -0,0 +1,22 @@ +# This component replaces the many ingresses with a single one that has multiple rules. +# +# We should probably migrate the base manifest to this, as it is much simpler to provision and more +# friendly to external load balancer integration. The only disadvantage is that the hosts patches +# have to access the correct rule via array index, which is a little more error-prone. + +kind: Component +resources: + - ingress.yaml + - managed-certificate.yaml + +# Delete the many Ingresses for individual services +patches: + - target: + kind: Ingress + labelSelector: app.kubernetes.io/component + patch: | + $patch: delete + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: not-used diff --git a/glasskube/trieve-gcp/ingress/managed-certificate.yaml b/glasskube/trieve-gcp/ingress/managed-certificate.yaml new file mode 100644 index 0000000000..283d521863 --- /dev/null +++ b/glasskube/trieve-gcp/ingress/managed-certificate.yaml @@ -0,0 +1,12 @@ +apiVersion: networking.gke.io/v1 +kind: ManagedCertificate +metadata: + name: trieve-cert +spec: + domains: + - auth.localtrieve.com + - api.localtrieve.com + - analytics.localtrieve.com + - chat.localtrieve.com + - dashboard.localtrieve.com + - search.localtrieve.com diff --git a/glasskube/trieve-gcp/kustomization.yaml b/glasskube/trieve-gcp/kustomization.yaml index ede37c1fe8..4cd5e9d858 100644 --- a/glasskube/trieve-gcp/kustomization.yaml +++ b/glasskube/trieve-gcp/kustomization.yaml @@ -3,3 +3,4 @@ resources: components: - ../trieve/embeddings/components/gpu - ./embeddings/components/gpu + - ingress From 383fc7f43eeca9aa864117ac27cf97fea8d23b57 Mon Sep 17 00:00:00 2001 From: Jakob Steiner Date: Thu, 3 Oct 2024 16:42:42 +0200 Subject: [PATCH 035/159] chore: add keycloak extra service with gcp backend config POC --- .../trieve-gcp/keycloak/backend-config.yaml | 10 +++++++++ .../trieve-gcp/keycloak/kustomization.yaml | 6 +++++ glasskube/trieve-gcp/keycloak/service.yaml | 22 +++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 glasskube/trieve-gcp/keycloak/backend-config.yaml create mode 100644 glasskube/trieve-gcp/keycloak/kustomization.yaml create mode 100644 glasskube/trieve-gcp/keycloak/service.yaml diff --git a/glasskube/trieve-gcp/keycloak/backend-config.yaml b/glasskube/trieve-gcp/keycloak/backend-config.yaml new file mode 100644 index 0000000000..ea5e462b0e --- /dev/null +++ b/glasskube/trieve-gcp/keycloak/backend-config.yaml @@ -0,0 +1,10 @@ +apiVersion: cloud.google.com/v1 +kind: BackendConfig +metadata: + name: trieve-keycloak +spec: + healthCheck: + type: HTTP + checkIntervalSec: 15 + port: 9000 + requestPath: /admin/trieve/console/ diff --git a/glasskube/trieve-gcp/keycloak/kustomization.yaml b/glasskube/trieve-gcp/keycloak/kustomization.yaml new file mode 100644 index 0000000000..4ab01c7170 --- /dev/null +++ b/glasskube/trieve-gcp/keycloak/kustomization.yaml @@ -0,0 +1,6 @@ +labels: + - pairs: + app.kubernetes.io/component: keycloak +resources: + - service.yaml + - backend-config.yaml diff --git a/glasskube/trieve-gcp/keycloak/service.yaml b/glasskube/trieve-gcp/keycloak/service.yaml new file mode 100644 index 0000000000..e9782f4afa --- /dev/null +++ b/glasskube/trieve-gcp/keycloak/service.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + name: trieve-keycloak + annotations: + cloud.google.com/neg: '{"ingress": true}' + beta.cloud.google.com/backend-config: '{"default": "trieve-keycloak"}' +spec: + # TODO: This won't work because the operator injects it's own labels into all metadata, selectors + # and templates. To fix this, the simplest way is probably to let the operator "know" that it + # also has to inject labels in the keycloak pod template. But should there be a list of + # "well-known" label field-specs? Or should it be specified in the package.yaml? + selector: + app: keycloak + # TODO: operator insert + # app.kubernetes.io/instance: ${INSTANCE_NAME}-trieve-keycloak + app.kubernetes.io/managed-by: keycloak-operator + ports: + - name: http + port: 8080 + - name: management + port: 9000 From 97bf136df6f5f5220a0f48e7112242f2cd98108e Mon Sep 17 00:00:00 2001 From: Jakob Steiner Date: Wed, 2 Oct 2024 16:41:08 +0200 Subject: [PATCH 036/159] fix: disable keycloak builtin ingress Signed-off-by: Jakob Steiner --- glasskube/trieve/keycloak/keycloak.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/glasskube/trieve/keycloak/keycloak.yaml b/glasskube/trieve/keycloak/keycloak.yaml index e54327470c..772cc40289 100644 --- a/glasskube/trieve/keycloak/keycloak.yaml +++ b/glasskube/trieve/keycloak/keycloak.yaml @@ -21,8 +21,10 @@ spec: key: password http: httpEnabled: true + ingress: + enabled: false hostname: strict: false hostname: http://trieve-keycloak-service:8080 proxy: - headers: xforwarded # double check your reverse proxy sets and overwrites the X-Forwarded-* headers \ No newline at end of file + headers: xforwarded # double check your reverse proxy sets and overwrites the X-Forwarded-* headers From a60aa4da81430a0bb245c62a6e4c67af75174353 Mon Sep 17 00:00:00 2001 From: Jakob Steiner Date: Fri, 4 Oct 2024 11:05:18 +0200 Subject: [PATCH 037/159] chore: move combined ingress to base --- .../trieve-gcp/ingress-cert/ingress.yaml | 6 +++++ .../ingress-cert/kustomization.yaml | 5 +++++ .../managed-certificate.yaml | 0 .../trieve-gcp/ingress/kustomization.yaml | 22 ------------------- glasskube/trieve-gcp/kustomization.yaml | 2 +- .../ingress => trieve}/ingress.yaml | 2 -- glasskube/trieve/keycloak/ingress.yaml | 16 -------------- glasskube/trieve/keycloak/kustomization.yaml | 1 - glasskube/trieve/kustomization.yaml | 1 + glasskube/trieve/server/ingress.yaml | 16 -------------- glasskube/trieve/server/kustomization.yaml | 1 - .../trieve/ui/analytics/kustomization.yaml | 8 ------- glasskube/trieve/ui/base/ingress.yaml | 15 ------------- glasskube/trieve/ui/base/kustomization.yaml | 1 - glasskube/trieve/ui/chat/kustomization.yaml | 8 ------- .../trieve/ui/dashboard/kustomization.yaml | 8 ------- glasskube/trieve/ui/search/kustomization.yaml | 8 ------- 17 files changed, 13 insertions(+), 107 deletions(-) create mode 100644 glasskube/trieve-gcp/ingress-cert/ingress.yaml create mode 100644 glasskube/trieve-gcp/ingress-cert/kustomization.yaml rename glasskube/trieve-gcp/{ingress => ingress-cert}/managed-certificate.yaml (100%) delete mode 100644 glasskube/trieve-gcp/ingress/kustomization.yaml rename glasskube/{trieve-gcp/ingress => trieve}/ingress.yaml (95%) delete mode 100644 glasskube/trieve/keycloak/ingress.yaml delete mode 100644 glasskube/trieve/server/ingress.yaml delete mode 100644 glasskube/trieve/ui/base/ingress.yaml diff --git a/glasskube/trieve-gcp/ingress-cert/ingress.yaml b/glasskube/trieve-gcp/ingress-cert/ingress.yaml new file mode 100644 index 0000000000..94a9c139b3 --- /dev/null +++ b/glasskube/trieve-gcp/ingress-cert/ingress.yaml @@ -0,0 +1,6 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: trieve + annotations: + networking.gke.io/managed-certificates: trieve-cert diff --git a/glasskube/trieve-gcp/ingress-cert/kustomization.yaml b/glasskube/trieve-gcp/ingress-cert/kustomization.yaml new file mode 100644 index 0000000000..a60af23fb1 --- /dev/null +++ b/glasskube/trieve-gcp/ingress-cert/kustomization.yaml @@ -0,0 +1,5 @@ +kind: Component +resources: + - managed-certificate.yaml +patches: + - path: ingress.yaml diff --git a/glasskube/trieve-gcp/ingress/managed-certificate.yaml b/glasskube/trieve-gcp/ingress-cert/managed-certificate.yaml similarity index 100% rename from glasskube/trieve-gcp/ingress/managed-certificate.yaml rename to glasskube/trieve-gcp/ingress-cert/managed-certificate.yaml diff --git a/glasskube/trieve-gcp/ingress/kustomization.yaml b/glasskube/trieve-gcp/ingress/kustomization.yaml deleted file mode 100644 index 4112232154..0000000000 --- a/glasskube/trieve-gcp/ingress/kustomization.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# This component replaces the many ingresses with a single one that has multiple rules. -# -# We should probably migrate the base manifest to this, as it is much simpler to provision and more -# friendly to external load balancer integration. The only disadvantage is that the hosts patches -# have to access the correct rule via array index, which is a little more error-prone. - -kind: Component -resources: - - ingress.yaml - - managed-certificate.yaml - -# Delete the many Ingresses for individual services -patches: - - target: - kind: Ingress - labelSelector: app.kubernetes.io/component - patch: | - $patch: delete - apiVersion: networking.k8s.io/v1 - kind: Ingress - metadata: - name: not-used diff --git a/glasskube/trieve-gcp/kustomization.yaml b/glasskube/trieve-gcp/kustomization.yaml index 4cd5e9d858..9abba4ea68 100644 --- a/glasskube/trieve-gcp/kustomization.yaml +++ b/glasskube/trieve-gcp/kustomization.yaml @@ -3,4 +3,4 @@ resources: components: - ../trieve/embeddings/components/gpu - ./embeddings/components/gpu - - ingress + - ingress-cert diff --git a/glasskube/trieve-gcp/ingress/ingress.yaml b/glasskube/trieve/ingress.yaml similarity index 95% rename from glasskube/trieve-gcp/ingress/ingress.yaml rename to glasskube/trieve/ingress.yaml index b593cd3621..5b132c3e5b 100644 --- a/glasskube/trieve-gcp/ingress/ingress.yaml +++ b/glasskube/trieve/ingress.yaml @@ -2,8 +2,6 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: trieve - annotations: - networking.gke.io/managed-certificates: trieve-cert spec: rules: - host: auth.localtrieve.com diff --git a/glasskube/trieve/keycloak/ingress.yaml b/glasskube/trieve/keycloak/ingress.yaml deleted file mode 100644 index 843cd9f398..0000000000 --- a/glasskube/trieve/keycloak/ingress.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: trieve-keycloak -spec: - rules: - - host: auth.localtrieve.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: trieve-keycloak-service - port: - number: 8080 diff --git a/glasskube/trieve/keycloak/kustomization.yaml b/glasskube/trieve/keycloak/kustomization.yaml index 9f753f587b..0ff037b9b2 100644 --- a/glasskube/trieve/keycloak/kustomization.yaml +++ b/glasskube/trieve/keycloak/kustomization.yaml @@ -3,4 +3,3 @@ commonLabels: resources: - keycloak.yaml - realm-import.yaml - - ingress.yaml diff --git a/glasskube/trieve/kustomization.yaml b/glasskube/trieve/kustomization.yaml index 06b73115ad..f182899208 100644 --- a/glasskube/trieve/kustomization.yaml +++ b/glasskube/trieve/kustomization.yaml @@ -4,6 +4,7 @@ commonLabels: app.kubernetes.io/instance: trieve resources: - configmap.yaml + - ingress.yaml - clickhouse - keycloak - server diff --git a/glasskube/trieve/server/ingress.yaml b/glasskube/trieve/server/ingress.yaml deleted file mode 100644 index 532a7b6525..0000000000 --- a/glasskube/trieve/server/ingress.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: trieve-server -spec: - rules: - - host: api.localtrieve.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: trieve-server - port: - name: server diff --git a/glasskube/trieve/server/kustomization.yaml b/glasskube/trieve/server/kustomization.yaml index 416a27777e..febc3868a2 100644 --- a/glasskube/trieve/server/kustomization.yaml +++ b/glasskube/trieve/server/kustomization.yaml @@ -3,4 +3,3 @@ commonLabels: resources: - deployment.yaml - service.yaml - - ingress.yaml diff --git a/glasskube/trieve/ui/analytics/kustomization.yaml b/glasskube/trieve/ui/analytics/kustomization.yaml index bb19d35739..199a04a8a8 100644 --- a/glasskube/trieve/ui/analytics/kustomization.yaml +++ b/glasskube/trieve/ui/analytics/kustomization.yaml @@ -3,14 +3,6 @@ commonLabels: nameSuffix: -analytics resources: - ../base -patches: - - target: - kind: Ingress - name: trieve-ui - patch: |- - - op: add - path: /spec/rules/0/host - value: analytics.localtrieve.com images: - name: ui newName: trieve/analytics-site diff --git a/glasskube/trieve/ui/base/ingress.yaml b/glasskube/trieve/ui/base/ingress.yaml deleted file mode 100644 index f0e3185d16..0000000000 --- a/glasskube/trieve/ui/base/ingress.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: trieve-ui -spec: - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: trieve-ui - port: - name: http diff --git a/glasskube/trieve/ui/base/kustomization.yaml b/glasskube/trieve/ui/base/kustomization.yaml index f6f370e41e..6d1374a18e 100644 --- a/glasskube/trieve/ui/base/kustomization.yaml +++ b/glasskube/trieve/ui/base/kustomization.yaml @@ -1,4 +1,3 @@ resources: - deployment.yaml - service.yaml - - ingress.yaml diff --git a/glasskube/trieve/ui/chat/kustomization.yaml b/glasskube/trieve/ui/chat/kustomization.yaml index 923bc9c372..9b26f907cd 100644 --- a/glasskube/trieve/ui/chat/kustomization.yaml +++ b/glasskube/trieve/ui/chat/kustomization.yaml @@ -3,14 +3,6 @@ commonLabels: nameSuffix: -chat resources: - ../base -patches: - - target: - kind: Ingress - name: trieve-ui - patch: |- - - op: add - path: /spec/rules/0/host - value: chat.localtrieve.com images: - name: ui newName: trieve/chat diff --git a/glasskube/trieve/ui/dashboard/kustomization.yaml b/glasskube/trieve/ui/dashboard/kustomization.yaml index e9db323f0e..664d717fdf 100644 --- a/glasskube/trieve/ui/dashboard/kustomization.yaml +++ b/glasskube/trieve/ui/dashboard/kustomization.yaml @@ -3,14 +3,6 @@ commonLabels: nameSuffix: -dashboard resources: - ../base -patches: - - target: - kind: Ingress - name: trieve-ui - patch: |- - - op: add - path: /spec/rules/0/host - value: dashboard.localtrieve.com images: - name: ui newName: trieve/dashboard diff --git a/glasskube/trieve/ui/search/kustomization.yaml b/glasskube/trieve/ui/search/kustomization.yaml index cfd4b2fadc..30e2ff6a2e 100644 --- a/glasskube/trieve/ui/search/kustomization.yaml +++ b/glasskube/trieve/ui/search/kustomization.yaml @@ -3,14 +3,6 @@ commonLabels: nameSuffix: -search resources: - ../base -patches: - - target: - kind: Ingress - name: trieve-ui - patch: |- - - op: add - path: /spec/rules/0/host - value: search.localtrieve.com images: - name: ui newName: trieve/search From e52d95f05f5d1dfef893fcd7bfb82b2f84dfe041 Mon Sep 17 00:00:00 2001 From: Jakob Steiner Date: Fri, 4 Oct 2024 14:59:52 +0200 Subject: [PATCH 038/159] chore: fix keycloak backendconfig --- glasskube/trieve-gcp/keycloak/kustomization.yaml | 14 +++++++++----- .../keycloak/{ => res}/backend-config.yaml | 2 +- .../trieve-gcp/keycloak/res/kustomization.yaml | 6 ++++++ .../trieve-gcp/keycloak/{ => res}/service.yaml | 0 glasskube/trieve-gcp/kustomization.yaml | 1 + 5 files changed, 17 insertions(+), 6 deletions(-) rename glasskube/trieve-gcp/keycloak/{ => res}/backend-config.yaml (92%) create mode 100644 glasskube/trieve-gcp/keycloak/res/kustomization.yaml rename glasskube/trieve-gcp/keycloak/{ => res}/service.yaml (100%) diff --git a/glasskube/trieve-gcp/keycloak/kustomization.yaml b/glasskube/trieve-gcp/keycloak/kustomization.yaml index 4ab01c7170..03ff5e3a4a 100644 --- a/glasskube/trieve-gcp/keycloak/kustomization.yaml +++ b/glasskube/trieve-gcp/keycloak/kustomization.yaml @@ -1,6 +1,10 @@ -labels: - - pairs: - app.kubernetes.io/component: keycloak +kind: Component resources: - - service.yaml - - backend-config.yaml + - res +patches: + - target: + kind: Ingress + patch: | + - op: replace + path: /spec/rules/0/http/paths/0/backend/service/name + value: trieve-keycloak diff --git a/glasskube/trieve-gcp/keycloak/backend-config.yaml b/glasskube/trieve-gcp/keycloak/res/backend-config.yaml similarity index 92% rename from glasskube/trieve-gcp/keycloak/backend-config.yaml rename to glasskube/trieve-gcp/keycloak/res/backend-config.yaml index ea5e462b0e..a50c8339db 100644 --- a/glasskube/trieve-gcp/keycloak/backend-config.yaml +++ b/glasskube/trieve-gcp/keycloak/res/backend-config.yaml @@ -6,5 +6,5 @@ spec: healthCheck: type: HTTP checkIntervalSec: 15 - port: 9000 + port: 8080 requestPath: /admin/trieve/console/ diff --git a/glasskube/trieve-gcp/keycloak/res/kustomization.yaml b/glasskube/trieve-gcp/keycloak/res/kustomization.yaml new file mode 100644 index 0000000000..4ab01c7170 --- /dev/null +++ b/glasskube/trieve-gcp/keycloak/res/kustomization.yaml @@ -0,0 +1,6 @@ +labels: + - pairs: + app.kubernetes.io/component: keycloak +resources: + - service.yaml + - backend-config.yaml diff --git a/glasskube/trieve-gcp/keycloak/service.yaml b/glasskube/trieve-gcp/keycloak/res/service.yaml similarity index 100% rename from glasskube/trieve-gcp/keycloak/service.yaml rename to glasskube/trieve-gcp/keycloak/res/service.yaml diff --git a/glasskube/trieve-gcp/kustomization.yaml b/glasskube/trieve-gcp/kustomization.yaml index 9abba4ea68..093be2cca8 100644 --- a/glasskube/trieve-gcp/kustomization.yaml +++ b/glasskube/trieve-gcp/kustomization.yaml @@ -4,3 +4,4 @@ components: - ../trieve/embeddings/components/gpu - ./embeddings/components/gpu - ingress-cert + - keycloak From 3c8661ecd013ca2a2ab6606b370cab3ed262c974 Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Fri, 4 Oct 2024 09:38:01 -0700 Subject: [PATCH 039/159] feat: upgrade glasskube to v0.24.0 Signed-off-by: Philip Miglinci --- glasskube/argocd/glasskube/glasskube.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/glasskube/argocd/glasskube/glasskube.yaml b/glasskube/argocd/glasskube/glasskube.yaml index b8b6f498cb..0e9603db6a 100644 --- a/glasskube/argocd/glasskube/glasskube.yaml +++ b/glasskube/argocd/glasskube/glasskube.yaml @@ -10034,7 +10034,7 @@ spec: - --leader-elect command: - /package-operator - image: ghcr.io/glasskube/package-operator:v0.23.0 + image: ghcr.io/glasskube/package-operator:v0.24.0 livenessProbe: httpGet: path: /healthz @@ -10114,7 +10114,7 @@ spec: containers: - command: - /cert-manager - image: ghcr.io/glasskube/package-operator:v0.23.0 + image: ghcr.io/glasskube/package-operator:v0.24.0 name: manager resources: limits: @@ -10147,7 +10147,7 @@ spec: containers: - command: - /cert-manager - image: ghcr.io/glasskube/package-operator:v0.23.0 + image: ghcr.io/glasskube/package-operator:v0.24.0 name: manager resources: limits: From 3fafd77fe3c56a1e5233c0c9c37823142d1af369 Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Fri, 4 Oct 2024 15:42:54 -0700 Subject: [PATCH 040/159] feat: upgrade to the latest trieve-gcp package Signed-off-by: Philip Miglinci --- glasskube/argocd/trieve-hackernews/application.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glasskube/argocd/trieve-hackernews/application.yaml b/glasskube/argocd/trieve-hackernews/application.yaml index 3a70a0639f..5987f4509d 100644 --- a/glasskube/argocd/trieve-hackernews/application.yaml +++ b/glasskube/argocd/trieve-hackernews/application.yaml @@ -7,7 +7,7 @@ spec: packageInfo: name: trieve-gcp repositoryName: trieve - version: v0.11.8+3 + version: v0.11.8+4 values: cpuMode: value: 'false' From 72dfab6d0cb46119013e2b8bed35285cb8f436a3 Mon Sep 17 00:00:00 2001 From: Philip Miglinci Date: Tue, 8 Oct 2024 16:52:29 -0700 Subject: [PATCH 041/159] feat: let embeddings share, gpu, update hash, increase splade-query doc memory Signed-off-by: Philip Miglinci --- .../components/gpu/deployment-patch.yaml | 2 +- .../splade-query/deployment-patch.yaml | 4 ++-- glasskube/trieve/kustomization.yaml | 20 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/glasskube/trieve/embeddings/components/gpu/deployment-patch.yaml b/glasskube/trieve/embeddings/components/gpu/deployment-patch.yaml index 6e3e2190c6..be308005d4 100644 --- a/glasskube/trieve/embeddings/components/gpu/deployment-patch.yaml +++ b/glasskube/trieve/embeddings/components/gpu/deployment-patch.yaml @@ -9,4 +9,4 @@ spec: - name: embedding resources: limits: - nvidia.com/gpu: "1" + nvidia.com/gpu: "0.2" diff --git a/glasskube/trieve/embeddings/splade-query/deployment-patch.yaml b/glasskube/trieve/embeddings/splade-query/deployment-patch.yaml index c11c75617b..1e89e81081 100644 --- a/glasskube/trieve/embeddings/splade-query/deployment-patch.yaml +++ b/glasskube/trieve/embeddings/splade-query/deployment-patch.yaml @@ -17,6 +17,6 @@ spec: - splade resources: requests: - memory: 500Mi + memory: 1Gi limits: - memory: 500Mi + memory: 2Gi diff --git a/glasskube/trieve/kustomization.yaml b/glasskube/trieve/kustomization.yaml index f182899208..0fe256e2e7 100644 --- a/glasskube/trieve/kustomization.yaml +++ b/glasskube/trieve/kustomization.yaml @@ -17,30 +17,30 @@ images: - name: ghcr.io/huggingface/text-embeddings-inference newTag: cpu-1.4 - name: trieve/ingest - newTag: sha-d8b18ca + newTag: sha-85f4954 - name: trieve/sync_qdrant - newTag: sha-d8b18ca + newTag: sha-85f4954 - name: trieve/server - newTag: sha-d8b18ca + newTag: sha-85f4954 - name: trieve/analytics-site newTag: sha-ed22f21 - name: trieve/chat - newTag: sha-ed22f21 + newTag: sha-85f4954 - name: trieve/dashboard - newTag: sha-ed22f21 + newTag: sha-85f4954 - name: trieve/search - newTag: sha-ed22f21 + newTag: sha-85f4954 - name: trieve/delete-worker newName: trieve/delete_worker - newTag: sha-d8b18ca + newTag: sha-85f4954 - name: trieve/file-worker newName: trieve/file_worker - newTag: sha-d8b18ca + newTag: sha-85f4954 - name: trieve/group-worker newName: trieve/group_worker - newTag: sha-d8b18ca + newTag: sha-85f4954 - name: trieve/word-id-cronjob - newTag: sha-d8b18ca + newTag: sha-85f4954 - name: trieve/clickhouse-collapse-query-script newTag: sha-5cdcd38 - name: trieve/clickhouse-clustering From 81a0567d7f1a6bc999d4eed17299cac2c14c587c Mon Sep 17 00:00:00 2001 From: cdxker Date: Tue, 8 Oct 2024 22:44:31 -0700 Subject: [PATCH 042/159] feat: auto focus input field --- clients/search-component/package.json | 2 +- .../src/TrieveModal/Chat/ChatMode.tsx | 11 ++++++++++- .../src/TrieveModal/Search/SearchMode.tsx | 8 ++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/clients/search-component/package.json b/clients/search-component/package.json index f18177c307..d79ce74d83 100644 --- a/clients/search-component/package.json +++ b/clients/search-component/package.json @@ -7,7 +7,7 @@ "dist/*", "dist/**/*" ], - "version": "0.0.19", + "version": "0.0.20", "license": "MIT", "scripts": { "dev": "run-p watch:css watch:js", diff --git a/clients/search-component/src/TrieveModal/Chat/ChatMode.tsx b/clients/search-component/src/TrieveModal/Chat/ChatMode.tsx index ab5b0e09fd..de452efa96 100644 --- a/clients/search-component/src/TrieveModal/Chat/ChatMode.tsx +++ b/clients/search-component/src/TrieveModal/Chat/ChatMode.tsx @@ -6,7 +6,7 @@ import { useChatState } from "../../utils/hooks/chat-context"; import { ChatMessage } from "./ChatMessage"; export const ChatMode = () => { - const { setMode, modalRef } = useModalState(); + const { setMode, modalRef, open, mode } = useModalState(); const { askQuestion, messages, @@ -17,6 +17,14 @@ export const ChatMode = () => { stopGeneratingMessage, } = useChatState(); + const chatInput = React.useRef(null); + + React.useEffect(() => { + if (mode == "chat" && open) { + chatInput.current?.focus(); + } + }, [mode, open]); + return ( <>
@@ -51,6 +59,7 @@ export const ChatMode = () => { }} > setCurrentQuestion(e.target.value)} placeholder="Ask me anything" diff --git a/clients/search-component/src/TrieveModal/Search/SearchMode.tsx b/clients/search-component/src/TrieveModal/Search/SearchMode.tsx index e3426ccd19..0e33a44fd1 100644 --- a/clients/search-component/src/TrieveModal/Search/SearchMode.tsx +++ b/clients/search-component/src/TrieveModal/Search/SearchMode.tsx @@ -15,6 +15,8 @@ export const SearchMode = () => { setQuery, requestID, inputRef, + open, + mode, } = useModalState(); const { suggestedQueries, @@ -23,6 +25,12 @@ export const SearchMode = () => { } = useSuggestedQueries(); const { switchToChatAndAskQuestion } = useChatState(); + React.useEffect(() => { + if (mode == "search" && open) { + inputRef.current?.focus(); + } + }, [mode, open]); + return ( <>
From 5510dae2d28014126919a9f58057f4a7f8f0a03c Mon Sep 17 00:00:00 2001 From: cdxker Date: Tue, 8 Oct 2024 23:08:39 -0700 Subject: [PATCH 043/159] bugfix: autofocus chatInput on open --- clients/search-component/src/TrieveModal/Chat/ChatMode.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/search-component/src/TrieveModal/Chat/ChatMode.tsx b/clients/search-component/src/TrieveModal/Chat/ChatMode.tsx index de452efa96..65802c4118 100644 --- a/clients/search-component/src/TrieveModal/Chat/ChatMode.tsx +++ b/clients/search-component/src/TrieveModal/Chat/ChatMode.tsx @@ -23,7 +23,7 @@ export const ChatMode = () => { if (mode == "chat" && open) { chatInput.current?.focus(); } - }, [mode, open]); + }, [chatInput, mode, open]); return ( <> From 5c343d8ca9ebae2521ccd580e30de56ecea405a6 Mon Sep 17 00:00:00 2001 From: skeptrune Date: Tue, 8 Oct 2024 23:43:41 -0700 Subject: [PATCH 044/159] cleanup: avoid panic when ditto api key is not set --- server/src/handlers/auth_handler.rs | 1 + server/src/operators/dittofeed_operator.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/server/src/handlers/auth_handler.rs b/server/src/handlers/auth_handler.rs index 5dd64da752..2ff5220707 100644 --- a/server/src/handlers/auth_handler.rs +++ b/server/src/handlers/auth_handler.rs @@ -512,6 +512,7 @@ pub async fn callback( log::info!("No ditto identity for user {}. Error: {}", user.email, e); } } + (user, user_orgs, orgs) } }; diff --git a/server/src/operators/dittofeed_operator.rs b/server/src/operators/dittofeed_operator.rs index 17f861b1f1..2ea03ac340 100644 --- a/server/src/operators/dittofeed_operator.rs +++ b/server/src/operators/dittofeed_operator.rs @@ -299,7 +299,14 @@ pub async fn send_user_ditto_identity( ) -> Result<(), ServiceError> { let dittofeed_url = std::env::var("DITTOFEED_URL").unwrap_or("https://app.dittofeed.com".to_string()); - let api_key = std::env::var("DITTOFEED_API_KEY").expect("DITTOFEED_API_KEY is not set"); + let api_key = match std::env::var("DITTOFEED_API_KEY") { + Ok(api_key) => api_key, + Err(_) => { + return Err(ServiceError::BadRequest( + "DITTOFEED_API_KEY is not set".to_string(), + )) + } + }; let client = reqwest::Client::new(); From 90971946ecb7c199c17a5c5676fb2e090999f4b1 Mon Sep 17 00:00:00 2001 From: Dens Sumesh Date: Wed, 9 Oct 2024 18:02:56 -0700 Subject: [PATCH 045/159] feature: add prices and images to ecomm chat --- .../example/src/routes/index.lazy.tsx | 16 +- .../src/TrieveModal/Chat/ChatMessage.tsx | 170 ++++++++++++------ .../src/TrieveModal/index.css | 37 +++- .../src/utils/hooks/chat-context.tsx | 53 +++--- .../src/utils/hooks/modal-context.tsx | 12 +- 5 files changed, 195 insertions(+), 93 deletions(-) diff --git a/clients/search-component/example/src/routes/index.lazy.tsx b/clients/search-component/example/src/routes/index.lazy.tsx index 36e649037b..7772b5a648 100644 --- a/clients/search-component/example/src/routes/index.lazy.tsx +++ b/clients/search-component/example/src/routes/index.lazy.tsx @@ -11,8 +11,8 @@ export const Route = createLazyFileRoute("/")({ }); const trieve = new TrieveSDK({ - apiKey: "tr-IzHXaZ89aELJXk0RkP1oIgqA6GFLGInG", - datasetId: "78c10935-12ad-4431-bb0f-3d9929262861", + apiKey: "tr-UPjrAOp35kGrJWM1IvODj4zVWTdGgQxI", + datasetId: "4650e231-7857-45aa-beb1-cb52006a2460", }); export default function Home() { @@ -23,14 +23,12 @@ export default function Home() {
+ }`}>
  • @@ -61,8 +59,7 @@ export default function Home() { strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" - className="w-3 h-3" - > + className="w-3 h-3"> @@ -86,8 +83,7 @@ export default function Home() { stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" - className="w-3 h-3" - > + className="w-3 h-3"> diff --git a/clients/search-component/src/TrieveModal/Chat/ChatMessage.tsx b/clients/search-component/src/TrieveModal/Chat/ChatMessage.tsx index c9c15e0f5a..df1bd15962 100644 --- a/clients/search-component/src/TrieveModal/Chat/ChatMessage.tsx +++ b/clients/search-component/src/TrieveModal/Chat/ChatMessage.tsx @@ -1,5 +1,11 @@ import * as React from "react"; -import { AIIcon, LoadingIcon, ThumbsDownIcon, ThumbsUpIcon, UserIcon } from "../icons"; +import { + AIIcon, + LoadingIcon, + ThumbsDownIcon, + ThumbsUpIcon, + UserIcon, +} from "../icons"; import Markdown from "react-markdown"; import SyntaxHighlighter from "react-syntax-highlighter"; import { nightOwl } from "react-syntax-highlighter/dist/esm/styles/hljs"; @@ -36,13 +42,12 @@ export const ChatMessage = ({ ? `${props.brandColor}18` : "#CB53EB18", color: props.brandColor ?? "#CB53EB", - }} - > + }}> User

    - {message.text} + {message.text}
    ) : ( @@ -64,8 +69,7 @@ export const ChatMessage = ({ ? `${props.brandColor}18` : "#CB53EB18", color: props.brandColor ?? "#CB53EB", - }} - > + }}> AI assistant

    @@ -85,6 +89,7 @@ export const Message = ({ }) => { const { rateChatCompletion } = useChatState(); const [positive, setPositive] = React.useState(null); + const { props } = useModalState(); return (
    @@ -110,66 +115,119 @@ export const Message = ({ return ( + style={nightOwl}> {children?.toString()} ); }, }} - key={idx} - > + key={idx}> {message.text} - {message.additional ? ( -
    -
    - {message.additional - .filter( - (m) => - (m.metadata.heading || - m.metadata.title || - m.metadata.page_title) && - m.link - ) - .map((m) => [ - m.metadata.heading || - m.metadata.title || - m.metadata.page_title, - m.link, - ]) - .filter( - (v, i, a) => a.findIndex((t) => t[0] === v[0]) === i && v[0] - ) - .map((link, idx) => ( - - {link[0]} - - ))} -
    -
    - -
    - - +
    + {message.additional ? ( + props.type === "ecommerce" ? ( +
    + {message.additional + .filter( + (chunk) => + (chunk.metadata.heading || + chunk.metadata.title || + chunk.metadata.page_title) && + chunk.link && + chunk.image_urls?.length && + chunk.num_value, + ) + .map((chunk) => ({ + title: + chunk.metadata.heading || + chunk.metadata.title || + chunk.metadata.page_title, + link: chunk.link, + imageUrl: (chunk.image_urls ?? [])[0], + price: chunk.num_value, + })) + .filter( + (item, index, array) => + array.findIndex( + (arrayItem) => arrayItem.title === item.title, + ) === index && item.title, + ) + .map((item, index) => ( + + {item.title} +
    +

    {item.title}

    +

    + ${item.price} +

    +
    +
    + ))}
    + ) : ( +
    + {message.additional + .filter( + (chunk) => + (chunk.metadata.heading || + chunk.metadata.title || + chunk.metadata.page_title) && + chunk.link, + ) + .map((chunk) => [ + chunk.metadata.heading || + chunk.metadata.title || + chunk.metadata.page_title, + chunk.link, + ]) + .filter( + (link, index, array) => + array.findIndex((item) => item[0] === link[0]) === + index && link[0], + ) + .map((link, index) => ( + + {link[0]} + + ))} +
    + ) + ) : null} +
    + +
    + +
    - ) : null} +
    ) : null}
    diff --git a/clients/search-component/src/TrieveModal/index.css b/clients/search-component/src/TrieveModal/index.css index 74921ed314..9b56d78d01 100644 --- a/clients/search-component/src/TrieveModal/index.css +++ b/clients/search-component/src/TrieveModal/index.css @@ -209,8 +209,42 @@ body { } } + .additional-image-links { + @apply grid grid-cols-3 gap-2 mt-4; + + a { + @apply rounded px-2 text-xs py-1 line-clamp-2 overflow-ellipsis grid-rows-2; + color: var(--tv-zinc-700); + background-color: var(--tv-zinc-300); + + &:hover { + color: var(--tv-zinc-950); + } + } + + .ecomm-details { + @apply flex gap-1 items-center; + } + + .ecomm-item-title { + @apply p-0 mb-0.5 line-clamp-2; + } + + .ecomm-item-price { + @apply p-0 mb-0.5; + } + + .ecommerce-featured-image-chat { + @apply mb-1; + } + } + .user { @apply flex gap-1 text-center items-center my-5; + + .user-text { + @apply text-left; + } } .loading-system { @@ -220,6 +254,7 @@ body { .system { @apply mt-2 sm:mt-8 mb-5 items-center sm:max-w-[46vw] overflow-ellipsis; + p { @apply pb-1; } @@ -538,7 +573,7 @@ body { } svg { - @apply shrink-0 w-8 + @apply shrink-0 w-8; } mark { diff --git a/clients/search-component/src/utils/hooks/chat-context.tsx b/clients/search-component/src/utils/hooks/chat-context.tsx index dd620215f0..a9032b758a 100644 --- a/clients/search-component/src/utils/hooks/chat-context.tsx +++ b/clients/search-component/src/utils/hooks/chat-context.tsx @@ -41,7 +41,7 @@ function ChatProvider({ children }: { children: React.ReactNode }) { const [messages, setMessages] = useState([]); const [isLoading, setIsLoading] = useState(false); const chatMessageAbortController = useRef( - new AbortController() + new AbortController(), ); const isDoneReading = useRef(true); const createTopic = async ({ question }: { question: string }) => { @@ -66,7 +66,7 @@ function ChatProvider({ children }: { children: React.ReactNode }) { const handleReader = async ( reader: ReadableStreamDefaultReader, - queryId: string | null + queryId: string | null, ) => { setIsLoading(true); isDoneReading.current = false; @@ -97,7 +97,7 @@ function ChatProvider({ children }: { children: React.ReactNode }) { type: "system", text: text, additional: json ? json : null, - queryId + queryId, }, ], ]); @@ -119,16 +119,18 @@ function ChatProvider({ children }: { children: React.ReactNode }) { question?: string; }) => { setIsLoading(true); - const { reader, queryId } = await props.trieve.createMessageReaderWithQueryId( - { - topic_id: id || currentTopic, - new_message_content: question || currentQuestion, - llm_options: { - completion_first: true, + const { reader, queryId } = + await props.trieve.createMessageReaderWithQueryId( + { + topic_id: id || currentTopic, + new_message_content: question || currentQuestion, + llm_options: { + completion_first: true, + }, + page_size: 5, }, - }, - chatMessageAbortController.current.signal - ); + chatMessageAbortController.current.signal, + ); handleReader(reader, queryId); }; @@ -143,7 +145,7 @@ function ChatProvider({ children }: { children: React.ReactNode }) { [ ...messages.slice(0, -1), messages[messages.length - 1]?.slice(0, -1), - ].filter((a) => a.length) + ].filter((a) => a.length), ); } }; @@ -152,7 +154,14 @@ function ChatProvider({ children }: { children: React.ReactNode }) { isDoneReading.current = false; setMessages((m) => [ ...m, - [{ type: "user", text: question || currentQuestion, additional: null, queryId: null }], + [ + { + type: "user", + text: question || currentQuestion, + additional: null, + queryId: null, + }, + ], ]); if (!currentTopic) { @@ -177,14 +186,17 @@ function ChatProvider({ children }: { children: React.ReactNode }) { await askQuestion(query); }; - const rateChatCompletion = async(isPositive: boolean, queryId: string | null) => { + const rateChatCompletion = async ( + isPositive: boolean, + queryId: string | null, + ) => { if (queryId) { props.trieve.rateRagQuery({ - rating: isPositive ? 1: 0, - query_id: queryId + rating: isPositive ? 1 : 0, + query_id: queryId, }); } - } + }; return ( + rateChatCompletion, + }}> {children} ); diff --git a/clients/search-component/src/utils/hooks/modal-context.tsx b/clients/search-component/src/utils/hooks/modal-context.tsx index 3ea7ea600a..e648a320f8 100644 --- a/clients/search-component/src/utils/hooks/modal-context.tsx +++ b/clients/search-component/src/utils/hooks/modal-context.tsx @@ -41,6 +41,7 @@ export type ModalProps = { tags?: { tag: string; label?: string; + selected?: boolean; icon?: () => JSX.Element; }[]; defaultSearchMode?: SearchModes; @@ -127,7 +128,9 @@ function ModalProvider({ const [mode, setMode] = useState(props.defaultSearchMode || "search"); const modalRef = useRef(null); const [tagCounts, setTagCounts] = useState([]); - const [currentTag, setCurrentTag] = useState("all"); + const [currentTag, setCurrentTag] = useState( + props.tags?.find((t) => t.selected)?.tag || "all", + ); useEffect(() => { setProps((p) => ({ @@ -182,8 +185,8 @@ function ModalProvider({ trieve: props.trieve, abortController, ...(tag.tag !== "all" && { tag: tag.tag }), - }) - ) + }), + ), ); setTagCounts(numberOfRecords); } @@ -224,8 +227,7 @@ function ModalProvider({ currentTag, setCurrentTag, tagCounts, - }} - > + }}> {children} ); From d3a6609728bfebc5ac5f867457431083c42bb35c Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Tue, 8 Oct 2024 17:32:11 -0500 Subject: [PATCH 046/159] feat: add shopify scraping --- server/src/bin/crawl-worker.rs | 145 ++++++++++++++++++++++++++++++++- server/src/data/models.rs | 2 + 2 files changed, 144 insertions(+), 3 deletions(-) diff --git a/server/src/bin/crawl-worker.rs b/server/src/bin/crawl-worker.rs index c921a58b46..0bb37a468a 100644 --- a/server/src/bin/crawl-worker.rs +++ b/server/src/bin/crawl-worker.rs @@ -1,6 +1,7 @@ use actix_web::web; use diesel_async::pooled_connection::{AsyncDieselConnectionManager, ManagerConfig}; use sentry::{Hub, SentryFutureExt}; +use serde::{Deserialize, Serialize}; use signal_hook::consts::SIGTERM; use std::sync::{ atomic::{AtomicBool, Ordering}, @@ -41,12 +42,135 @@ struct ScrapeReport { chunks_created: usize, } +#[derive(Debug, Deserialize)] +struct ShopifyResponse { + products: Vec, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +struct ShopifyVariant { + id: u64, + product_id: u64, + title: String, + price: String, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +struct ShopifyProduct { + id: u64, + title: String, + body_html: String, + handle: String, + tags: Vec, + variants: Vec, + images: Vec, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +struct ShopifyImage { + src: String, +} + +fn create_chunk_req_payload( + product: &ShopifyProduct, + variant: &ShopifyVariant, + base_url: &str, + scrape_request: &CrawlRequest, +) -> Result { + let image_urls: Vec = product.images.iter().map(|img| img.src.clone()).collect(); + + let link = format!( + "{}/products/{}?variant={}", + base_url, product.handle, variant.id + ); + + let chunk_html = format!( + "

    {} - {}

    {}", + product.title, variant.title, product.body_html + ); + + let semantic_boost_phrase = product.title.clone(); + let fulltext_boost_phrase = product.title.clone(); + + Ok(ChunkReqPayload { + chunk_html: Some(chunk_html), + link: Some(link), + tag_set: Some(product.tags.clone()), + num_value: variant.price.parse().ok(), + metadata: serde_json::to_value(product.clone()).ok(), + tracking_id: Some(variant.id.to_string()), + image_urls: Some(image_urls), + fulltext_boost: if scrape_request.crawl_options.boost_titles.unwrap_or(true) { + Some(FullTextBoost { + phrase: fulltext_boost_phrase, + boost_factor: 1.3, + }) + } else { + None + }, + semantic_boost: if scrape_request.crawl_options.boost_titles.unwrap_or(true) { + Some(SemanticBoost { + phrase: semantic_boost_phrase, + distance_factor: 0.3, + }) + } else { + None + }, + convert_html_to_text: Some(true), + ..Default::default() + }) +} + +async fn get_chunks_from_shopify( + scrape_request: CrawlRequest, +) -> Result<(Vec, usize), ServiceError> { + let mut chunks: Vec = Vec::new(); + let mut cur_page = 1; + + loop { + let url = format!("{}/products.json?page={}", scrape_request.url, cur_page); + let response: ShopifyResponse = ureq::get(&url) + .call() + .map_err(|e| ServiceError::InternalServerError(format!("Failed to fetch: {}", e)))? + .into_json() + .map_err(|e| { + ServiceError::InternalServerError(format!("Failed to parse JSON: {}", e)) + })?; + if response.products.is_empty() { + break; + } + + for product in response.products { + if product.variants.len() == 1 { + chunks.push(create_chunk_req_payload( + &product, + &product.variants[0], + &scrape_request.url, + &scrape_request, + )?); + } else { + for variant in &product.variants { + chunks.push(create_chunk_req_payload( + &product, + variant, + &scrape_request.url, + &scrape_request, + )?); + } + } + } + + cur_page += 1; + } + + Ok((chunks, cur_page)) +} + #[allow(clippy::print_stdout)] -async fn crawl( +async fn get_chunks_with_firecrawl( scrape_request: CrawlRequest, pool: web::Data, - redis_pool: web::Data, -) -> Result { +) -> Result<(Vec, usize), ServiceError> { let mut chunks = vec![]; let crawl_options = scrape_request.crawl_options.openapi_options.clone(); let mut spec = None; @@ -384,6 +508,15 @@ async fn crawl( } } + Ok((chunks, page_count)) +} + +#[allow(clippy::print_stdout)] +async fn crawl( + scrape_request: CrawlRequest, + pool: web::Data, + redis_pool: web::Data, +) -> Result { let dataset = get_dataset_by_id_query( trieve_server::data::models::UnifiedId::TrieveUuid(scrape_request.dataset_id), pool.clone(), @@ -396,6 +529,12 @@ async fn crawl( let dataset_config = DatasetConfiguration::from_json(dataset.server_configuration.clone()); + // Use shopify specific logic to get chunks or firecrawl + let (chunks, page_count) = match scrape_request.crawl_options.is_shopify { + Some(true) => get_chunks_from_shopify(scrape_request.clone()).await?, + _ => get_chunks_with_firecrawl(scrape_request.clone(), pool.clone()).await?, + }; + let chunks_to_upload = chunks.chunks(120); for chunk in chunks_to_upload { diff --git a/server/src/data/models.rs b/server/src/data/models.rs index 8754a8291f..7334fbf8b6 100644 --- a/server/src/data/models.rs +++ b/server/src/data/models.rs @@ -6542,6 +6542,7 @@ pub struct CrawlOptions { pub boost_titles: Option, /// Options for including an openapi spec in the crawl pub openapi_options: Option, + pub is_shopify: Option, } impl CrawlOptions { @@ -6557,6 +6558,7 @@ impl CrawlOptions { max_depth: self.max_depth.or(other.max_depth), boost_titles: self.boost_titles.or(other.boost_titles), openapi_options: self.openapi_options.clone(), + is_shopify: self.is_shopify.or(other.is_shopify), } } } From 745d4ea72a593354299af95dd1984fa7c17225dd Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Tue, 8 Oct 2024 17:52:38 -0500 Subject: [PATCH 047/159] feat: add shopify option to frontend --- clients/ts-sdk/openapi.json | 240 ++++++++++++------ clients/ts-sdk/src/types.gen.ts | 1 + .../src/pages/dataset/CrawlingSettings.tsx | 36 ++- frontends/shared/ui/MultiStringInput.tsx | 4 + 4 files changed, 200 insertions(+), 81 deletions(-) diff --git a/clients/ts-sdk/openapi.json b/clients/ts-sdk/openapi.json index 3a419e2e21..22a9f07a9e 100644 --- a/clients/ts-sdk/openapi.json +++ b/clients/ts-sdk/openapi.json @@ -40,7 +40,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -143,7 +144,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -197,7 +199,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -258,7 +261,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -317,7 +321,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -371,7 +376,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -432,7 +438,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -491,7 +498,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -545,7 +553,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -606,7 +615,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -813,7 +823,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -892,7 +903,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -946,7 +958,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -1021,7 +1034,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -1082,7 +1096,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -1152,7 +1167,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -1227,7 +1243,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -1302,7 +1319,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -1363,7 +1381,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -1418,7 +1437,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -1499,7 +1519,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -1551,7 +1572,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -1633,7 +1655,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -1686,7 +1709,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -1755,7 +1779,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -1809,7 +1834,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -1871,7 +1897,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -1951,7 +1978,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -2015,7 +2043,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2090,7 +2119,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2165,7 +2195,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2240,7 +2271,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2334,7 +2366,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2401,7 +2434,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2462,7 +2496,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2525,7 +2560,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2586,7 +2622,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2654,7 +2691,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2716,7 +2754,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2812,7 +2851,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -2900,7 +2940,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -2961,7 +3002,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -3039,7 +3081,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -3098,7 +3141,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -3169,7 +3213,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -3232,7 +3277,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -3302,7 +3348,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -3376,7 +3423,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -3437,7 +3485,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -3507,7 +3556,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -3602,7 +3652,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -3664,7 +3715,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -3734,7 +3786,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -3802,7 +3855,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -3865,7 +3919,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -3926,7 +3981,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -3987,7 +4043,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -4055,7 +4112,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -4142,7 +4200,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -4196,7 +4255,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -4249,7 +4309,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -4312,7 +4373,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -4380,7 +4442,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -4441,7 +4504,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -4510,7 +4574,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -4580,7 +4645,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -4691,7 +4757,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -4752,7 +4819,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -4806,7 +4874,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -4866,7 +4935,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -4929,7 +4999,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -4997,7 +5068,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -5237,7 +5309,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -5290,7 +5363,8 @@ "description": "The organization id to use for the request", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -5353,7 +5427,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -5412,7 +5487,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -5536,7 +5612,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -5589,7 +5666,8 @@ "description": "The dataset id or tracking_id to use for the request. We assume you intend to use an id if the value is a valid uuid.", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } }, { @@ -7268,6 +7346,7 @@ }, "ContentChunkMetadata": { "type": "object", + "title": "ContentChunkMetadata", "required": [ "id", "weight" @@ -7465,6 +7544,10 @@ ], "nullable": true }, + "is_shopify": { + "type": "boolean", + "nullable": true + }, "limit": { "type": "integer", "format": "int32", @@ -12932,6 +13015,7 @@ }, "SlimChunkMetadataWithArrayTagSet": { "type": "object", + "title": "SlimChunkMetadataWithArrayTagSet", "required": [ "id", "created_at", diff --git a/clients/ts-sdk/src/types.gen.ts b/clients/ts-sdk/src/types.gen.ts index e1819a33be..4086ed63b9 100644 --- a/clients/ts-sdk/src/types.gen.ts +++ b/clients/ts-sdk/src/types.gen.ts @@ -454,6 +454,7 @@ export type CrawlOptions = { */ include_tags?: Array<(string)> | null; interval?: ((CrawlInterval) | null); + is_shopify?: (boolean) | null; /** * How many pages to crawl, defaults to 1000 */ diff --git a/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx b/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx index f80c943718..8437f8de54 100644 --- a/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx +++ b/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx @@ -1,5 +1,5 @@ import { createMutation, createQuery } from "@tanstack/solid-query"; -import { Show, useContext } from "solid-js"; +import { Show, useContext, createMemo } from "solid-js"; import { DatasetContext } from "../../contexts/DatasetContext"; import { useTrieve } from "../../hooks/useTrieve"; import { @@ -14,6 +14,7 @@ import { Spacer } from "../../components/Spacer"; import { UserContext } from "../../contexts/UserContext"; import { createToast } from "../../components/ShowToasts"; import { ValidateFn } from "../../utils/validation"; +import { cn } from "shared/utils"; const defaultCrawlOptions: CrawlOptions = { boost_titles: false, @@ -26,6 +27,7 @@ const defaultCrawlOptions: CrawlOptions = { max_depth: 10, site_url: "", openapi_options: null, + is_shopify: false, }; const normalizeOpenAPIOptions = ( @@ -127,6 +129,8 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { ReturnType>["errors"] >({}); + const isShopify = createMemo(() => !!options.is_shopify); + const validate: ValidateFn = (value) => { const errors: Record = {}; if (!value.site_url) { @@ -209,17 +213,31 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => {
    { + setOptions("boost_titles", e.currentTarget.checked); + }} + class="h-4 w-4 rounded border border-neutral-300 bg-neutral-100 p-1 accent-magenta-400 dark:border-neutral-900 dark:bg-neutral-800" + type="checkbox" + /> + + { + setOptions("is_shopify", e.currentTarget.checked); + }} + checked={options.is_shopify || false} class="h-4 w-4 rounded border border-neutral-300 bg-neutral-100 p-1 accent-magenta-400 dark:border-neutral-900 dark:bg-neutral-800" type="checkbox" />
    -
    +
    { setOptions("limit", parseInt(e.currentTarget.value)); @@ -234,6 +252,7 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { Max Depth { setOptions("max_depth", parseInt(e.currentTarget.value)); @@ -248,6 +267,7 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { OpenAPI Schema URL { @@ -269,6 +289,7 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { OpenAPI Tag { if (!options.openapi_options) { @@ -284,10 +305,16 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { />
    -
    +
    Include Paths
    {
    Exclude Paths
    {
    Include Tags
    {
    Exclude Tags
    { @@ -44,6 +45,7 @@ export const MultiStringInput = (props: MultiStringInputProps) => { {(entry) => (
    { @@ -56,6 +58,7 @@ export const MultiStringInput = (props: MultiStringInputProps) => { />
    - + { } }; + createEffect(() => { + console.log(crawlOptions()); + }); + return ( { />
    -
    -
    + > +
    + + component + crawlOptions()?.scrape_options + ?.openapi_schema_url ?? "" + } + onInput={(e) => + setCrawlOptions((prev) => { + if (!prev) { + return { + scrape_options: { + type: "openapi", + openapi_schema_url: + e.currentTarget.value, + openapi_tag: "", + }, + }; + } + + return { + ...prev, + scrape_options: { + type: "openapi", + openapi_schema_url: + e.currentTarget.value, + openapi_tag: + prev.scrape_options?.openapi_tag ?? + "", + }, + }; + }) + } + /> +
    + +
    + + + setCrawlOptions((prev) => { + if (!prev) { + return { + scrape_options: { + type: "openapi", + openapi_schema_url: "", + openapi_tag: e.currentTarget.value, + }, + }; + } + + return { + ...prev, + scrape_options: { + type: "openapi", + openapi_schema_url: + prev.scrape_options + ?.openapi_schema_url ?? "", + openapi_tag: e.currentTarget.value, + }, + }; + }) + } + /> +
    + +
    diff --git a/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx b/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx index 8437f8de54..2db29508d3 100644 --- a/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx +++ b/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx @@ -1,5 +1,5 @@ import { createMutation, createQuery } from "@tanstack/solid-query"; -import { Show, useContext, createMemo } from "solid-js"; +import { Show, useContext, createMemo, createEffect } from "solid-js"; import { DatasetContext } from "../../contexts/DatasetContext"; import { useTrieve } from "../../hooks/useTrieve"; import { @@ -26,14 +26,13 @@ const defaultCrawlOptions: CrawlOptions = { limit: 1000, max_depth: 10, site_url: "", - openapi_options: null, - is_shopify: false, + scrape_options: {}, }; const normalizeOpenAPIOptions = ( options: CrawlOpenAPIOptions | null | undefined, ) => { - if (options) { + if (options && options.type == "openapi") { if (options.openapi_schema_url === "") { return null; } @@ -41,6 +40,8 @@ const normalizeOpenAPIOptions = ( return null; } return options; + } else if (options && options.type == "shopify") { + return options; } return null; }; @@ -71,7 +72,7 @@ export const CrawlingSettings = () => { data: { crawl_options: { ...options, - openapi_options: normalizeOpenAPIOptions(options.openapi_options), + scrape_options: normalizeOpenAPIOptions(options.scrape_options), }, dataset_id: datasetId(), }, @@ -129,7 +130,20 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { ReturnType>["errors"] >({}); - const isShopify = createMemo(() => !!options.is_shopify); + const isShopify = createMemo( + () => + options.scrape_options != {} && + options.scrape_options?.type === "shopify", + ); + const isOpenAPI = createMemo( + () => + options.scrape_options?.type != null && + options.scrape_options?.type === "openapi", + ); + + createEffect(() => { + console.log(isOpenAPI()); + }); const validate: ValidateFn = (value) => { const errors: Record = {}; @@ -148,10 +162,10 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { errors.max_depth = "Max depth must be greater than 0"; } if ( - value.openapi_options?.openapi_tag && - !value.openapi_options.openapi_schema_url + value.scrape_options?.openapi_tag && + !value.scrape_options.openapi_schema_url ) { - errors.openapi_options = "OpenAPI Schema URL is required for tag"; + errors.scrape_options = "OpenAPI Schema URL is required for tag"; } return { @@ -220,12 +234,31 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { class="h-4 w-4 rounded border border-neutral-300 bg-neutral-100 p-1 accent-magenta-400 dark:border-neutral-900 dark:bg-neutral-800" type="checkbox" /> - + + { + if (e.currentTarget.checked) { + setOptions("scrape_options", "type", "shopify"); + } else { + setOptions("scrape_options", "type", {}); + setOptions("scrape_options", {}); + } + }} + checked={isShopify()} + class="h-4 w-4 rounded border border-neutral-300 bg-neutral-100 p-1 accent-magenta-400 dark:border-neutral-900 dark:bg-neutral-800" + type="checkbox" + /> + { - setOptions("is_shopify", e.currentTarget.checked); + if (e.currentTarget.checked) { + setOptions("scrape_options", "type", "openapi"); + } else { + setOptions("scrape_options", "type", {}); + setOptions("scrape_options", {}); + } }} - checked={options.is_shopify || false} + checked={isOpenAPI()} class="h-4 w-4 rounded border border-neutral-300 bg-neutral-100 p-1 accent-magenta-400 dark:border-neutral-900 dark:bg-neutral-800" type="checkbox" /> @@ -267,36 +300,36 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { OpenAPI Schema URL { - if (!options.openapi_options) { - setOptions("openapi_options", {}); + if (!options.scrape_options) { + setOptions("scrape_options", {}); } setOptions( - "openapi_options", + "scrape_options", "openapi_schema_url", e.currentTarget.value, ); }} class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 focus:outline-magenta-500 sm:text-sm sm:leading-6" /> - +
    { - if (!options.openapi_options) { - setOptions("openapi_options", {}); + if (!options.scrape_options) { + setOptions("scrape_options", { type: "openapi" }); } setOptions( - "openapi_options", + "scrape_options", "openapi_tag", e.currentTarget.value, ); diff --git a/server/src/bin/crawl-worker.rs b/server/src/bin/crawl-worker.rs index 0bb37a468a..1d1fafdd77 100644 --- a/server/src/bin/crawl-worker.rs +++ b/server/src/bin/crawl-worker.rs @@ -17,7 +17,7 @@ use trieve_server::{ }, }; use trieve_server::{ - data::models::{CrawlRequest, DatasetConfiguration, RedisPool}, + data::models::{CrawlRequest, DatasetConfiguration, RedisPool, ScrapeOptions}, operators::crawl_operator::{get_crawl_from_firecrawl, Status}, }; use trieve_server::{ @@ -172,10 +172,9 @@ async fn get_chunks_with_firecrawl( pool: web::Data, ) -> Result<(Vec, usize), ServiceError> { let mut chunks = vec![]; - let crawl_options = scrape_request.crawl_options.openapi_options.clone(); let mut spec = None; - if let Some(openapi_options) = scrape_request.crawl_options.openapi_options.clone() { + if let Some(ScrapeOptions::OpenApi(openapi_options)) = scrape_request.crawl_options.scrape_options.clone() { let client = reqwest::Client::new(); let schema = match client @@ -287,9 +286,9 @@ async fn get_chunks_with_firecrawl( let page_html = page.html.clone().unwrap_or_default(); let page_tags = get_tags(page_link.clone()); - if let Some(crawl_options) = &crawl_options { - if let Some(spec) = &spec { - if page_tags.contains(&crawl_options.openapi_tag) { + if let Some(spec) = &spec { + if let Some(ScrapeOptions::OpenApi(ref openapi_options)) = scrape_request.crawl_options.scrape_options { + if page_tags.contains(&openapi_options.openapi_tag) { if let Some(last_tag) = page_tags.last() { // try to find a operation in the spec with an operation_id that matches the last tag directly, with - replaced by _ or vice versa let operation = spec.operations().find(|(_, _, operation)| { @@ -530,8 +529,8 @@ async fn crawl( let dataset_config = DatasetConfiguration::from_json(dataset.server_configuration.clone()); // Use shopify specific logic to get chunks or firecrawl - let (chunks, page_count) = match scrape_request.crawl_options.is_shopify { - Some(true) => get_chunks_from_shopify(scrape_request.clone()).await?, + let (chunks, page_count) = match scrape_request.crawl_options.scrape_options { + Some(ScrapeOptions::Shopify(_)) => get_chunks_from_shopify(scrape_request.clone()).await?, _ => get_chunks_with_firecrawl(scrape_request.clone(), pool.clone()).await?, }; diff --git a/server/src/data/models.rs b/server/src/data/models.rs index 7334fbf8b6..6210a59dc2 100644 --- a/server/src/data/models.rs +++ b/server/src/data/models.rs @@ -6541,8 +6541,25 @@ pub struct CrawlOptions { /// Boost titles such that keyword matches in titles are prioritized in search results. Strongly recommended to leave this on. Defaults to true. pub boost_titles: Option, /// Options for including an openapi spec in the crawl - pub openapi_options: Option, - pub is_shopify: Option, + pub scrape_options: Option +} + +#[derive(Serialize, Deserialize, Debug, ToSchema, Clone)] +#[serde(tag = "type")] +/// Options for including an openapi spec or shopify settigns +pub enum ScrapeOptions { + /// OpenAPI Scrape Options + #[serde(rename = "openapi")] + OpenApi(CrawlOpenAPIOptions), + /// Shopify Scrape Options + #[serde(rename = "shopify")] + Shopify(CrawlShopifyOptions) +} + +#[derive(Serialize, Deserialize, Debug, ToSchema, Clone)] +#[schema(title = "CrawlShopifyOptions")] +pub struct CrawlShopifyOptions { + pub boost_item_names: Option, } impl CrawlOptions { @@ -6557,8 +6574,7 @@ impl CrawlOptions { exclude_paths: self.exclude_paths.clone().or(other.exclude_paths.clone()), max_depth: self.max_depth.or(other.max_depth), boost_titles: self.boost_titles.or(other.boost_titles), - openapi_options: self.openapi_options.clone(), - is_shopify: self.is_shopify.or(other.is_shopify), + scrape_options: self.scrape_options.clone(), } } } diff --git a/server/src/handlers/chunk_handler.rs b/server/src/handlers/chunk_handler.rs index 8988a0210a..a8a44760c0 100644 --- a/server/src/handlers/chunk_handler.rs +++ b/server/src/handlers/chunk_handler.rs @@ -2725,6 +2725,7 @@ pub fn check_completion_param_validity( #[derive(Serialize, Deserialize, Debug, ToSchema, Clone)] /// Options for including an openapi spec in the crawl +#[schema(title = "CrawlOpenAPIOptions")] pub struct CrawlOpenAPIOptions { /// OpenAPI json schema to be processed alongside the site crawl pub openapi_schema_url: String, diff --git a/server/src/lib.rs b/server/src/lib.rs index 51f4283a92..49e65fe5e2 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -342,6 +342,8 @@ impl Modify for SecurityAddon { data::models::DatasetConfigurationDTO, handlers::chunk_handler::CrawlOpenAPIOptions, handlers::chunk_handler::CrawlInterval, + data::models::ScrapeOptions, + data::models::CrawlShopifyOptions, handlers::analytics_handler::GetTopDatasetsRequestBody, handlers::analytics_handler::CTRDataRequestBody, data::models::CTRType, diff --git a/server/src/operators/crawl_operator.rs b/server/src/operators/crawl_operator.rs index 5fde3fd575..308067881f 100644 --- a/server/src/operators/crawl_operator.rs +++ b/server/src/operators/crawl_operator.rs @@ -4,7 +4,7 @@ use crate::data::models::FirecrawlCrawlRequest; use crate::data::models::RedisPool; use crate::handlers::chunk_handler::CrawlInterval; use crate::{ - data::models::{CrawlRequest, CrawlRequestPG, Pool}, + data::models::{CrawlRequest, CrawlRequestPG, ScrapeOptions, CrawlShopifyOptions, Pool}, errors::ServiceError, }; use actix_web::web; @@ -127,7 +127,7 @@ pub async fn crawl( redis_pool: web::Data, dataset_id: uuid::Uuid, ) -> Result { - let scrape_id = if crawl_options.is_shopify.unwrap_or(false) { + let scrape_id = if let Some(ScrapeOptions::Shopify(_)) = crawl_options.scrape_options { uuid::Uuid::nil() } else { crawl_site(crawl_options.clone()) @@ -339,7 +339,7 @@ pub async fn update_crawl_settings_for_dataset( .await .map_err(|e| ServiceError::InternalServerError(e.to_string()))?; - let crawl_req = crawl_requests_table::crawl_requests + let prev_crawl_req = crawl_requests_table::crawl_requests .select(( crawl_requests_table::id, crawl_requests_table::url, @@ -353,7 +353,8 @@ pub async fn update_crawl_settings_for_dataset( )) .filter(crawl_requests_table::dataset_id.eq(dataset_id)) .first::(&mut conn) - .await; + .await + .optional()?; if let Some(ref url) = crawl_options.site_url { diesel::update( @@ -382,14 +383,13 @@ pub async fn update_crawl_settings_for_dataset( .map_err(|e| ServiceError::InternalServerError(e.to_string()))?; } - let previous_crawl_options: CrawlOptions = serde_json::from_value( - crawl_req - .map_err(|e| ServiceError::InternalServerError(e.to_string()))? - .crawl_options, - ) - .map_err(|e| ServiceError::InternalServerError(e.to_string()))?; + let merged_options = if let Some(prev_crawl_req) = prev_crawl_req { + let previous_crawl_options: CrawlOptions = serde_json::from_value(prev_crawl_req.crawl_options).map_err(|e| ServiceError::InternalServerError(e.to_string()))?; + crawl_options.merge(previous_crawl_options) + } else { + crawl_options + }; - let merged_options = crawl_options.merge(previous_crawl_options); diesel::update( crawl_requests_table::crawl_requests @@ -405,7 +405,7 @@ pub async fn update_crawl_settings_for_dataset( .map_err(|e| ServiceError::InternalServerError(e.to_string()))?; crawl( - crawl_options.clone(), + merged_options.clone(), pool.clone(), redis_pool.clone(), dataset_id, From 6b2c7d50b11c8184aba6dc4960c3b5fc0a0de449 Mon Sep 17 00:00:00 2001 From: cdxker Date: Wed, 9 Oct 2024 20:11:07 -0700 Subject: [PATCH 050/159] feature: added route to the backend for group_variants --- server/src/bin/crawl-worker.rs | 34 ++++++++++++++++++++++++++++++---- server/src/data/models.rs | 2 +- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/server/src/bin/crawl-worker.rs b/server/src/bin/crawl-worker.rs index 1d1fafdd77..cd2733faff 100644 --- a/server/src/bin/crawl-worker.rs +++ b/server/src/bin/crawl-worker.rs @@ -17,7 +17,7 @@ use trieve_server::{ }, }; use trieve_server::{ - data::models::{CrawlRequest, DatasetConfiguration, RedisPool, ScrapeOptions}, + data::models::{CrawlRequest, DatasetConfiguration, RedisPool, ScrapeOptions, CrawlShopifyOptions}, operators::crawl_operator::{get_crawl_from_firecrawl, Status}, }; use trieve_server::{ @@ -89,8 +89,23 @@ fn create_chunk_req_payload( product.title, variant.title, product.body_html ); - let semantic_boost_phrase = product.title.clone(); - let fulltext_boost_phrase = product.title.clone(); + let group_variants = if let Some(ScrapeOptions::Shopify(CrawlShopifyOptions{ group_variants: Some(group_variants), .. })) = scrape_request.crawl_options.scrape_options { + group_variants + } else { + true + }; + + let semantic_boost_phrase = if group_variants { + variant.title.clone() + } else { + product.title.clone() + }; + + let fulltext_boost_phrase = if group_variants { + variant.title.clone() + } else { + product.title.clone() + }; Ok(ChunkReqPayload { chunk_html: Some(chunk_html), @@ -98,7 +113,18 @@ fn create_chunk_req_payload( tag_set: Some(product.tags.clone()), num_value: variant.price.parse().ok(), metadata: serde_json::to_value(product.clone()).ok(), - tracking_id: Some(variant.id.to_string()), + tracking_id: if group_variants { + Some(variant.id.to_string()) + } else { + Some(product.id.to_string()) + }, + group_tracking_ids: if group_variants { + Some(vec![ + product.id.to_string() + ]) + } else { + None + }, image_urls: Some(image_urls), fulltext_boost: if scrape_request.crawl_options.boost_titles.unwrap_or(true) { Some(FullTextBoost { diff --git a/server/src/data/models.rs b/server/src/data/models.rs index 6210a59dc2..1b01028bce 100644 --- a/server/src/data/models.rs +++ b/server/src/data/models.rs @@ -6559,7 +6559,7 @@ pub enum ScrapeOptions { #[derive(Serialize, Deserialize, Debug, ToSchema, Clone)] #[schema(title = "CrawlShopifyOptions")] pub struct CrawlShopifyOptions { - pub boost_item_names: Option, + pub group_variants: Option, } impl CrawlOptions { From 070a6a50ed1c445b104fcf05d32b935fd1ed03d2 Mon Sep 17 00:00:00 2001 From: cdxker Date: Wed, 9 Oct 2024 21:04:33 -0700 Subject: [PATCH 051/159] feature: add group variants flag --- .../src/components/NewDatasetModal.tsx | 53 +++++++++++++++++++ .../src/pages/dataset/CrawlingSettings.tsx | 25 +++++---- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/frontends/dashboard/src/components/NewDatasetModal.tsx b/frontends/dashboard/src/components/NewDatasetModal.tsx index b9e926848e..ddb1828558 100644 --- a/frontends/dashboard/src/components/NewDatasetModal.tsx +++ b/frontends/dashboard/src/components/NewDatasetModal.tsx @@ -1049,6 +1049,59 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { />
    + +
    + + + setCrawlOptions((prev) => { + if (!prev) { + console.log(e.currentTarget.value); + return { + scrape_options: { + type: "shopify", + group_variants: e.currentTarget.checked, + }, + }; + } + + return { + ...prev, + scrape_options: { + type: "shopify", + group_variants: e.currentTarget.checked, + }, + }; + }) + } + /> +
    +
    diff --git a/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx b/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx index 2db29508d3..64733c4db3 100644 --- a/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx +++ b/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx @@ -155,17 +155,20 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { errors.site_url = "Invalid Site URL - http(s):// required"; } - if (!value.limit || value.limit <= 0) { - errors.limit = "Limit must be greater than 0"; - } - if (!value.max_depth) { - errors.max_depth = "Max depth must be greater than 0"; - } - if ( - value.scrape_options?.openapi_tag && - !value.scrape_options.openapi_schema_url - ) { - errors.scrape_options = "OpenAPI Schema URL is required for tag"; + if (value.scrape_options?.type != "shopify") { + if (!value.limit || value.limit <= 0) { + errors.limit = "Limit must be greater than 0"; + } + if (!value.max_depth) { + errors.max_depth = "Max depth must be greater than 0"; + } + if ( + value.scrape_options?.type == "openapi" && + value.scrape_options?.openapi_tag && + !value.scrape_options.openapi_schema_url + ) { + errors.scrape_options = "OpenAPI Schema URL is required for tag"; + } } return { From 8aa928e639fdd878873cc4180e9ac6eca90f0a9c Mon Sep 17 00:00:00 2001 From: cdxker Date: Wed, 9 Oct 2024 21:09:46 -0700 Subject: [PATCH 052/159] docs: add more documentation of group_variants and CrawlShopifyOptions --- clients/ts-sdk/openapi.json | 4 +- clients/ts-sdk/src/types.gen.ts | 8 +- .../src/components/NewDatasetModal.tsx | 1187 ++++++++--------- .../src/pages/dataset/CrawlingSettings.tsx | 320 +++-- frontends/dashboard/src/utils/validation.tsx | 13 +- server/src/bin/crawl-worker.rs | 32 +- server/src/data/models.rs | 6 +- server/src/operators/crawl_operator.rs | 9 +- 8 files changed, 793 insertions(+), 786 deletions(-) diff --git a/clients/ts-sdk/openapi.json b/clients/ts-sdk/openapi.json index 4b4bf5a4f9..7ce4059cb3 100644 --- a/clients/ts-sdk/openapi.json +++ b/clients/ts-sdk/openapi.json @@ -7597,9 +7597,11 @@ "CrawlShopifyOptions": { "type": "object", "title": "CrawlShopifyOptions", + "description": "Options for Crawling Shopify", "properties": { - "boost_item_names": { + "group_variants": { "type": "boolean", + "description": "This option will ingest all variants as individual chunks and place them in groups by product id. Turning this off will only scrape 1 variant per product. default: true", "nullable": true } } diff --git a/clients/ts-sdk/src/types.gen.ts b/clients/ts-sdk/src/types.gen.ts index f5af8a719a..c825461441 100644 --- a/clients/ts-sdk/src/types.gen.ts +++ b/clients/ts-sdk/src/types.gen.ts @@ -469,8 +469,14 @@ export type CrawlOptions = { site_url?: (string) | null; }; +/** + * Options for Crawling Shopify + */ export type CrawlShopifyOptions = { - boost_item_names?: (boolean) | null; + /** + * This option will ingest all variants as individual chunks and place them in groups by product id. Turning this off will only scrape 1 variant per product. default: true + */ + group_variants?: (boolean) | null; }; export type CreateBatchChunkGroupReqPayload = Array; diff --git a/frontends/dashboard/src/components/NewDatasetModal.tsx b/frontends/dashboard/src/components/NewDatasetModal.tsx index ddb1828558..2f9a98081c 100644 --- a/frontends/dashboard/src/components/NewDatasetModal.tsx +++ b/frontends/dashboard/src/components/NewDatasetModal.tsx @@ -1,7 +1,6 @@ import { Accessor, createSignal, - createEffect, useContext, For, Switch, @@ -26,14 +25,21 @@ import { createToast } from "./ShowToasts"; import { createNewDataset } from "../api/createDataset"; import { uploadSampleData } from "../api/uploadSampleData"; import { defaultServerEnvsConfiguration } from "../utils/serverEnvs"; -import { CrawlInterval, CrawlOptions, DistanceMetric } from "trieve-ts-sdk"; +import { CrawlInterval, DistanceMetric } from "trieve-ts-sdk"; import { FaRegularCircleQuestion } from "solid-icons/fa"; import { Tooltip } from "shared/ui"; import { FiChevronDown, FiChevronUp } from "solid-icons/fi"; import { createStore, SetStoreFunction, unwrap } from "solid-js/store"; import { DatasetConfig } from "./dataset-settings/LegacySettingsWrapper"; import { cn } from "shared/utils"; -import { ValidateFn, ErrorMsg } from "../utils/validation"; +import { ValidateFn, ErrorMsg, ValidateErrors } from "../utils/validation"; +import { + defaultCrawlOptions, + FlatCrawlOptions, + flattenCrawlOptions, + unflattenCrawlOptions, + validateFlatCrawlOptions, +} from "../pages/dataset/CrawlingSettings"; export interface NewDatasetModalProps { isOpen: Accessor; @@ -41,7 +47,7 @@ export interface NewDatasetModalProps { } const validate: ValidateFn = (value) => { - const errors: Record = {}; + const errors: ValidateErrors = {}; if (value.BM25_ENABLED) { if (!value.BM25_B) { @@ -74,7 +80,9 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { const [serverConfig, setServerConfig] = createStore( defaultServerEnvsConfiguration, ); - const [crawlOptions, setCrawlOptions] = createSignal(); + const [crawlOptions, setCrawlOptions] = createStore( + flattenCrawlOptions(defaultCrawlOptions), + ); const [name, setName] = createSignal(""); const [showAdvanced, setShowAdvanced] = createSignal(false); const [showScraping, setShowScraping] = createSignal(false); @@ -84,9 +92,13 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { const [errors, setErrors] = createStore< ReturnType>["errors"] >({}); + const [crawlErrors, setCrawlErrors] = createStore< + ReturnType>["errors"] + >({}); const createDataset = async () => { const curServerConfig = unwrap(serverConfig); + const unwrappedFlatCrawlOptions = unwrap(crawlOptions); const validateResult = validate(curServerConfig); if (validateResult.valid) { setErrors({}); @@ -95,13 +107,27 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { return; } + if (showScraping()) { + const crawlValidateResult = validateFlatCrawlOptions( + unwrappedFlatCrawlOptions, + ); + if (crawlValidateResult.valid) { + setCrawlErrors({}); + } else { + setCrawlErrors(crawlValidateResult.errors); + return; + } + } + try { setIsLoading(true); const dataset = await createNewDataset({ name: name(), organizationId: userContext.selectedOrg().id, serverConfig: curServerConfig, - crawlOptions: crawlOptions(), + crawlOptions: showScraping() + ? unflattenCrawlOptions(unwrappedFlatCrawlOptions) + : undefined, }); if (fillWithExampleData()) { @@ -133,10 +159,6 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { } }; - createEffect(() => { - console.log(crawlOptions()); - }); - return ( { > - + - + Scraping @@ -485,625 +515,11 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { /> -
    -
    - - - setCrawlOptions((prev) => { - if (!prev) { - return { - site_url: e.currentTarget.value, - }; - } - - return { - ...prev, - site_url: e.currentTarget.value, - }; - }) - } - /> -
    -
    - - - setCrawlOptions((prev) => { - if (!prev) { - return { - exclude_paths: - e.currentTarget.value.split(","), - }; - } - - return { - ...prev, - exclude_paths: - e.currentTarget.value.split(","), - }; - }) - } - /> -
    - -
    - - - setCrawlOptions((prev) => { - if (!prev) { - return { - include_paths: - e.currentTarget.value.split(","), - }; - } - - return { - ...prev, - include_paths: - e.currentTarget.value.split(","), - }; - }) - } - /> -
    - -
    - - - setCrawlOptions((prev) => { - if (!prev) { - return { - exclude_tags: - e.currentTarget.value.split(","), - }; - } - - return { - ...prev, - exclude_tags: - e.currentTarget.value.split(","), - }; - }) - } - /> -
    - -
    - - - setCrawlOptions((prev) => { - if (!prev) { - return { - include_tags: - e.currentTarget.value.split(","), - }; - } - - return { - ...prev, - include_tags: - e.currentTarget.value.split(","), - }; - }) - } - /> -
    - -
    - - - setCrawlOptions((prev) => { - if (!prev) { - return { - max_depth: parseInt( - e.currentTarget.value, - ), - }; - } - - return { - ...prev, - max_depth: parseInt(e.currentTarget.value), - }; - }) - } - /> -
    - -
    - - - setCrawlOptions((prev) => { - if (!prev) { - return { - limit: parseInt(e.currentTarget.value), - }; - } - - return { - ...prev, - limit: parseInt(e.currentTarget.value), - }; - }) - } - /> -
    - -
    - - -
    - -
    - - - setCrawlOptions((prev) => { - if (!prev) { - return { - boost_titles: e.currentTarget.checked, - }; - } - - return { - ...prev, - boost_titles: e.currentTarget.checked, - }; - }) - } - /> -
    - -
    -
    - - setCrawlOptions((prev) => { - if (!prev) { - return { - scrape_options: { - type: "openapi", - }, - }; - } - if (!e.currentTarget.checked) { - return { - ...prev, - scrape_options: null, - }; - } else { - return { - ...prev, - scrape_options: { - type: "openapi", - }, - }; - } - }) - } - /> - - -
    -
    - - setCrawlOptions((prev) => { - if (!prev) { - return { - scrape_options: { - type: "shopify", - }, - }; - } - - if (!e.currentTarget.checked) { - return { - ...prev, - scrape_options: null, - }; - } else { - return { - ...prev, - scrape_options: { - type: "shopify", - }, - }; - } - }) - } - /> - - -
    -
    - - - -
    - - component - crawlOptions()?.scrape_options - ?.openapi_schema_url ?? "" - } - onInput={(e) => - setCrawlOptions((prev) => { - if (!prev) { - return { - scrape_options: { - type: "openapi", - openapi_schema_url: - e.currentTarget.value, - openapi_tag: "", - }, - }; - } - - return { - ...prev, - scrape_options: { - type: "openapi", - openapi_schema_url: - e.currentTarget.value, - openapi_tag: - prev.scrape_options?.openapi_tag ?? - "", - }, - }; - }) - } - /> -
    - -
    - - - setCrawlOptions((prev) => { - if (!prev) { - return { - scrape_options: { - type: "openapi", - openapi_schema_url: "", - openapi_tag: e.currentTarget.value, - }, - }; - } - - return { - ...prev, - scrape_options: { - type: "openapi", - openapi_schema_url: - prev.scrape_options - ?.openapi_schema_url ?? "", - openapi_tag: e.currentTarget.value, - }, - }; - }) - } - /> -
    -
    - -
    - - - setCrawlOptions((prev) => { - if (!prev) { - console.log(e.currentTarget.value); - return { - scrape_options: { - type: "shopify", - group_variants: e.currentTarget.checked, - }, - }; - } - - return { - ...prev, - scrape_options: { - type: "shopify", - group_variants: e.currentTarget.checked, - }, - }; - }) - } - /> -
    -
    -
    -
    +
    @@ -1135,6 +551,515 @@ export const NewDatasetModal = (props: NewDatasetModalProps) => { }; export default NewDatasetModal; +const ScrapingSettings = (props: { + crawlOptions: FlatCrawlOptions; + setCrawlOptions: SetStoreFunction; + errors: ReturnType>["errors"]; +}) => { + return ( +
    +
    +
    + + + props.setCrawlOptions((prev) => { + if (!prev) { + return { + site_url: e.currentTarget.value, + }; + } + + return { + ...prev, + site_url: e.currentTarget.value, + }; + }) + } + /> +
    +
    + + + props.setCrawlOptions((prev) => { + if (!prev) { + return { + exclude_paths: e.currentTarget.value.split(","), + }; + } + + return { + ...prev, + exclude_paths: e.currentTarget.value.split(","), + }; + }) + } + /> +
    + +
    + + + props.setCrawlOptions((prev) => { + if (!prev) { + return { + include_paths: e.currentTarget.value.split(","), + }; + } + + return { + ...prev, + include_paths: e.currentTarget.value.split(","), + }; + }) + } + /> +
    + +
    + + + props.setCrawlOptions((prev) => { + if (!prev) { + return { + exclude_tags: e.currentTarget.value.split(","), + }; + } + return { + ...prev, + exclude_tags: e.currentTarget.value.split(","), + }; + }) + } + /> +
    + +
    + + + props.setCrawlOptions((prev) => { + if (!prev) { + return { + include_tags: e.currentTarget.value.split(","), + }; + } + + return { + ...prev, + include_tags: e.currentTarget.value.split(","), + }; + }) + } + /> +
    + +
    + + + props.setCrawlOptions((prev) => { + if (!prev) { + return { + max_depth: parseInt(e.currentTarget.value), + }; + } + + return { + ...prev, + max_depth: parseInt(e.currentTarget.value), + }; + }) + } + /> +
    + +
    + + + props.setCrawlOptions((prev) => { + if (!prev) { + return { + limit: parseInt(e.currentTarget.value), + }; + } + + return { + ...prev, + limit: parseInt(e.currentTarget.value), + }; + }) + } + /> +
    + +
    + + +
    + +
    + + + props.setCrawlOptions((prev) => { + if (!prev) { + return { + boost_titles: e.currentTarget.checked, + }; + } + + return { + ...prev, + boost_titles: e.currentTarget.checked, + }; + }) + } + /> +
    + +
    +
    + + props.setCrawlOptions((prev) => { + if (!e.currentTarget.checked) { + if (prev.type === "openapi") { + return { + ...prev, + type: undefined, + }; + } + return { + ...prev, + }; + } else { + return { + ...prev, + type: "openapi", + }; + } + }) + } + /> + + +
    +
    + + props.setCrawlOptions((prev) => { + if (!e.currentTarget.checked) { + if (prev.type === "shopify") { + return { + ...prev, + type: undefined, + }; + } + return { + ...prev, + }; + } else { + return { + type: "shopify" as const, + }; + } + }) + } + /> + + +
    +
    + + + +
    + + + props.setCrawlOptions((prev) => { + return { + ...prev, + type: "openapi", + openapi_schema_url: e.currentTarget.value, + }; + }) + } + /> +
    + +
    + + + props.setCrawlOptions((prev) => { + return { + ...prev, + type: "openapi", + openapi_tag: e.currentTarget.value, + }; + }) + } + /> +
    +
    + +
    + + + props.setCrawlOptions((prev) => { + return { + ...prev, + scrape_options: { + type: "shopify", + group_variants: e.currentTarget.checked, + }, + }; + }) + } + /> +
    +
    +
    +
    +
    + ); +}; + const BM25Settings = (props: { config: DatasetConfig; setConfig: SetStoreFunction; diff --git a/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx b/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx index 64733c4db3..1aed43fbc0 100644 --- a/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx +++ b/frontends/dashboard/src/pages/dataset/CrawlingSettings.tsx @@ -1,22 +1,18 @@ import { createMutation, createQuery } from "@tanstack/solid-query"; -import { Show, useContext, createMemo, createEffect } from "solid-js"; +import { Show, useContext, createMemo } from "solid-js"; import { DatasetContext } from "../../contexts/DatasetContext"; import { useTrieve } from "../../hooks/useTrieve"; -import { - CrawlInterval, - CrawlOpenAPIOptions, - CrawlOptions, -} from "trieve-ts-sdk"; +import { CrawlInterval, CrawlOptions } from "trieve-ts-sdk"; import { createStore } from "solid-js/store"; import { MultiStringInput, Select } from "shared/ui"; import { toTitleCase } from "../../analytics/utils/titleCase"; import { Spacer } from "../../components/Spacer"; import { UserContext } from "../../contexts/UserContext"; import { createToast } from "../../components/ShowToasts"; -import { ValidateFn } from "../../utils/validation"; +import { ErrorMsg, ValidateErrors, ValidateFn } from "../../utils/validation"; import { cn } from "shared/utils"; -const defaultCrawlOptions: CrawlOptions = { +export const defaultCrawlOptions: CrawlOptions = { boost_titles: false, exclude_paths: [], exclude_tags: [], @@ -26,24 +22,87 @@ const defaultCrawlOptions: CrawlOptions = { limit: 1000, max_depth: 10, site_url: "", - scrape_options: {}, + scrape_options: null, }; -const normalizeOpenAPIOptions = ( - options: CrawlOpenAPIOptions | null | undefined, -) => { +export type FlatCrawlOptions = Omit & { + type?: "openapi" | "shopify"; + openapi_schema_url?: string; + openapi_tag?: string; + group_variants?: boolean | null; +}; + +export const unflattenCrawlOptions = ( + options: FlatCrawlOptions, +): CrawlOptions => { if (options && options.type == "openapi") { - if (options.openapi_schema_url === "") { - return null; + if (!options.openapi_schema_url || !options.openapi_tag) { + return { + ...options, + scrape_options: null, + }; } - if (!options.openapi_tag && !options.openapi_schema_url) { - return null; - } - return options; + return { + boost_titles: options.boost_titles, + exclude_paths: options.exclude_paths, + exclude_tags: options.exclude_tags, + include_paths: options.include_paths, + include_tags: options.include_tags, + interval: options.interval, + limit: options.limit, + max_depth: options.max_depth, + site_url: options.site_url, + scrape_options: { + type: "openapi", + openapi_schema_url: options.openapi_schema_url, + openapi_tag: options.openapi_tag, + }, + }; } else if (options && options.type == "shopify") { - return options; + return { + boost_titles: options.boost_titles, + exclude_paths: options.exclude_paths, + exclude_tags: options.exclude_tags, + include_paths: options.include_paths, + include_tags: options.include_tags, + interval: options.interval, + limit: options.limit, + max_depth: options.max_depth, + site_url: options.site_url, + scrape_options: { + type: "shopify", + group_variants: options.group_variants, + }, + }; + } + return { + ...options, + scrape_options: null, + }; +}; + +export const flattenCrawlOptions = ( + options: CrawlOptions, +): FlatCrawlOptions => { + if (options.scrape_options?.type == "openapi") { + return { + ...options, + type: "openapi", + openapi_schema_url: options.scrape_options.openapi_schema_url, + openapi_tag: options.scrape_options.openapi_tag, + }; + } else if (options.scrape_options?.type == "shopify") { + return { + ...options, + type: "shopify", + group_variants: options.scrape_options.group_variants, + }; + } else { + return { + ...options, + type: undefined, + }; } - return null; }; export const CrawlingSettings = () => { @@ -70,10 +129,7 @@ export const CrawlingSettings = () => { mutationFn: async (options: CrawlOptions) => { await trieve.fetch("/api/dataset", "put", { data: { - crawl_options: { - ...options, - scrape_options: normalizeOpenAPIOptions(options.scrape_options), - }, + crawl_options: options, dataset_id: datasetId(), }, organizationId: userContext.selectedOrg().id, @@ -104,14 +160,16 @@ export const CrawlingSettings = () => { ); }; interface RealCrawlingSettingsProps { - initialCrawlingSettings: CrawlOptions; + initialCrawlingSettings: FlatCrawlOptions; mode: "edit" | "create"; onSave: (options: CrawlOptions) => void; } @@ -124,64 +182,57 @@ const Error = (props: { error: string | null | undefined }) => { ); }; -const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { - const [options, setOptions] = createStore(props.initialCrawlingSettings); - const [errors, setErrors] = createStore< - ReturnType>["errors"] - >({}); - - const isShopify = createMemo( - () => - options.scrape_options != {} && - options.scrape_options?.type === "shopify", - ); - const isOpenAPI = createMemo( - () => - options.scrape_options?.type != null && - options.scrape_options?.type === "openapi", - ); +export const validateFlatCrawlOptions: ValidateFn = ( + value, +) => { + const errors: ValidateErrors = {}; + if (!value.site_url) { + errors.site_url = "Site URL is required"; + } - createEffect(() => { - console.log(isOpenAPI()); - }); + if (value.site_url && !value.site_url.startsWith("http")) { + errors.site_url = "Invalid Site URL - http(s):// required"; + } - const validate: ValidateFn = (value) => { - const errors: Record = {}; - if (!value.site_url) { - errors.site_url = "Site URL is required"; + if (value.type != "shopify") { + if (!value.limit || value.limit <= 0) { + errors.limit = "Limit must be greater than 0"; } - - if (value.site_url && !value.site_url.startsWith("http")) { - errors.site_url = "Invalid Site URL - http(s):// required"; + if (!value.max_depth) { + errors.max_depth = "Max depth must be greater than 0"; } - - if (value.scrape_options?.type != "shopify") { - if (!value.limit || value.limit <= 0) { - errors.limit = "Limit must be greater than 0"; - } - if (!value.max_depth) { - errors.max_depth = "Max depth must be greater than 0"; - } - if ( - value.scrape_options?.type == "openapi" && - value.scrape_options?.openapi_tag && - !value.scrape_options.openapi_schema_url - ) { - errors.scrape_options = "OpenAPI Schema URL is required for tag"; - } + if (value.type === "openapi" && !value.openapi_schema_url) { + errors.openapi_schema_url = "OpenAPI Schema URL is required"; } + if ( + value.type == "openapi" && + value.openapi_tag && + !value.openapi_schema_url + ) { + errors.openapi_schema_url = "OpenAPI Schema URL is required for tag"; + } + } - return { - errors, - valid: Object.values(errors).filter((v) => !!v).length === 0, - }; + return { + errors, + valid: Object.values(errors).filter((v) => !!v).length === 0, }; +}; + +const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { + const [options, setOptions] = createStore(props.initialCrawlingSettings); + const [errors, setErrors] = createStore< + ReturnType>["errors"] + >({}); + + const isShopify = createMemo(() => options.type === "shopify"); + const isOpenAPI = createMemo(() => options.type === "openapi"); const submit = () => { - const validateResult = validate(options); + const validateResult = validateFlatCrawlOptions(options); if (validateResult.valid) { setErrors({}); - props.onSave(options); + props.onSave(unflattenCrawlOptions(options)); } else { setErrors(validateResult.errors); } @@ -240,12 +291,23 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { { - if (e.currentTarget.checked) { - setOptions("scrape_options", "type", "shopify"); - } else { - setOptions("scrape_options", "type", {}); - setOptions("scrape_options", {}); - } + setOptions((prev) => { + if (!e.currentTarget.checked) { + if (prev.type === "shopify") { + return { + ...prev, + type: undefined, + }; + } + return { + ...prev, + }; + } else { + return { + type: "shopify" as const, + }; + } + }); }} checked={isShopify()} class="h-4 w-4 rounded border border-neutral-300 bg-neutral-100 p-1 accent-magenta-400 dark:border-neutral-900 dark:bg-neutral-800" @@ -253,14 +315,26 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { /> { - if (e.currentTarget.checked) { - setOptions("scrape_options", "type", "openapi"); - } else { - setOptions("scrape_options", "type", {}); - setOptions("scrape_options", {}); - } - }} + onChange={(e) => + setOptions((prev) => { + if (!e.currentTarget.checked) { + if (prev.type === "openapi") { + return { + ...prev, + type: undefined, + }; + } + return { + ...prev, + }; + } else { + return { + ...prev, + type: "openapi", + }; + } + }) + } checked={isOpenAPI()} class="h-4 w-4 rounded border border-neutral-300 bg-neutral-100 p-1 accent-magenta-400 dark:border-neutral-900 dark:bg-neutral-800" type="checkbox" @@ -298,48 +372,36 @@ const RealCrawlingSettings = (props: RealCrawlingSettingsProps) => { />
    -
    - - { - if (!options.scrape_options) { - setOptions("scrape_options", {}); - } - setOptions( - "scrape_options", - "openapi_schema_url", - e.currentTarget.value, - ); - }} - class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 focus:outline-magenta-500 sm:text-sm sm:leading-6" - /> - -
    -
    - - { - if (!options.scrape_options) { - setOptions("scrape_options", { type: "openapi" }); - } - setOptions( - "scrape_options", - "openapi_tag", - e.currentTarget.value, - ); - }} - class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 focus:outline-magenta-500 sm:text-sm sm:leading-6" - /> -
    + +
    + + { + setOptions("openapi_schema_url", e.currentTarget.value); + }} + class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 focus:outline-magenta-500 sm:text-sm sm:leading-6" + /> + +
    +
    + + { + setOptions("openapi_tag", e.currentTarget.value); + }} + class="block w-full rounded border border-neutral-300 px-3 py-1.5 shadow-sm placeholder:text-neutral-400 focus:outline-magenta-500 sm:text-sm sm:leading-6" + /> +
    +
    > = (value: T) => { - errors: { - [key in keyof T]: string | undefined; - }; +export type ValidateFn> = (value: T) => { + errors: ValidateErrors; valid: boolean; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export type ValidateErrors> = ReturnType["errors"]; +export type ValidateErrors> = { + [key in keyof T]: NonNullable extends Record + ? ReturnType>>["errors"] + : string | undefined; +}; export const ErrorMsg = (props: { error: string | null | undefined }) => { return ( diff --git a/server/src/bin/crawl-worker.rs b/server/src/bin/crawl-worker.rs index cd2733faff..57098dd971 100644 --- a/server/src/bin/crawl-worker.rs +++ b/server/src/bin/crawl-worker.rs @@ -17,7 +17,9 @@ use trieve_server::{ }, }; use trieve_server::{ - data::models::{CrawlRequest, DatasetConfiguration, RedisPool, ScrapeOptions, CrawlShopifyOptions}, + data::models::{ + CrawlRequest, CrawlShopifyOptions, DatasetConfiguration, RedisPool, ScrapeOptions, + }, operators::crawl_operator::{get_crawl_from_firecrawl, Status}, }; use trieve_server::{ @@ -89,7 +91,11 @@ fn create_chunk_req_payload( product.title, variant.title, product.body_html ); - let group_variants = if let Some(ScrapeOptions::Shopify(CrawlShopifyOptions{ group_variants: Some(group_variants), .. })) = scrape_request.crawl_options.scrape_options { + let group_variants = if let Some(ScrapeOptions::Shopify(CrawlShopifyOptions { + group_variants: Some(group_variants), + .. + })) = scrape_request.crawl_options.scrape_options + { group_variants } else { true @@ -98,7 +104,7 @@ fn create_chunk_req_payload( let semantic_boost_phrase = if group_variants { variant.title.clone() } else { - product.title.clone() + product.title.clone() }; let fulltext_boost_phrase = if group_variants { @@ -114,14 +120,12 @@ fn create_chunk_req_payload( num_value: variant.price.parse().ok(), metadata: serde_json::to_value(product.clone()).ok(), tracking_id: if group_variants { - Some(variant.id.to_string()) - } else { - Some(product.id.to_string()) - }, + Some(variant.id.to_string()) + } else { + Some(product.id.to_string()) + }, group_tracking_ids: if group_variants { - Some(vec![ - product.id.to_string() - ]) + Some(vec![product.id.to_string()]) } else { None }, @@ -200,7 +204,9 @@ async fn get_chunks_with_firecrawl( let mut chunks = vec![]; let mut spec = None; - if let Some(ScrapeOptions::OpenApi(openapi_options)) = scrape_request.crawl_options.scrape_options.clone() { + if let Some(ScrapeOptions::OpenApi(openapi_options)) = + scrape_request.crawl_options.scrape_options.clone() + { let client = reqwest::Client::new(); let schema = match client @@ -313,7 +319,9 @@ async fn get_chunks_with_firecrawl( let page_tags = get_tags(page_link.clone()); if let Some(spec) = &spec { - if let Some(ScrapeOptions::OpenApi(ref openapi_options)) = scrape_request.crawl_options.scrape_options { + if let Some(ScrapeOptions::OpenApi(ref openapi_options)) = + scrape_request.crawl_options.scrape_options + { if page_tags.contains(&openapi_options.openapi_tag) { if let Some(last_tag) = page_tags.last() { // try to find a operation in the spec with an operation_id that matches the last tag directly, with - replaced by _ or vice versa diff --git a/server/src/data/models.rs b/server/src/data/models.rs index 1b01028bce..5160847f60 100644 --- a/server/src/data/models.rs +++ b/server/src/data/models.rs @@ -6541,7 +6541,7 @@ pub struct CrawlOptions { /// Boost titles such that keyword matches in titles are prioritized in search results. Strongly recommended to leave this on. Defaults to true. pub boost_titles: Option, /// Options for including an openapi spec in the crawl - pub scrape_options: Option + pub scrape_options: Option, } #[derive(Serialize, Deserialize, Debug, ToSchema, Clone)] @@ -6553,12 +6553,14 @@ pub enum ScrapeOptions { OpenApi(CrawlOpenAPIOptions), /// Shopify Scrape Options #[serde(rename = "shopify")] - Shopify(CrawlShopifyOptions) + Shopify(CrawlShopifyOptions), } #[derive(Serialize, Deserialize, Debug, ToSchema, Clone)] #[schema(title = "CrawlShopifyOptions")] +/// Options for Crawling Shopify pub struct CrawlShopifyOptions { + /// This option will ingest all variants as individual chunks and place them in groups by product id. Turning this off will only scrape 1 variant per product. default: true pub group_variants: Option, } diff --git a/server/src/operators/crawl_operator.rs b/server/src/operators/crawl_operator.rs index 308067881f..a0ea85953e 100644 --- a/server/src/operators/crawl_operator.rs +++ b/server/src/operators/crawl_operator.rs @@ -4,7 +4,7 @@ use crate::data::models::FirecrawlCrawlRequest; use crate::data::models::RedisPool; use crate::handlers::chunk_handler::CrawlInterval; use crate::{ - data::models::{CrawlRequest, CrawlRequestPG, ScrapeOptions, CrawlShopifyOptions, Pool}, + data::models::{CrawlRequest, CrawlRequestPG, Pool, ScrapeOptions}, errors::ServiceError, }; use actix_web::web; @@ -127,7 +127,7 @@ pub async fn crawl( redis_pool: web::Data, dataset_id: uuid::Uuid, ) -> Result { - let scrape_id = if let Some(ScrapeOptions::Shopify(_)) = crawl_options.scrape_options { + let scrape_id = if let Some(ScrapeOptions::Shopify(_)) = crawl_options.scrape_options { uuid::Uuid::nil() } else { crawl_site(crawl_options.clone()) @@ -384,13 +384,14 @@ pub async fn update_crawl_settings_for_dataset( } let merged_options = if let Some(prev_crawl_req) = prev_crawl_req { - let previous_crawl_options: CrawlOptions = serde_json::from_value(prev_crawl_req.crawl_options).map_err(|e| ServiceError::InternalServerError(e.to_string()))?; + let previous_crawl_options: CrawlOptions = + serde_json::from_value(prev_crawl_req.crawl_options) + .map_err(|e| ServiceError::InternalServerError(e.to_string()))?; crawl_options.merge(previous_crawl_options) } else { crawl_options }; - diesel::update( crawl_requests_table::crawl_requests .filter(crawl_requests_table::dataset_id.eq(dataset_id)), From f8145434910d5f0893f4c124e1ecd0a196ad1422 Mon Sep 17 00:00:00 2001 From: Drew Harris Date: Fri, 11 Oct 2024 14:31:46 -0500 Subject: [PATCH 053/159] cleanup: update mintlify widget --- frontends/dashboard/public/mintlify.js | 132 ++++++++++++------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/frontends/dashboard/public/mintlify.js b/frontends/dashboard/public/mintlify.js index eb21a61b96..320c386bc7 100644 --- a/frontends/dashboard/public/mintlify.js +++ b/frontends/dashboard/public/mintlify.js @@ -1,10 +1,12 @@ -(function(cs,Ac){typeof exports=="object"&&typeof module<"u"?Ac(exports):typeof define=="function"&&define.amd?define(["exports"],Ac):(cs=typeof globalThis<"u"?globalThis:cs||self,Ac(cs.MintlifyWidget={}))})(this,function(cs){"use strict";var Ac=document.createElement("style");Ac.textContent='.mintlify-prose{color:var(--tw-prose-body);max-width:65ch}.mintlify-prose :where(p):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.mintlify-prose :where([class~=lead]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.mintlify-prose :where(a):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.mintlify-prose :where(strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.mintlify-prose :where(a strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(blockquote strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(thead th strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(ol):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.mintlify-prose :where(ol[type=A]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:upper-alpha}.mintlify-prose :where(ol[type=a]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:lower-alpha}.mintlify-prose :where(ol[type=A s]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:upper-alpha}.mintlify-prose :where(ol[type=a s]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:lower-alpha}.mintlify-prose :where(ol[type=I]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:upper-roman}.mintlify-prose :where(ol[type=i]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:lower-roman}.mintlify-prose :where(ol[type=I s]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:upper-roman}.mintlify-prose :where(ol[type=i s]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:lower-roman}.mintlify-prose :where(ol[type="1"]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:decimal}.mintlify-prose :where(ul):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.mintlify-prose :where(ol>li):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.mintlify-prose :where(ul>li):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *))::marker{color:var(--tw-prose-bullets)}.mintlify-prose :where(dt):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.mintlify-prose :where(hr):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.mintlify-prose :where(blockquote):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.mintlify-prose :where(blockquote p:first-of-type):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):before{content:open-quote}.mintlify-prose :where(blockquote p:last-of-type):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):after{content:close-quote}.mintlify-prose :where(h1):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.mintlify-prose :where(h1 strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:900;color:inherit}.mintlify-prose :where(h2):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.mintlify-prose :where(h2 strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:800;color:inherit}.mintlify-prose :where(h3):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.mintlify-prose :where(h3 strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:700;color:inherit}.mintlify-prose :where(h4):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.mintlify-prose :where(h4 strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:700;color:inherit}.mintlify-prose :where(img):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:2em;margin-bottom:2em}.mintlify-prose :where(picture):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.mintlify-prose :where(video):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:2em;margin-bottom:2em}.mintlify-prose :where(kbd):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%),0 3px rgb(var(--tw-prose-kbd-shadows) / 10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.mintlify-prose :where(code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.mintlify-prose :where(code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):before{content:"`"}.mintlify-prose :where(code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):after{content:"`"}.mintlify-prose :where(a code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(h1 code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(h2 code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit;font-size:.875em}.mintlify-prose :where(h3 code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit;font-size:.9em}.mintlify-prose :where(h4 code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(blockquote code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(thead th code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(pre):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.mintlify-prose :where(pre code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.mintlify-prose :where(pre code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):before{content:none}.mintlify-prose :where(pre code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):after{content:none}.mintlify-prose :where(table):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){width:100%;table-layout:auto;text-align:start;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.mintlify-prose :where(thead):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.mintlify-prose :where(thead th):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.mintlify-prose :where(tbody tr):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.mintlify-prose :where(tbody tr:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){border-bottom-width:0}.mintlify-prose :where(tbody td):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){vertical-align:baseline}.mintlify-prose :where(tfoot):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.mintlify-prose :where(tfoot td):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){vertical-align:top}.mintlify-prose :where(figure>*):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0;margin-bottom:0}.mintlify-prose :where(figcaption):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.mintlify-prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: 17 24 39;--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.mintlify-prose :where(picture>img):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0;margin-bottom:0}.mintlify-prose :where(li):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:.5em;margin-bottom:.5em}.mintlify-prose :where(ol>li):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-start:.375em}.mintlify-prose :where(ul>li):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-start:.375em}.mintlify-prose :where(.mintlify-prose>ul>li p):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:.75em;margin-bottom:.75em}.mintlify-prose :where(.mintlify-prose>ul>li>p:first-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:1.25em}.mintlify-prose :where(.mintlify-prose>ul>li>p:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-bottom:1.25em}.mintlify-prose :where(.mintlify-prose>ol>li>p:first-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:1.25em}.mintlify-prose :where(.mintlify-prose>ol>li>p:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-bottom:1.25em}.mintlify-prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:.75em;margin-bottom:.75em}.mintlify-prose :where(dl):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.mintlify-prose :where(dd):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.mintlify-prose :where(hr+*):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0}.mintlify-prose :where(h2+*):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0}.mintlify-prose :where(h3+*):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0}.mintlify-prose :where(h4+*):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0}.mintlify-prose :where(thead th:first-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-start:0}.mintlify-prose :where(thead th:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-end:0}.mintlify-prose :where(tbody td,tfoot td):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.mintlify-prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-start:0}.mintlify-prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-end:0}.mintlify-prose :where(figure):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:2em;margin-bottom:2em}.mintlify-prose :where(.mintlify-prose>:first-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0}.mintlify-prose :where(.mintlify-prose>:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-bottom:0}.mintlify-prose:is(.mintlify-dark *){color:inherit}.mintlify-prose thead:is(.mintlify-dark *){border-color:#37415180}.mintlify-prose tr:is(.mintlify-dark *){border-color:#1f293780}.mintlify-prose hr:is(.mintlify-dark *){border-color:#1f293780}.mintlify-prose strong:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(249 250 251 / var(--tw-text-opacity))}.mintlify-prose a:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(249 250 251 / var(--tw-text-opacity))}.mintlify-prose h3:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(249 250 251 / var(--tw-text-opacity))}.mintlify-prose blockquote:is(.mintlify-dark *){--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity));--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.mintlify-fixed{position:fixed}.mintlify-absolute{position:absolute}.mintlify-relative{position:relative}.mintlify-inset-0{top:0;right:0;bottom:0;left:0}.mintlify-bottom-4{bottom:1rem}.mintlify-left-1\\/2{left:50%}.mintlify-right-4{right:1rem}.mintlify-top-1\\/2{top:50%}.mintlify-z-10{z-index:10}.mintlify-z-\\[998\\]{z-index:998}.mintlify-z-\\[999\\]{z-index:999}.mintlify-mx-auto{margin-left:auto;margin-right:auto}.mintlify-ml-1{margin-left:.25rem}.mintlify-mr-3{margin-right:.75rem}.mintlify-flex{display:flex}.mintlify-hidden{display:none}.mintlify-size-5{width:1.25rem;height:1.25rem}.mintlify-h-4{height:1rem}.mintlify-h-6{height:1.5rem}.mintlify-h-9{height:2.25rem}.mintlify-h-\\[72px\\]{height:72px}.mintlify-h-full{height:100%}.mintlify-h-screen{height:100vh}.mintlify-max-h-\\[calc\\(100vh-48px-72px-48px\\)\\]{max-height:calc(100vh - 168px)}.mintlify-w-2{width:.5rem}.mintlify-w-full{width:100%}.mintlify-w-screen{width:100vw}.mintlify-max-w-\\[640px\\]{max-width:640px}.mintlify-flex-none{flex:none}.mintlify--translate-x-1\\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.mintlify--translate-y-1\\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.mintlify-scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.mintlify-scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.mintlify-transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes mintlify-pulse{50%{opacity:.5}}.mintlify-animate-pulse{animation:mintlify-pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes mintlify-spin{to{transform:rotate(360deg)}}.mintlify-animate-spin{animation:mintlify-spin 1s linear infinite}.mintlify-cursor-pointer{cursor:pointer}.mintlify-flex-col{flex-direction:column}.mintlify-flex-wrap{flex-wrap:wrap}.mintlify-items-end{align-items:flex-end}.mintlify-items-center{align-items:center}.mintlify-justify-between{justify-content:space-between}.mintlify-gap-0{gap:0px}.mintlify-gap-0\\.5{gap:.125rem}.mintlify-gap-1{gap:.25rem}.mintlify-gap-2{gap:.5rem}.mintlify-gap-4{gap:1rem}.mintlify-overflow-hidden{overflow:hidden}.mintlify-overflow-y-auto{overflow-y:auto}.mintlify-break-all{word-break:break-all}.mintlify-rounded-2xl{border-radius:1rem}.mintlify-rounded-\\[20px\\]{border-radius:20px}.mintlify-rounded-full{border-radius:9999px}.mintlify-rounded-lg{border-radius:.5rem}.mintlify-rounded-md{border-radius:.375rem}.mintlify-rounded-xl{border-radius:.75rem}.mintlify-border{border-width:1px}.mintlify-border-b{border-bottom-width:1px}.mintlify-border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.mintlify-border-transparent{border-color:transparent}.mintlify-bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.mintlify-bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity))}.mintlify-bg-primary{background-color:var(--mintlify-widget-primary-color, #0D9373)}.mintlify-bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.mintlify-bg-white\\/20{background-color:#fff3}.mintlify-fill-primary{fill:var(--mintlify-widget-primary-color, #0D9373)}.mintlify-p-2{padding:.5rem}.mintlify-p-4{padding:1rem}.mintlify-px-1{padding-left:.25rem;padding-right:.25rem}.mintlify-px-1\\.5{padding-left:.375rem;padding-right:.375rem}.mintlify-px-2{padding-left:.5rem;padding-right:.5rem}.mintlify-px-3{padding-left:.75rem;padding-right:.75rem}.mintlify-px-4{padding-left:1rem;padding-right:1rem}.mintlify-py-1{padding-top:.25rem;padding-bottom:.25rem}.mintlify-py-1\\.5{padding-top:.375rem;padding-bottom:.375rem}.mintlify-py-2{padding-top:.5rem;padding-bottom:.5rem}.mintlify-py-2\\.5{padding-top:.625rem;padding-bottom:.625rem}.mintlify-py-3{padding-top:.75rem;padding-bottom:.75rem}.mintlify-pb-4{padding-bottom:1rem}.mintlify-pl-2{padding-left:.5rem}.mintlify-pl-4{padding-left:1rem}.mintlify-pr-12{padding-right:3rem}.mintlify-pr-3{padding-right:.75rem}.mintlify-text-sm{font-size:.875rem;line-height:1.25rem}.mintlify-text-xs{font-size:.75rem;line-height:1rem}.mintlify-font-medium{font-weight:500}.mintlify-leading-6{line-height:1.5rem}.mintlify-tracking-tight{letter-spacing:-.025em}.mintlify-text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.mintlify-text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.mintlify-text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.mintlify-text-gray-950{--tw-text-opacity: 1;color:rgb(3 7 18 / var(--tw-text-opacity))}.mintlify-text-gray-950\\/50{color:#03071280}.mintlify-text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity))}.mintlify-text-primary{color:var(--mintlify-widget-primary-color, #0D9373)}.mintlify-text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity))}.mintlify-text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.mintlify-opacity-0{opacity:0}.mintlify-opacity-100{opacity:1}.mintlify-opacity-25{opacity:.25}.mintlify-opacity-70{opacity:.7}.mintlify-shadow-button{--tw-shadow: 0px 16px 32px -12px rgba(88, 92, 95, .2);--tw-shadow-colored: 0px 16px 32px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.mintlify-shadow-input{--tw-shadow: 0px 16px 32px -12px rgba(88, 92, 95, .1);--tw-shadow-colored: 0px 16px 32px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.mintlify-shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.mintlify-outline-none{outline:2px solid transparent;outline-offset:2px}.mintlify-backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.mintlify-transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.mintlify-transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.mintlify-duration-100{transition-duration:.1s}.mintlify-ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.mintlify-ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.dark\\:mintlify-prose-invert:is(.mintlify-dark *){--tw-prose-body: var(--tw-prose-invert-body);--tw-prose-headings: var(--tw-prose-invert-headings);--tw-prose-lead: var(--tw-prose-invert-lead);--tw-prose-links: var(--tw-prose-invert-links);--tw-prose-bold: var(--tw-prose-invert-bold);--tw-prose-counters: var(--tw-prose-invert-counters);--tw-prose-bullets: var(--tw-prose-invert-bullets);--tw-prose-hr: var(--tw-prose-invert-hr);--tw-prose-quotes: var(--tw-prose-invert-quotes);--tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);--tw-prose-captions: var(--tw-prose-invert-captions);--tw-prose-kbd: var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);--tw-prose-code: var(--tw-prose-invert-code);--tw-prose-pre-code: var(--tw-prose-invert-pre-code);--tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);--tw-prose-th-borders: var(--tw-prose-invert-th-borders);--tw-prose-td-borders: var(--tw-prose-invert-td-borders)}.placeholder\\:mintlify-text-gray-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.placeholder\\:mintlify-text-gray-400::placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.last\\:mintlify-mb-2:last-child{margin-bottom:.5rem}.hover\\:mintlify-bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.hover\\:mintlify-bg-primary:hover{background-color:var(--mintlify-widget-primary-color, #0D9373)}.hover\\:mintlify-text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.hover\\:mintlify-text-gray-800:hover{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.hover\\:mintlify-text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.focus\\:mintlify-border-gray-950:focus{--tw-border-opacity: 1;border-color:rgb(3 7 18 / var(--tw-border-opacity))}.focus\\:mintlify-ring-0:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.disabled\\:mintlify-text-gray-500:disabled{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.mintlify-group:hover .group-hover\\:mintlify-block{display:block}.mintlify-group:hover .group-hover\\:mintlify-hidden{display:none}.dark\\:mintlify-border-gray-800:is(.mintlify-dark *){--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity))}.dark\\:mintlify-border-white\\/10:is(.mintlify-dark *){border-color:#ffffff1a}.dark\\:mintlify-bg-gray-700:is(.mintlify-dark *){--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity))}.dark\\:mintlify-bg-gray-800:is(.mintlify-dark *){--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity))}.dark\\:mintlify-bg-gray-950:is(.mintlify-dark *){--tw-bg-opacity: 1;background-color:rgb(3 7 18 / var(--tw-bg-opacity))}.dark\\:mintlify-bg-gray-950\\/20:is(.mintlify-dark *){background-color:#03071233}.dark\\:mintlify-bg-primary-light:is(.mintlify-dark *){background-color:var(--mintlify-widget-primary-light-color, #55D799)}.dark\\:mintlify-bg-white\\/5:is(.mintlify-dark *){background-color:#ffffff0d}.dark\\:mintlify-fill-primary-light:is(.mintlify-dark *){fill:var(--mintlify-widget-primary-light-color, #55D799)}.dark\\:mintlify-text-gray-300:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.dark\\:mintlify-text-gray-500:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.dark\\:mintlify-text-gray-950:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(3 7 18 / var(--tw-text-opacity))}.dark\\:mintlify-text-orange-400:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity))}.dark\\:mintlify-text-primary-light:is(.mintlify-dark *){color:var(--mintlify-widget-primary-light-color, #55D799)}.dark\\:mintlify-text-red-400:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.dark\\:mintlify-text-white:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.dark\\:mintlify-text-white\\/50:is(.mintlify-dark *){color:#ffffff80}.placeholder\\:dark\\:mintlify-text-white\\/50:is(.mintlify-dark *)::-moz-placeholder{color:#ffffff80}.placeholder\\:dark\\:mintlify-text-white\\/50:is(.mintlify-dark *)::placeholder{color:#ffffff80}.dark\\:hover\\:mintlify-bg-gray-700:hover:is(.mintlify-dark *){--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity))}.dark\\:hover\\:mintlify-bg-primary-light:hover:is(.mintlify-dark *){background-color:var(--mintlify-widget-primary-light-color, #55D799)}.dark\\:hover\\:mintlify-text-gray-300:hover:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.dark\\:hover\\:mintlify-text-gray-950:hover:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(3 7 18 / var(--tw-text-opacity))}.hover\\:dark\\:mintlify-text-gray-200:is(.mintlify-dark *):hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.dark\\:focus\\:mintlify-border-white:focus:is(.mintlify-dark *){--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity))}.dark\\:disabled\\:mintlify-text-white\\/50:disabled:is(.mintlify-dark *){color:#ffffff80}@media (min-width: 640px){.sm\\:mintlify-p-6{padding:1.5rem}}@media (min-width: 768px){.md\\:mintlify-p-12{padding:3rem}}\n',document.head.appendChild(Ac);function Kx(c,p){for(var m=0;mS[T]})}}}return Object.freeze(Object.defineProperty(c,Symbol.toStringTag,{value:"Module"}))}function Qw(c){return c&&c.__esModule&&Object.prototype.hasOwnProperty.call(c,"default")?c.default:c}var Py={exports:{}},bd={},Vy={exports:{}},_t={};/** @license React react.production.min.js +(function(cs,Ac){typeof exports=="object"&&typeof module<"u"?Ac(exports):typeof define=="function"&&define.amd?define(["exports"],Ac):(cs=typeof globalThis<"u"?globalThis:cs||self,Ac(cs.MintlifyWidget={}))})(this,function(cs){"use strict";var Ac=document.createElement("style");Ac.textContent='.mintlify-prose{color:var(--tw-prose-body);max-width:65ch}.mintlify-prose :where(p):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.mintlify-prose :where([class~=lead]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.mintlify-prose :where(a):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.mintlify-prose :where(strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.mintlify-prose :where(a strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(blockquote strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(thead th strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(ol):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.mintlify-prose :where(ol[type=A]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:upper-alpha}.mintlify-prose :where(ol[type=a]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:lower-alpha}.mintlify-prose :where(ol[type=A s]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:upper-alpha}.mintlify-prose :where(ol[type=a s]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:lower-alpha}.mintlify-prose :where(ol[type=I]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:upper-roman}.mintlify-prose :where(ol[type=i]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:lower-roman}.mintlify-prose :where(ol[type=I s]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:upper-roman}.mintlify-prose :where(ol[type=i s]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:lower-roman}.mintlify-prose :where(ol[type="1"]):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:decimal}.mintlify-prose :where(ul):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.mintlify-prose :where(ol>li):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.mintlify-prose :where(ul>li):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *))::marker{color:var(--tw-prose-bullets)}.mintlify-prose :where(dt):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.mintlify-prose :where(hr):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.mintlify-prose :where(blockquote):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.mintlify-prose :where(blockquote p:first-of-type):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):before{content:open-quote}.mintlify-prose :where(blockquote p:last-of-type):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):after{content:close-quote}.mintlify-prose :where(h1):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.mintlify-prose :where(h1 strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:900;color:inherit}.mintlify-prose :where(h2):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.mintlify-prose :where(h2 strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:800;color:inherit}.mintlify-prose :where(h3):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.mintlify-prose :where(h3 strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:700;color:inherit}.mintlify-prose :where(h4):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.mintlify-prose :where(h4 strong):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:700;color:inherit}.mintlify-prose :where(img):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:2em;margin-bottom:2em}.mintlify-prose :where(picture):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.mintlify-prose :where(video):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:2em;margin-bottom:2em}.mintlify-prose :where(kbd):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%),0 3px rgb(var(--tw-prose-kbd-shadows) / 10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.mintlify-prose :where(code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.mintlify-prose :where(code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):before{content:"`"}.mintlify-prose :where(code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):after{content:"`"}.mintlify-prose :where(a code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(h1 code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(h2 code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit;font-size:.875em}.mintlify-prose :where(h3 code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit;font-size:.9em}.mintlify-prose :where(h4 code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(blockquote code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(thead th code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:inherit}.mintlify-prose :where(pre):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.mintlify-prose :where(pre code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.mintlify-prose :where(pre code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):before{content:none}.mintlify-prose :where(pre code):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)):after{content:none}.mintlify-prose :where(table):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){width:100%;table-layout:auto;text-align:start;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.mintlify-prose :where(thead):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.mintlify-prose :where(thead th):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.mintlify-prose :where(tbody tr):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.mintlify-prose :where(tbody tr:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){border-bottom-width:0}.mintlify-prose :where(tbody td):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){vertical-align:baseline}.mintlify-prose :where(tfoot):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.mintlify-prose :where(tfoot td):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){vertical-align:top}.mintlify-prose :where(figure>*):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0;margin-bottom:0}.mintlify-prose :where(figcaption):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.mintlify-prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: 17 24 39;--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.mintlify-prose :where(picture>img):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0;margin-bottom:0}.mintlify-prose :where(li):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:.5em;margin-bottom:.5em}.mintlify-prose :where(ol>li):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-start:.375em}.mintlify-prose :where(ul>li):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-start:.375em}.mintlify-prose :where(.mintlify-prose>ul>li p):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:.75em;margin-bottom:.75em}.mintlify-prose :where(.mintlify-prose>ul>li>p:first-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:1.25em}.mintlify-prose :where(.mintlify-prose>ul>li>p:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-bottom:1.25em}.mintlify-prose :where(.mintlify-prose>ol>li>p:first-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:1.25em}.mintlify-prose :where(.mintlify-prose>ol>li>p:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-bottom:1.25em}.mintlify-prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:.75em;margin-bottom:.75em}.mintlify-prose :where(dl):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.mintlify-prose :where(dd):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.mintlify-prose :where(hr+*):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0}.mintlify-prose :where(h2+*):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0}.mintlify-prose :where(h3+*):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0}.mintlify-prose :where(h4+*):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0}.mintlify-prose :where(thead th:first-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-start:0}.mintlify-prose :where(thead th:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-end:0}.mintlify-prose :where(tbody td,tfoot td):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.mintlify-prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-start:0}.mintlify-prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){padding-inline-end:0}.mintlify-prose :where(figure):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:2em;margin-bottom:2em}.mintlify-prose :where(.mintlify-prose>:first-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-top:0}.mintlify-prose :where(.mintlify-prose>:last-child):not(:where([class~=mintlify-not-prose],[class~=mintlify-not-prose] *)){margin-bottom:0}.mintlify-prose:is(.mintlify-dark *){color:inherit}.mintlify-prose thead:is(.mintlify-dark *){border-color:#37415180}.mintlify-prose tr:is(.mintlify-dark *){border-color:#1f293780}.mintlify-prose hr:is(.mintlify-dark *){border-color:#1f293780}.mintlify-prose strong:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(249 250 251 / var(--tw-text-opacity))}.mintlify-prose a:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(249 250 251 / var(--tw-text-opacity))}.mintlify-prose h3:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(249 250 251 / var(--tw-text-opacity))}.mintlify-prose blockquote:is(.mintlify-dark *){--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity));--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.mintlify-fixed{position:fixed}.mintlify-absolute{position:absolute}.mintlify-relative{position:relative}.mintlify-inset-0{top:0;right:0;bottom:0;left:0}.mintlify-bottom-4{bottom:1rem}.mintlify-left-1\\/2{left:50%}.mintlify-right-4{right:1rem}.mintlify-top-1\\/2{top:50%}.mintlify-z-10{z-index:10}.mintlify-z-\\[998\\]{z-index:998}.mintlify-z-\\[999\\]{z-index:999}.mintlify-mx-auto{margin-left:auto;margin-right:auto}.mintlify-ml-1{margin-left:.25rem}.mintlify-mr-3{margin-right:.75rem}.mintlify-flex{display:flex}.mintlify-hidden{display:none}.mintlify-size-5{width:1.25rem;height:1.25rem}.mintlify-h-4{height:1rem}.mintlify-h-6{height:1.5rem}.mintlify-h-9{height:2.25rem}.mintlify-h-\\[72px\\]{height:72px}.mintlify-h-full{height:100%}.mintlify-h-screen{height:100vh}.mintlify-max-h-\\[calc\\(100vh-48px-72px-48px\\)\\]{max-height:calc(100vh - 168px)}.mintlify-w-2{width:.5rem}.mintlify-w-full{width:100%}.mintlify-w-screen{width:100vw}.mintlify-max-w-\\[640px\\]{max-width:640px}.mintlify-flex-none{flex:none}.mintlify--translate-x-1\\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.mintlify--translate-y-1\\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.mintlify-scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.mintlify-scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.mintlify-transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes mintlify-pulse{50%{opacity:.5}}.mintlify-animate-pulse{animation:mintlify-pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes mintlify-spin{to{transform:rotate(360deg)}}.mintlify-animate-spin{animation:mintlify-spin 1s linear infinite}.mintlify-cursor-pointer{cursor:pointer}.mintlify-flex-col{flex-direction:column}.mintlify-flex-wrap{flex-wrap:wrap}.mintlify-items-end{align-items:flex-end}.mintlify-items-center{align-items:center}.mintlify-justify-between{justify-content:space-between}.mintlify-gap-0{gap:0px}.mintlify-gap-0\\.5{gap:.125rem}.mintlify-gap-1{gap:.25rem}.mintlify-gap-2{gap:.5rem}.mintlify-gap-4{gap:1rem}.mintlify-overflow-hidden{overflow:hidden}.mintlify-overflow-y-auto{overflow-y:auto}.mintlify-break-all{word-break:break-all}.mintlify-rounded-2xl{border-radius:1rem}.mintlify-rounded-\\[20px\\]{border-radius:20px}.mintlify-rounded-full{border-radius:9999px}.mintlify-rounded-lg{border-radius:.5rem}.mintlify-rounded-md{border-radius:.375rem}.mintlify-rounded-xl{border-radius:.75rem}.mintlify-border{border-width:1px}.mintlify-border-b{border-bottom-width:1px}.mintlify-border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.mintlify-border-transparent{border-color:transparent}.mintlify-bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.mintlify-bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity))}.mintlify-bg-primary{background-color:var(--mintlify-widget-primary-color, #0D9373)}.mintlify-bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.mintlify-bg-white\\/20{background-color:#fff3}.mintlify-fill-primary{fill:var(--mintlify-widget-primary-color, #0D9373)}.mintlify-p-2{padding:.5rem}.mintlify-p-4{padding:1rem}.mintlify-px-1{padding-left:.25rem;padding-right:.25rem}.mintlify-px-1\\.5{padding-left:.375rem;padding-right:.375rem}.mintlify-px-2{padding-left:.5rem;padding-right:.5rem}.mintlify-px-3{padding-left:.75rem;padding-right:.75rem}.mintlify-px-4{padding-left:1rem;padding-right:1rem}.mintlify-py-1{padding-top:.25rem;padding-bottom:.25rem}.mintlify-py-1\\.5{padding-top:.375rem;padding-bottom:.375rem}.mintlify-py-2{padding-top:.5rem;padding-bottom:.5rem}.mintlify-py-2\\.5{padding-top:.625rem;padding-bottom:.625rem}.mintlify-py-3{padding-top:.75rem;padding-bottom:.75rem}.mintlify-pb-4{padding-bottom:1rem}.mintlify-pl-2{padding-left:.5rem}.mintlify-pl-4{padding-left:1rem}.mintlify-pr-12{padding-right:3rem}.mintlify-pr-3{padding-right:.75rem}.mintlify-text-sm{font-size:.875rem;line-height:1.25rem}.mintlify-text-xs{font-size:.75rem;line-height:1rem}.mintlify-font-medium{font-weight:500}.mintlify-leading-6{line-height:1.5rem}.mintlify-tracking-tight{letter-spacing:-.025em}.mintlify-text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.mintlify-text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.mintlify-text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.mintlify-text-gray-950{--tw-text-opacity: 1;color:rgb(3 7 18 / var(--tw-text-opacity))}.mintlify-text-gray-950\\/50{color:#03071280}.mintlify-text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity))}.mintlify-text-primary{color:var(--mintlify-widget-primary-color, #0D9373)}.mintlify-text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity))}.mintlify-text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.mintlify-opacity-0{opacity:0}.mintlify-opacity-100{opacity:1}.mintlify-opacity-25{opacity:.25}.mintlify-opacity-70{opacity:.7}.mintlify-shadow-button{--tw-shadow: 0px 16px 32px -12px rgba(88, 92, 95, .2);--tw-shadow-colored: 0px 16px 32px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.mintlify-shadow-input{--tw-shadow: 0px 16px 32px -12px rgba(88, 92, 95, .1);--tw-shadow-colored: 0px 16px 32px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.mintlify-shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.mintlify-outline-none{outline:2px solid transparent;outline-offset:2px}.mintlify-backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.mintlify-transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.mintlify-transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.mintlify-duration-100{transition-duration:.1s}.mintlify-ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.mintlify-ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.dark\\:mintlify-prose-invert:is(.mintlify-dark *){--tw-prose-body: var(--tw-prose-invert-body);--tw-prose-headings: var(--tw-prose-invert-headings);--tw-prose-lead: var(--tw-prose-invert-lead);--tw-prose-links: var(--tw-prose-invert-links);--tw-prose-bold: var(--tw-prose-invert-bold);--tw-prose-counters: var(--tw-prose-invert-counters);--tw-prose-bullets: var(--tw-prose-invert-bullets);--tw-prose-hr: var(--tw-prose-invert-hr);--tw-prose-quotes: var(--tw-prose-invert-quotes);--tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);--tw-prose-captions: var(--tw-prose-invert-captions);--tw-prose-kbd: var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);--tw-prose-code: var(--tw-prose-invert-code);--tw-prose-pre-code: var(--tw-prose-invert-pre-code);--tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);--tw-prose-th-borders: var(--tw-prose-invert-th-borders);--tw-prose-td-borders: var(--tw-prose-invert-td-borders)}.placeholder\\:mintlify-text-gray-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.placeholder\\:mintlify-text-gray-400::placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.last\\:mintlify-mb-2:last-child{margin-bottom:.5rem}.hover\\:mintlify-bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.hover\\:mintlify-bg-primary:hover{background-color:var(--mintlify-widget-primary-color, #0D9373)}.hover\\:mintlify-text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.hover\\:mintlify-text-gray-800:hover{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.hover\\:mintlify-text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.focus\\:mintlify-border-gray-950:focus{--tw-border-opacity: 1;border-color:rgb(3 7 18 / var(--tw-border-opacity))}.focus\\:mintlify-ring-0:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.disabled\\:mintlify-text-gray-500:disabled{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.mintlify-group:hover .group-hover\\:mintlify-block{display:block}.mintlify-group:hover .group-hover\\:mintlify-hidden{display:none}.dark\\:mintlify-border-gray-800:is(.mintlify-dark *){--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity))}.dark\\:mintlify-border-white\\/10:is(.mintlify-dark *){border-color:#ffffff1a}.dark\\:mintlify-bg-gray-700:is(.mintlify-dark *){--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity))}.dark\\:mintlify-bg-gray-800:is(.mintlify-dark *){--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity))}.dark\\:mintlify-bg-gray-950:is(.mintlify-dark *){--tw-bg-opacity: 1;background-color:rgb(3 7 18 / var(--tw-bg-opacity))}.dark\\:mintlify-bg-gray-950\\/20:is(.mintlify-dark *){background-color:#03071233}.dark\\:mintlify-bg-primary-light:is(.mintlify-dark *){background-color:var(--mintlify-widget-primary-light-color, #55D799)}.dark\\:mintlify-bg-white\\/5:is(.mintlify-dark *){background-color:#ffffff0d}.dark\\:mintlify-fill-primary-light:is(.mintlify-dark *){fill:var(--mintlify-widget-primary-light-color, #55D799)}.dark\\:mintlify-text-gray-300:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.dark\\:mintlify-text-gray-500:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.dark\\:mintlify-text-gray-950:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(3 7 18 / var(--tw-text-opacity))}.dark\\:mintlify-text-orange-400:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity))}.dark\\:mintlify-text-primary-light:is(.mintlify-dark *){color:var(--mintlify-widget-primary-light-color, #55D799)}.dark\\:mintlify-text-red-400:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.dark\\:mintlify-text-white:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.dark\\:mintlify-text-white\\/50:is(.mintlify-dark *){color:#ffffff80}.placeholder\\:dark\\:mintlify-text-white\\/50:is(.mintlify-dark *)::-moz-placeholder{color:#ffffff80}.placeholder\\:dark\\:mintlify-text-white\\/50:is(.mintlify-dark *)::placeholder{color:#ffffff80}.dark\\:hover\\:mintlify-bg-gray-700:hover:is(.mintlify-dark *){--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity))}.dark\\:hover\\:mintlify-bg-primary-light:hover:is(.mintlify-dark *){background-color:var(--mintlify-widget-primary-light-color, #55D799)}.dark\\:hover\\:mintlify-text-gray-300:hover:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.dark\\:hover\\:mintlify-text-gray-950:hover:is(.mintlify-dark *){--tw-text-opacity: 1;color:rgb(3 7 18 / var(--tw-text-opacity))}.hover\\:dark\\:mintlify-text-gray-200:is(.mintlify-dark *):hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.dark\\:focus\\:mintlify-border-white:focus:is(.mintlify-dark *){--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity))}.dark\\:disabled\\:mintlify-text-white\\/50:disabled:is(.mintlify-dark *){color:#ffffff80}@media (min-width: 640px){.sm\\:mintlify-p-6{padding:1.5rem}}@media (min-width: 768px){.md\\:mintlify-p-12{padding:3rem}}\n',document.head.appendChild(Ac);function Jx(c,p){for(var m=0;mS[x]})}}}return Object.freeze(Object.defineProperty(c,Symbol.toStringTag,{value:"Module"}))}function Qw(c){return c&&c.__esModule&&Object.prototype.hasOwnProperty.call(c,"default")?c.default:c}var Py={exports:{}},Ed={},Vy={exports:{}},_t={};/** + * @license React + * react.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Gw;function Jx(){if(Gw)return _t;Gw=1;var c=Symbol.for("react.element"),p=Symbol.for("react.portal"),m=Symbol.for("react.fragment"),S=Symbol.for("react.strict_mode"),T=Symbol.for("react.profiler"),N=Symbol.for("react.provider"),k=Symbol.for("react.context"),g=Symbol.for("react.forward_ref"),U=Symbol.for("react.suspense"),H=Symbol.for("react.memo"),B=Symbol.for("react.lazy"),ae=Symbol.iterator;function ee(L){return L===null||typeof L!="object"?null:(L=ae&&L[ae]||L["@@iterator"],typeof L=="function"?L:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},z=Object.assign,_={};function Z(L,te,Oe){this.props=L,this.context=te,this.refs=_,this.updater=Oe||x}Z.prototype.isReactComponent={},Z.prototype.setState=function(L,te){if(typeof L!="object"&&typeof L!="function"&&L!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,L,te,"setState")},Z.prototype.forceUpdate=function(L){this.updater.enqueueForceUpdate(this,L,"forceUpdate")};function me(){}me.prototype=Z.prototype;function he(L,te,Oe){this.props=L,this.context=te,this.refs=_,this.updater=Oe||x}var de=he.prototype=new me;de.constructor=he,z(de,Z.prototype),de.isPureReactComponent=!0;var ce=Array.isArray,ze=Object.prototype.hasOwnProperty,ve={current:null},be={key:!0,ref:!0,__self:!0,__source:!0};function xe(L,te,Oe){var Pe,Ke={},wt=null,pt=null;if(te!=null)for(Pe in te.ref!==void 0&&(pt=te.ref),te.key!==void 0&&(wt=""+te.key),te)ze.call(te,Pe)&&!be.hasOwnProperty(Pe)&&(Ke[Pe]=te[Pe]);var St=arguments.length-2;if(St===1)Ke.children=Oe;else if(11?D-1:0),X=1;X1?D-1:0),X=1;X1){for(var qt=Array(Wt),Xt=0;Xt1){for(var Zt=Array(Xt),fn=0;fn is not supported and will be removed in a future major release. Did you mean to render instead?")),D.Provider},set:function(Le){D.Provider=Le}},_currentValue:{get:function(){return D._currentValue},set:function(Le){D._currentValue=Le}},_currentValue2:{get:function(){return D._currentValue2},set:function(Le){D._currentValue2=Le}},_threadCount:{get:function(){return D._threadCount},set:function(Le){D._threadCount=Le}},Consumer:{get:function(){return W||(W=!0,re("Rendering is not supported and will be removed in a future major release. Did you mean to render instead?")),D.Consumer}},displayName:{get:function(){return D.displayName},set:function(Le){ye||(nt("Setting `displayName` on Context.Consumer has no effect. You should set it directly on the context with Context.displayName = '%s'.",Le),ye=!0)}}}),D.Consumer=He}return D._currentRenderer=null,D._currentRenderer2=null,D}var aa=-1,zi=0,Ba=1,Ir=2;function Yr(w){if(w._status===aa){var D=w._result,W=D();if(W.then(function(He){if(w._status===zi||w._status===aa){var Le=w;Le._status=Ba,Le._result=He}},function(He){if(w._status===zi||w._status===aa){var Le=w;Le._status=Ir,Le._result=He}}),w._status===aa){var X=w;X._status=zi,X._result=W}}if(w._status===Ba){var ye=w._result;return ye===void 0&&re(`lazy: Expected the result of a dynamic import() call. Instead received: %s + */m.NODE_ENV!=="production"&&function(){typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart=="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error);var S="18.2.0",x=Symbol.for("react.element"),N=Symbol.for("react.portal"),k=Symbol.for("react.fragment"),g=Symbol.for("react.strict_mode"),U=Symbol.for("react.profiler"),H=Symbol.for("react.provider"),B=Symbol.for("react.context"),ae=Symbol.for("react.forward_ref"),ee=Symbol.for("react.suspense"),T=Symbol.for("react.suspense_list"),z=Symbol.for("react.memo"),_=Symbol.for("react.lazy"),Z=Symbol.for("react.offscreen"),me=Symbol.iterator,he="@@iterator";function de(w){if(w===null||typeof w!="object")return null;var D=me&&w[me]||w[he];return typeof D=="function"?D:null}var ce={current:null},ze={transition:null},ve={current:null,isBatchingLegacy:!1,didScheduleLegacyUpdate:!1},be={current:null},xe={},Et=null;function Ut(w){Et=w}xe.setExtraStackFrame=function(w){Et=w},xe.getCurrentStack=null,xe.getStackAddendum=function(){var w="";Et&&(w+=Et);var D=xe.getCurrentStack;return D&&(w+=D()||""),w};var Dt=!1,ot=!1,Nt=!1,Ze=!1,at=!1,bt={ReactCurrentDispatcher:ce,ReactCurrentBatchConfig:ze,ReactCurrentOwner:be};bt.ReactDebugCurrentFrame=xe,bt.ReactCurrentActQueue=ve;function nt(w){{for(var D=arguments.length,W=new Array(D>1?D-1:0),X=1;X1?D-1:0),X=1;X1){for(var qt=Array(Wt),Xt=0;Xt1){for(var Zt=Array(Xt),fn=0;fn is not supported and will be removed in a future major release. Did you mean to render instead?")),D.Provider},set:function(Le){D.Provider=Le}},_currentValue:{get:function(){return D._currentValue},set:function(Le){D._currentValue=Le}},_currentValue2:{get:function(){return D._currentValue2},set:function(Le){D._currentValue2=Le}},_threadCount:{get:function(){return D._threadCount},set:function(Le){D._threadCount=Le}},Consumer:{get:function(){return W||(W=!0,re("Rendering is not supported and will be removed in a future major release. Did you mean to render instead?")),D.Consumer}},displayName:{get:function(){return D.displayName},set:function(Le){ye||(nt("Setting `displayName` on Context.Consumer has no effect. You should set it directly on the context with Context.displayName = '%s'.",Le),ye=!0)}}}),D.Consumer=He}return D._currentRenderer=null,D._currentRenderer2=null,D}var aa=-1,zi=0,Ba=1,Ir=2;function Yr(w){if(w._status===aa){var D=w._result,W=D();if(W.then(function(He){if(w._status===zi||w._status===aa){var Le=w;Le._status=Ba,Le._result=He}},function(He){if(w._status===zi||w._status===aa){var Le=w;Le._status=Ir,Le._result=He}}),w._status===aa){var X=w;X._status=zi,X._result=W}}if(w._status===Ba){var ye=w._result;return ye===void 0&&re(`lazy: Expected the result of a dynamic import() call. Instead received: %s Your code should look like: const MyComponent = lazy(() => import('./MyComponent')) @@ -20,21 +22,21 @@ Your code should look like: Did you accidentally put curly braces around the import?`,ye),"default"in ye||re(`lazy: Expected the result of a dynamic import() call. Instead received: %s Your code should look like: - const MyComponent = lazy(() => import('./MyComponent'))`,ye),ye.default}else throw w._result}function gi(w){var D={_status:aa,_result:w},W={$$typeof:_,_payload:D,_init:Yr};{var X,ye;Object.defineProperties(W,{defaultProps:{configurable:!0,get:function(){return X},set:function(He){re("React.lazy(...): It is not supported to assign `defaultProps` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),X=He,Object.defineProperty(W,"defaultProps",{enumerable:!0})}},propTypes:{configurable:!0,get:function(){return ye},set:function(He){re("React.lazy(...): It is not supported to assign `propTypes` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),ye=He,Object.defineProperty(W,"propTypes",{enumerable:!0})}}})}return W}function Ia(w){w!=null&&w.$$typeof===z?re("forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."):typeof w!="function"?re("forwardRef requires a render function but was given %s.",w===null?"null":typeof w):w.length!==0&&w.length!==2&&re("forwardRef render functions accept exactly two parameters: props and ref. %s",w.length===1?"Did you forget to use the ref parameter?":"Any additional parameter will be undefined."),w!=null&&(w.defaultProps!=null||w.propTypes!=null)&&re("forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?");var D={$$typeof:ae,render:w};{var W;Object.defineProperty(D,"displayName",{enumerable:!1,configurable:!0,get:function(){return W},set:function(X){W=X,!w.name&&!w.displayName&&(w.displayName=X)}})}return D}var O;O=Symbol.for("react.module.reference");function ue(w){return!!(typeof w=="string"||typeof w=="function"||w===k||w===U||at||w===g||w===ee||w===x||Ze||w===Z||Dt||ot||Nt||typeof w=="object"&&w!==null&&(w.$$typeof===_||w.$$typeof===z||w.$$typeof===H||w.$$typeof===B||w.$$typeof===ae||w.$$typeof===O||w.getModuleId!==void 0))}function Ee(w,D){ue(w)||re("memo: The first argument must be a component. Instead received: %s",w===null?"null":typeof w);var W={$$typeof:z,type:w,compare:D===void 0?null:D};{var X;Object.defineProperty(W,"displayName",{enumerable:!1,configurable:!0,get:function(){return X},set:function(ye){X=ye,!w.name&&!w.displayName&&(w.displayName=ye)}})}return W}function Te(){var w=ce.current;return w===null&&re(`Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: + const MyComponent = lazy(() => import('./MyComponent'))`,ye),ye.default}else throw w._result}function gi(w){var D={_status:aa,_result:w},W={$$typeof:_,_payload:D,_init:Yr};{var X,ye;Object.defineProperties(W,{defaultProps:{configurable:!0,get:function(){return X},set:function(He){re("React.lazy(...): It is not supported to assign `defaultProps` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),X=He,Object.defineProperty(W,"defaultProps",{enumerable:!0})}},propTypes:{configurable:!0,get:function(){return ye},set:function(He){re("React.lazy(...): It is not supported to assign `propTypes` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),ye=He,Object.defineProperty(W,"propTypes",{enumerable:!0})}}})}return W}function Ia(w){w!=null&&w.$$typeof===z?re("forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."):typeof w!="function"?re("forwardRef requires a render function but was given %s.",w===null?"null":typeof w):w.length!==0&&w.length!==2&&re("forwardRef render functions accept exactly two parameters: props and ref. %s",w.length===1?"Did you forget to use the ref parameter?":"Any additional parameter will be undefined."),w!=null&&(w.defaultProps!=null||w.propTypes!=null)&&re("forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?");var D={$$typeof:ae,render:w};{var W;Object.defineProperty(D,"displayName",{enumerable:!1,configurable:!0,get:function(){return W},set:function(X){W=X,!w.name&&!w.displayName&&(w.displayName=X)}})}return D}var O;O=Symbol.for("react.module.reference");function ue(w){return!!(typeof w=="string"||typeof w=="function"||w===k||w===U||at||w===g||w===ee||w===T||Ze||w===Z||Dt||ot||Nt||typeof w=="object"&&w!==null&&(w.$$typeof===_||w.$$typeof===z||w.$$typeof===H||w.$$typeof===B||w.$$typeof===ae||w.$$typeof===O||w.getModuleId!==void 0))}function Ce(w,D){ue(w)||re("memo: The first argument must be a component. Instead received: %s",w===null?"null":typeof w);var W={$$typeof:z,type:w,compare:D===void 0?null:D};{var X;Object.defineProperty(W,"displayName",{enumerable:!1,configurable:!0,get:function(){return X},set:function(ye){X=ye,!w.name&&!w.displayName&&(w.displayName=ye)}})}return W}function Te(){var w=ce.current;return w===null&&re(`Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app -See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.`),w}function Et(w){var D=Te();if(w._context!==void 0){var W=w._context;W.Consumer===w?re("Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be removed in a future major release. Did you mean to call useContext(Context) instead?"):W.Provider===w&&re("Calling useContext(Context.Provider) is not supported. Did you mean to call useContext(Context) instead?")}return D.useContext(w)}function Ot(w){var D=Te();return D.useState(w)}function mt(w,D,W){var X=Te();return X.useReducer(w,D,W)}function Qe(w){var D=Te();return D.useRef(w)}function Pn(w,D){var W=Te();return W.useEffect(w,D)}function an(w,D){var W=Te();return W.useInsertionEffect(w,D)}function ln(w,D){var W=Te();return W.useLayoutEffect(w,D)}function Er(w,D){var W=Te();return W.useCallback(w,D)}function wa(w,D){var W=Te();return W.useMemo(w,D)}function on(w,D,W){var X=Te();return X.useImperativeHandle(w,D,W)}function Wr(w,D){{var W=Te();return W.useDebugValue(w,D)}}function hs(){var w=Te();return w.useTransition()}function Sa(w){var D=Te();return D.useDeferredValue(w)}function yt(){var w=Te();return w.useId()}function fo(w,D,W){var X=Te();return X.useSyncExternalStore(w,D,W)}var Ya=0,xl,Qr,ys,Lr,gs,ws,Ss;function po(){}po.__reactDisabledLog=!0;function au(){{if(Ya===0){xl=console.log,Qr=console.info,ys=console.warn,Lr=console.error,gs=console.group,ws=console.groupCollapsed,Ss=console.groupEnd;var w={configurable:!0,enumerable:!0,value:po,writable:!0};Object.defineProperties(console,{info:w,log:w,warn:w,error:w,group:w,groupCollapsed:w,groupEnd:w})}Ya++}}function Wa(){{if(Ya--,Ya===0){var w={configurable:!0,enumerable:!0,writable:!0};Object.defineProperties(console,{log:Pe({},w,{value:xl}),info:Pe({},w,{value:Qr}),warn:Pe({},w,{value:ys}),error:Pe({},w,{value:Lr}),group:Pe({},w,{value:gs}),groupCollapsed:Pe({},w,{value:ws}),groupEnd:Pe({},w,{value:Ss})})}Ya<0&&re("disabledDepth fell below zero. This is a bug in React. Please file an issue.")}}var ba=bt.ReactCurrentDispatcher,Ai;function Tl(w,D,W){{if(Ai===void 0)try{throw Error()}catch(ye){var X=ye.stack.trim().match(/\n( *(at )?)/);Ai=X&&X[1]||""}return` -`+Ai+w}}var Ca=!1,vo;{var mo=typeof WeakMap=="function"?WeakMap:Map;vo=new mo}function Rl(w,D){if(!w||Ca)return"";{var W=vo.get(w);if(W!==void 0)return W}var X;Ca=!0;var ye=Error.prepareStackTrace;Error.prepareStackTrace=void 0;var He;He=ba.current,ba.current=null,au();try{if(D){var Le=function(){throw Error()};if(Object.defineProperty(Le.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(Le,[])}catch(At){X=At}Reflect.construct(w,[],Le)}else{try{Le.call()}catch(At){X=At}w.call(Le.prototype)}}else{try{throw Error()}catch(At){X=At}w()}}catch(At){if(At&&X&&typeof At.stack=="string"){for(var rt=At.stack.split(` +See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.`),w}function Ct(w){var D=Te();if(w._context!==void 0){var W=w._context;W.Consumer===w?re("Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be removed in a future major release. Did you mean to call useContext(Context) instead?"):W.Provider===w&&re("Calling useContext(Context.Provider) is not supported. Did you mean to call useContext(Context) instead?")}return D.useContext(w)}function Ot(w){var D=Te();return D.useState(w)}function mt(w,D,W){var X=Te();return X.useReducer(w,D,W)}function Qe(w){var D=Te();return D.useRef(w)}function Pn(w,D){var W=Te();return W.useEffect(w,D)}function an(w,D){var W=Te();return W.useInsertionEffect(w,D)}function ln(w,D){var W=Te();return W.useLayoutEffect(w,D)}function Cr(w,D){var W=Te();return W.useCallback(w,D)}function wa(w,D){var W=Te();return W.useMemo(w,D)}function on(w,D,W){var X=Te();return X.useImperativeHandle(w,D,W)}function Wr(w,D){{var W=Te();return W.useDebugValue(w,D)}}function hs(){var w=Te();return w.useTransition()}function Sa(w){var D=Te();return D.useDeferredValue(w)}function yt(){var w=Te();return w.useId()}function fo(w,D,W){var X=Te();return X.useSyncExternalStore(w,D,W)}var Ya=0,xl,Qr,ys,Lr,gs,ws,Ss;function po(){}po.__reactDisabledLog=!0;function au(){{if(Ya===0){xl=console.log,Qr=console.info,ys=console.warn,Lr=console.error,gs=console.group,ws=console.groupCollapsed,Ss=console.groupEnd;var w={configurable:!0,enumerable:!0,value:po,writable:!0};Object.defineProperties(console,{info:w,log:w,warn:w,error:w,group:w,groupCollapsed:w,groupEnd:w})}Ya++}}function Wa(){{if(Ya--,Ya===0){var w={configurable:!0,enumerable:!0,writable:!0};Object.defineProperties(console,{log:Pe({},w,{value:xl}),info:Pe({},w,{value:Qr}),warn:Pe({},w,{value:ys}),error:Pe({},w,{value:Lr}),group:Pe({},w,{value:gs}),groupCollapsed:Pe({},w,{value:ws}),groupEnd:Pe({},w,{value:Ss})})}Ya<0&&re("disabledDepth fell below zero. This is a bug in React. Please file an issue.")}}var ba=bt.ReactCurrentDispatcher,Ai;function Tl(w,D,W){{if(Ai===void 0)try{throw Error()}catch(ye){var X=ye.stack.trim().match(/\n( *(at )?)/);Ai=X&&X[1]||""}return` +`+Ai+w}}var Ea=!1,vo;{var mo=typeof WeakMap=="function"?WeakMap:Map;vo=new mo}function Rl(w,D){if(!w||Ea)return"";{var W=vo.get(w);if(W!==void 0)return W}var X;Ea=!0;var ye=Error.prepareStackTrace;Error.prepareStackTrace=void 0;var He;He=ba.current,ba.current=null,au();try{if(D){var Le=function(){throw Error()};if(Object.defineProperty(Le.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(Le,[])}catch(At){X=At}Reflect.construct(w,[],Le)}else{try{Le.call()}catch(At){X=At}w.call(Le.prototype)}}else{try{throw Error()}catch(At){X=At}w()}}catch(At){if(At&&X&&typeof At.stack=="string"){for(var rt=At.stack.split(` `),gt=X.stack.split(` `),Wt=rt.length-1,qt=gt.length-1;Wt>=1&&qt>=0&&rt[Wt]!==gt[qt];)qt--;for(;Wt>=1&&qt>=0;Wt--,qt--)if(rt[Wt]!==gt[qt]){if(Wt!==1||qt!==1)do if(Wt--,qt--,qt<0||rt[Wt]!==gt[qt]){var Xt=` -`+rt[Wt].replace(" at new "," at ");return w.displayName&&Xt.includes("")&&(Xt=Xt.replace("",w.displayName)),typeof w=="function"&&vo.set(w,Xt),Xt}while(Wt>=1&&qt>=0);break}}}finally{Ca=!1,ba.current=He,Wa(),Error.prepareStackTrace=ye}var Zt=w?w.displayName||w.name:"",fn=Zt?Tl(Zt):"";return typeof w=="function"&&vo.set(w,fn),fn}function bs(w,D,W){return Rl(w,!1)}function Cs(w){var D=w.prototype;return!!(D&&D.isReactComponent)}function kt(w,D,W){if(w==null)return"";if(typeof w=="function")return Rl(w,Cs(w));if(typeof w=="string")return Tl(w);switch(w){case ee:return Tl("Suspense");case x:return Tl("SuspenseList")}if(typeof w=="object")switch(w.$$typeof){case ae:return bs(w.render);case z:return kt(w.type,D,W);case _:{var X=w,ye=X._payload,He=X._init;try{return kt(He(ye),D,W)}catch{}}}return""}var Es={},lu=bt.ReactDebugCurrentFrame;function kl(w){if(w){var D=w._owner,W=kt(w.type,w._source,D?D.type:null);lu.setExtraStackFrame(W)}else lu.setExtraStackFrame(null)}function xs(w,D,W,X,ye){{var He=Function.call.bind(di);for(var Le in w)if(He(w,Le)){var rt=void 0;try{if(typeof w[Le]!="function"){var gt=Error((X||"React class")+": "+W+" type `"+Le+"` is invalid; it must be a function, usually from the `prop-types` package, but received `"+typeof w[Le]+"`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.");throw gt.name="Invariant Violation",gt}rt=w[Le](D,Le,X,W,null,"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED")}catch(Wt){rt=Wt}rt&&!(rt instanceof Error)&&(kl(ye),re("%s: type specification of %s `%s` is invalid; the type checker function must return `null` or an `Error` but returned a %s. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).",X||"React class",W,Le,typeof rt),kl(null)),rt instanceof Error&&!(rt.message in Es)&&(Es[rt.message]=!0,kl(ye),re("Failed %s type: %s",W,rt.message),kl(null))}}}function zt(w){if(w){var D=w._owner,W=kt(w.type,w._source,D?D.type:null);Ut(W)}else Ut(null)}var ou;ou=!1;function ho(){if(be.current){var w=Qn(be.current.type);if(w)return` +`+rt[Wt].replace(" at new "," at ");return w.displayName&&Xt.includes("")&&(Xt=Xt.replace("",w.displayName)),typeof w=="function"&&vo.set(w,Xt),Xt}while(Wt>=1&&qt>=0);break}}}finally{Ea=!1,ba.current=He,Wa(),Error.prepareStackTrace=ye}var Zt=w?w.displayName||w.name:"",fn=Zt?Tl(Zt):"";return typeof w=="function"&&vo.set(w,fn),fn}function bs(w,D,W){return Rl(w,!1)}function Es(w){var D=w.prototype;return!!(D&&D.isReactComponent)}function kt(w,D,W){if(w==null)return"";if(typeof w=="function")return Rl(w,Es(w));if(typeof w=="string")return Tl(w);switch(w){case ee:return Tl("Suspense");case T:return Tl("SuspenseList")}if(typeof w=="object")switch(w.$$typeof){case ae:return bs(w.render);case z:return kt(w.type,D,W);case _:{var X=w,ye=X._payload,He=X._init;try{return kt(He(ye),D,W)}catch{}}}return""}var Cs={},lu=bt.ReactDebugCurrentFrame;function kl(w){if(w){var D=w._owner,W=kt(w.type,w._source,D?D.type:null);lu.setExtraStackFrame(W)}else lu.setExtraStackFrame(null)}function xs(w,D,W,X,ye){{var He=Function.call.bind(di);for(var Le in w)if(He(w,Le)){var rt=void 0;try{if(typeof w[Le]!="function"){var gt=Error((X||"React class")+": "+W+" type `"+Le+"` is invalid; it must be a function, usually from the `prop-types` package, but received `"+typeof w[Le]+"`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.");throw gt.name="Invariant Violation",gt}rt=w[Le](D,Le,X,W,null,"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED")}catch(Wt){rt=Wt}rt&&!(rt instanceof Error)&&(kl(ye),re("%s: type specification of %s `%s` is invalid; the type checker function must return `null` or an `Error` but returned a %s. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).",X||"React class",W,Le,typeof rt),kl(null)),rt instanceof Error&&!(rt.message in Cs)&&(Cs[rt.message]=!0,kl(ye),re("Failed %s type: %s",W,rt.message),kl(null))}}}function zt(w){if(w){var D=w._owner,W=kt(w.type,w._source,D?D.type:null);Ut(W)}else Ut(null)}var ou;ou=!1;function ho(){if(be.current){var w=Qn(be.current.type);if(w)return` Check the render method of \``+w+"`."}return""}function lt(w){if(w!==void 0){var D=w.fileName.replace(/^.*[\\\/]/,""),W=w.lineNumber;return` Check your code at `+D+":"+W+"."}return""}function la(w){return w!=null?lt(w.__source):""}var gn={};function Gr(w){var D=ho();if(!D){var W=typeof w=="string"?w:w.displayName||w.name;W&&(D=` -Check the top-level render call using <`+W+">.")}return D}function Ui(w,D){if(!(!w._store||w._store.validated||w.key!=null)){w._store.validated=!0;var W=Gr(D);if(!gn[W]){gn[W]=!0;var X="";w&&w._owner&&w._owner!==be.current&&(X=" It was passed a child from "+Qn(w._owner.type)+"."),zt(w),re('Each child in a list should have a unique "key" prop.%s%s See https://reactjs.org/link/warning-keys for more information.',W,X),zt(null)}}}function _l(w,D){if(typeof w=="object"){if(xn(w))for(var W=0;W",ye=" Did you accidentally export a JSX literal instead of a component?"):Le=typeof w,re("React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",Le,ye)}var rt=We.apply(this,arguments);if(rt==null)return rt;if(X)for(var gt=2;gt10&&nt("Detected a large number of updates inside startTransition. If this is due to a subscription please re-write it to use React provided hooks. Otherwise concurrent mode guarantees are off the table."),X._updatedFibers.clear()}}}var yo=!1,go=null;function Dl(w){if(go===null)try{var D=("require"+Math.random()).slice(0,7),W=c&&c[D];go=W.call(c,"timers").setImmediate}catch{go=function(ye){yo===!1&&(yo=!0,typeof MessageChannel>"u"&&re("This browser does not have a MessageChannel implementation, so enqueuing tasks via await act(async () => ...) will fail. Please file an issue at https://github.com/facebook/react/issues if you encounter this warning."));var He=new MessageChannel;He.port1.onmessage=ye,He.port2.postMessage(void 0)}}return go(w)}var ji=0,Qa=!1;function su(w){{var D=ji;ji++,ve.current===null&&(ve.current=[]);var W=ve.isBatchingLegacy,X;try{if(ve.isBatchingLegacy=!0,X=w(),!W&&ve.didScheduleLegacyUpdate){var ye=ve.current;ye!==null&&(ve.didScheduleLegacyUpdate=!1,qa(ye))}}catch(Zt){throw Ga(D),Zt}finally{ve.isBatchingLegacy=W}if(X!==null&&typeof X=="object"&&typeof X.then=="function"){var He=X,Le=!1,rt={then:function(Zt,fn){Le=!0,He.then(function(At){Ga(D),ji===0?cu(At,Zt,fn):Zt(At)},function(At){Ga(D),fn(At)})}};return!Qa&&typeof Promise<"u"&&Promise.resolve().then(function(){}).then(function(){Le||(Qa=!0,re("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"))}),rt}else{var gt=X;if(Ga(D),ji===0){var Wt=ve.current;Wt!==null&&(qa(Wt),ve.current=null);var qt={then:function(Zt,fn){ve.current===null?(ve.current=[],cu(gt,Zt,fn)):Zt(gt)}};return qt}else{var Xt={then:function(Zt,fn){Zt(gt)}};return Xt}}}}function Ga(w){w!==ji-1&&re("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. "),ji=w}function cu(w,D,W){{var X=ve.current;if(X!==null)try{qa(X),Dl(function(){X.length===0?(ve.current=null,D(w)):cu(w,D,W)})}catch(ye){W(ye)}else D(w)}}var Ea=!1;function qa(w){if(!Ea){Ea=!0;var D=0;try{for(;D.")}return D}function Ui(w,D){if(!(!w._store||w._store.validated||w.key!=null)){w._store.validated=!0;var W=Gr(D);if(!gn[W]){gn[W]=!0;var X="";w&&w._owner&&w._owner!==be.current&&(X=" It was passed a child from "+Qn(w._owner.type)+"."),zt(w),re('Each child in a list should have a unique "key" prop.%s%s See https://reactjs.org/link/warning-keys for more information.',W,X),zt(null)}}}function _l(w,D){if(typeof w=="object"){if(xn(w))for(var W=0;W",ye=" Did you accidentally export a JSX literal instead of a component?"):Le=typeof w,re("React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",Le,ye)}var rt=We.apply(this,arguments);if(rt==null)return rt;if(X)for(var gt=2;gt10&&nt("Detected a large number of updates inside startTransition. If this is due to a subscription please re-write it to use React provided hooks. Otherwise concurrent mode guarantees are off the table."),X._updatedFibers.clear()}}}var yo=!1,go=null;function Dl(w){if(go===null)try{var D=("require"+Math.random()).slice(0,7),W=c&&c[D];go=W.call(c,"timers").setImmediate}catch{go=function(ye){yo===!1&&(yo=!0,typeof MessageChannel>"u"&&re("This browser does not have a MessageChannel implementation, so enqueuing tasks via await act(async () => ...) will fail. Please file an issue at https://github.com/facebook/react/issues if you encounter this warning."));var He=new MessageChannel;He.port1.onmessage=ye,He.port2.postMessage(void 0)}}return go(w)}var ji=0,Qa=!1;function su(w){{var D=ji;ji++,ve.current===null&&(ve.current=[]);var W=ve.isBatchingLegacy,X;try{if(ve.isBatchingLegacy=!0,X=w(),!W&&ve.didScheduleLegacyUpdate){var ye=ve.current;ye!==null&&(ve.didScheduleLegacyUpdate=!1,qa(ye))}}catch(Zt){throw Ga(D),Zt}finally{ve.isBatchingLegacy=W}if(X!==null&&typeof X=="object"&&typeof X.then=="function"){var He=X,Le=!1,rt={then:function(Zt,fn){Le=!0,He.then(function(At){Ga(D),ji===0?cu(At,Zt,fn):Zt(At)},function(At){Ga(D),fn(At)})}};return!Qa&&typeof Promise<"u"&&Promise.resolve().then(function(){}).then(function(){Le||(Qa=!0,re("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"))}),rt}else{var gt=X;if(Ga(D),ji===0){var Wt=ve.current;Wt!==null&&(qa(Wt),ve.current=null);var qt={then:function(Zt,fn){ve.current===null?(ve.current=[],cu(gt,Zt,fn)):Zt(gt)}};return qt}else{var Xt={then:function(Zt,fn){Zt(gt)}};return Xt}}}}function Ga(w){w!==ji-1&&re("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. "),ji=w}function cu(w,D,W){{var X=ve.current;if(X!==null)try{qa(X),Dl(function(){X.length===0?(ve.current=null,D(w)):cu(w,D,W)})}catch(ye){W(ye)}else D(w)}}var Ca=!1;function qa(w){if(!Ca){Ca=!0;var D=0;try{for(;D.")}return D}function Ui(w,D){if(!( * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Xw;function nT(){if(Xw)return bd;Xw=1;var c=I,p=Symbol.for("react.element"),m=Symbol.for("react.fragment"),S=Object.prototype.hasOwnProperty,T=c.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,N={key:!0,ref:!0,__self:!0,__source:!0};function k(g,U,H){var B,ae={},ee=null,x=null;H!==void 0&&(ee=""+H),U.key!==void 0&&(ee=""+U.key),U.ref!==void 0&&(x=U.ref);for(B in U)S.call(U,B)&&!N.hasOwnProperty(B)&&(ae[B]=U[B]);if(g&&g.defaultProps)for(B in U=g.defaultProps,U)ae[B]===void 0&&(ae[B]=U[B]);return{$$typeof:p,type:g,key:ee,ref:x,props:ae,_owner:T.current}}return bd.Fragment=m,bd.jsx=k,bd.jsxs=k,bd}var xd={},Zw;function rT(){if(Zw)return xd;Zw=1;var c={};/** + */var Xw;function rT(){if(Xw)return Ed;Xw=1;var c=I,p=Symbol.for("react.element"),m=Symbol.for("react.fragment"),S=Object.prototype.hasOwnProperty,x=c.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,N={key:!0,ref:!0,__self:!0,__source:!0};function k(g,U,H){var B,ae={},ee=null,T=null;H!==void 0&&(ee=""+H),U.key!==void 0&&(ee=""+U.key),U.ref!==void 0&&(T=U.ref);for(B in U)S.call(U,B)&&!N.hasOwnProperty(B)&&(ae[B]=U[B]);if(g&&g.defaultProps)for(B in U=g.defaultProps,U)ae[B]===void 0&&(ae[B]=U[B]);return{$$typeof:p,type:g,key:ee,ref:T,props:ae,_owner:x.current}}return Ed.Fragment=m,Ed.jsx=k,Ed.jsxs=k,Ed}var Td={},Zw;function iT(){if(Zw)return Td;Zw=1;var c={};/** * @license React * react-jsx-runtime.development.js * @@ -50,15 +52,15 @@ Check the top-level render call using <`+W+">.")}return D}function Ui(w,D){if(!( * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */return c.NODE_ENV!=="production"&&function(){var p=I,m=Symbol.for("react.element"),S=Symbol.for("react.portal"),T=Symbol.for("react.fragment"),N=Symbol.for("react.strict_mode"),k=Symbol.for("react.profiler"),g=Symbol.for("react.provider"),U=Symbol.for("react.context"),H=Symbol.for("react.forward_ref"),B=Symbol.for("react.suspense"),ae=Symbol.for("react.suspense_list"),ee=Symbol.for("react.memo"),x=Symbol.for("react.lazy"),z=Symbol.for("react.offscreen"),_=Symbol.iterator,Z="@@iterator";function me(O){if(O===null||typeof O!="object")return null;var ue=_&&O[_]||O[Z];return typeof ue=="function"?ue:null}var he=p.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;function de(O){{for(var ue=arguments.length,Ee=new Array(ue>1?ue-1:0),Te=1;Te1?ue-1:0),Te=1;Te=1&&ln>=0&&Qe[an]!==Pn[ln];)ln--;for(;an>=1&&ln>=0;an--,ln--)if(Qe[an]!==Pn[ln]){if(an!==1||ln!==1)do if(an--,ln--,ln<0||Qe[an]!==Pn[ln]){var Er=` -`+Qe[an].replace(" at new "," at ");return O.displayName&&Er.includes("")&&(Er=Er.replace("",O.displayName)),typeof O=="function"&&Bt.set(O,Er),Er}while(an>=1&&ln>=0);break}}}finally{en=!1,St.current=Ot,pt(),Error.prepareStackTrace=Et}var wa=O?O.displayName||O.name:"",on=wa?dt(wa):"";return typeof O=="function"&&Bt.set(O,on),on}function xn(O,ue,Ee){return wr(O,!1)}function ar(O){var ue=O.prototype;return!!(ue&&ue.isReactComponent)}function Wn(O,ue,Ee){if(O==null)return"";if(typeof O=="function")return wr(O,ar(O));if(typeof O=="string")return dt(O);switch(O){case B:return dt("Suspense");case ae:return dt("SuspenseList")}if(typeof O=="object")switch(O.$$typeof){case H:return xn(O.render);case ee:return Wn(O.type,ue,Ee);case x:{var Te=O,Et=Te._payload,Ot=Te._init;try{return Wn(Ot(Et),ue,Ee)}catch{}}}return""}var Hn=Object.prototype.hasOwnProperty,$n={},Dr=he.ReactDebugCurrentFrame;function Sr(O){if(O){var ue=O._owner,Ee=Wn(O.type,O._source,ue?ue.type:null);Dr.setExtraStackFrame(Ee)}else Dr.setExtraStackFrame(null)}function Qn(O,ue,Ee,Te,Et){{var Ot=Function.call.bind(Hn);for(var mt in O)if(Ot(O,mt)){var Qe=void 0;try{if(typeof O[mt]!="function"){var Pn=Error((Te||"React class")+": "+Ee+" type `"+mt+"` is invalid; it must be a function, usually from the `prop-types` package, but received `"+typeof O[mt]+"`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.");throw Pn.name="Invariant Violation",Pn}Qe=O[mt](ue,mt,Te,Ee,null,"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED")}catch(an){Qe=an}Qe&&!(Qe instanceof Error)&&(Sr(Et),de("%s: type specification of %s `%s` is invalid; the type checker function must return `null` or an `Error` but returned a %s. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).",Te||"React class",Ee,mt,typeof Qe),Sr(null)),Qe instanceof Error&&!(Qe.message in $n)&&($n[Qe.message]=!0,Sr(Et),de("Failed %s type: %s",Ee,Qe.message),Sr(null))}}}var di=Array.isArray;function pi(O){return di(O)}function br(O){{var ue=typeof Symbol=="function"&&Symbol.toStringTag,Ee=ue&&O[Symbol.toStringTag]||O.constructor.name||"Object";return Ee}}function vi(O){try{return lr(O),!1}catch{return!0}}function lr(O){return""+O}function Or(O){if(vi(O))return de("The provided key is an unsupported type %s. This value must be coerced to a string before before using it here.",br(O)),lr(O)}var mn=he.ReactCurrentOwner,mi={key:!0,ref:!0,__self:!0,__source:!0},hi,yi,Ce;Ce={};function We(O){if(Hn.call(O,"ref")){var ue=Object.getOwnPropertyDescriptor(O,"ref").get;if(ue&&ue.isReactWarning)return!1}return O.ref!==void 0}function ht(O){if(Hn.call(O,"key")){var ue=Object.getOwnPropertyDescriptor(O,"key").get;if(ue&&ue.isReactWarning)return!1}return O.key!==void 0}function It(O,ue){if(typeof O.ref=="string"&&mn.current&&ue&&mn.current.stateNode!==ue){var Ee=Ze(mn.current.type);Ce[Ee]||(de('Component "%s" contains the string ref "%s". Support for string refs will be removed in a future major release. This case cannot be automatically converted to an arrow function. We ask you to manually fix this case by using useRef() or createRef() instead. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref',Ze(mn.current.type),O.ref),Ce[Ee]=!0)}}function Ft(O,ue){{var Ee=function(){hi||(hi=!0,de("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)",ue))};Ee.isReactWarning=!0,Object.defineProperty(O,"key",{get:Ee,configurable:!0})}}function Ln(O,ue){{var Ee=function(){yi||(yi=!0,de("%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)",ue))};Ee.isReactWarning=!0,Object.defineProperty(O,"ref",{get:Ee,configurable:!0})}}var yn=function(O,ue,Ee,Te,Et,Ot,mt){var Qe={$$typeof:m,type:O,key:ue,ref:Ee,props:mt,_owner:Ot};return Qe._store={},Object.defineProperty(Qe._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:!1}),Object.defineProperty(Qe,"_self",{configurable:!1,enumerable:!1,writable:!1,value:Te}),Object.defineProperty(Qe,"_source",{configurable:!1,enumerable:!1,writable:!1,value:Et}),Object.freeze&&(Object.freeze(Qe.props),Object.freeze(Qe)),Qe};function Cr(O,ue,Ee,Te,Et){{var Ot,mt={},Qe=null,Pn=null;Ee!==void 0&&(Or(Ee),Qe=""+Ee),ht(ue)&&(Or(ue.key),Qe=""+ue.key),We(ue)&&(Pn=ue.ref,It(ue,Et));for(Ot in ue)Hn.call(ue,Ot)&&!mi.hasOwnProperty(Ot)&&(mt[Ot]=ue[Ot]);if(O&&O.defaultProps){var an=O.defaultProps;for(Ot in an)mt[Ot]===void 0&&(mt[Ot]=an[Ot])}if(Qe||Pn){var ln=typeof O=="function"?O.displayName||O.name||"Unknown":O;Qe&&Ft(mt,ln),Pn&&Ln(mt,ln)}return yn(O,Qe,Pn,Et,Te,mn.current,mt)}}var Gt=he.ReactCurrentOwner,Jn=he.ReactDebugCurrentFrame;function Yt(O){if(O){var ue=O._owner,Ee=Wn(O.type,O._source,ue?ue.type:null);Jn.setExtraStackFrame(Ee)}else Jn.setExtraStackFrame(null)}var rn;rn=!1;function ra(O){return typeof O=="object"&&O!==null&&O.$$typeof===m}function Ni(){{if(Gt.current){var O=Ze(Gt.current.type);if(O)return` +`),an=Qe.length-1,ln=Pn.length-1;an>=1&&ln>=0&&Qe[an]!==Pn[ln];)ln--;for(;an>=1&&ln>=0;an--,ln--)if(Qe[an]!==Pn[ln]){if(an!==1||ln!==1)do if(an--,ln--,ln<0||Qe[an]!==Pn[ln]){var Cr=` +`+Qe[an].replace(" at new "," at ");return O.displayName&&Cr.includes("")&&(Cr=Cr.replace("",O.displayName)),typeof O=="function"&&Bt.set(O,Cr),Cr}while(an>=1&&ln>=0);break}}}finally{en=!1,St.current=Ot,pt(),Error.prepareStackTrace=Ct}var wa=O?O.displayName||O.name:"",on=wa?dt(wa):"";return typeof O=="function"&&Bt.set(O,on),on}function xn(O,ue,Ce){return wr(O,!1)}function ar(O){var ue=O.prototype;return!!(ue&&ue.isReactComponent)}function Wn(O,ue,Ce){if(O==null)return"";if(typeof O=="function")return wr(O,ar(O));if(typeof O=="string")return dt(O);switch(O){case B:return dt("Suspense");case ae:return dt("SuspenseList")}if(typeof O=="object")switch(O.$$typeof){case H:return xn(O.render);case ee:return Wn(O.type,ue,Ce);case T:{var Te=O,Ct=Te._payload,Ot=Te._init;try{return Wn(Ot(Ct),ue,Ce)}catch{}}}return""}var Hn=Object.prototype.hasOwnProperty,$n={},Dr=he.ReactDebugCurrentFrame;function Sr(O){if(O){var ue=O._owner,Ce=Wn(O.type,O._source,ue?ue.type:null);Dr.setExtraStackFrame(Ce)}else Dr.setExtraStackFrame(null)}function Qn(O,ue,Ce,Te,Ct){{var Ot=Function.call.bind(Hn);for(var mt in O)if(Ot(O,mt)){var Qe=void 0;try{if(typeof O[mt]!="function"){var Pn=Error((Te||"React class")+": "+Ce+" type `"+mt+"` is invalid; it must be a function, usually from the `prop-types` package, but received `"+typeof O[mt]+"`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.");throw Pn.name="Invariant Violation",Pn}Qe=O[mt](ue,mt,Te,Ce,null,"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED")}catch(an){Qe=an}Qe&&!(Qe instanceof Error)&&(Sr(Ct),de("%s: type specification of %s `%s` is invalid; the type checker function must return `null` or an `Error` but returned a %s. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).",Te||"React class",Ce,mt,typeof Qe),Sr(null)),Qe instanceof Error&&!(Qe.message in $n)&&($n[Qe.message]=!0,Sr(Ct),de("Failed %s type: %s",Ce,Qe.message),Sr(null))}}}var di=Array.isArray;function pi(O){return di(O)}function br(O){{var ue=typeof Symbol=="function"&&Symbol.toStringTag,Ce=ue&&O[Symbol.toStringTag]||O.constructor.name||"Object";return Ce}}function vi(O){try{return lr(O),!1}catch{return!0}}function lr(O){return""+O}function Or(O){if(vi(O))return de("The provided key is an unsupported type %s. This value must be coerced to a string before before using it here.",br(O)),lr(O)}var mn=he.ReactCurrentOwner,mi={key:!0,ref:!0,__self:!0,__source:!0},hi,yi,Ee;Ee={};function We(O){if(Hn.call(O,"ref")){var ue=Object.getOwnPropertyDescriptor(O,"ref").get;if(ue&&ue.isReactWarning)return!1}return O.ref!==void 0}function ht(O){if(Hn.call(O,"key")){var ue=Object.getOwnPropertyDescriptor(O,"key").get;if(ue&&ue.isReactWarning)return!1}return O.key!==void 0}function It(O,ue){if(typeof O.ref=="string"&&mn.current&&ue&&mn.current.stateNode!==ue){var Ce=Ze(mn.current.type);Ee[Ce]||(de('Component "%s" contains the string ref "%s". Support for string refs will be removed in a future major release. This case cannot be automatically converted to an arrow function. We ask you to manually fix this case by using useRef() or createRef() instead. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref',Ze(mn.current.type),O.ref),Ee[Ce]=!0)}}function Ft(O,ue){{var Ce=function(){hi||(hi=!0,de("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)",ue))};Ce.isReactWarning=!0,Object.defineProperty(O,"key",{get:Ce,configurable:!0})}}function Ln(O,ue){{var Ce=function(){yi||(yi=!0,de("%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)",ue))};Ce.isReactWarning=!0,Object.defineProperty(O,"ref",{get:Ce,configurable:!0})}}var yn=function(O,ue,Ce,Te,Ct,Ot,mt){var Qe={$$typeof:m,type:O,key:ue,ref:Ce,props:mt,_owner:Ot};return Qe._store={},Object.defineProperty(Qe._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:!1}),Object.defineProperty(Qe,"_self",{configurable:!1,enumerable:!1,writable:!1,value:Te}),Object.defineProperty(Qe,"_source",{configurable:!1,enumerable:!1,writable:!1,value:Ct}),Object.freeze&&(Object.freeze(Qe.props),Object.freeze(Qe)),Qe};function Er(O,ue,Ce,Te,Ct){{var Ot,mt={},Qe=null,Pn=null;Ce!==void 0&&(Or(Ce),Qe=""+Ce),ht(ue)&&(Or(ue.key),Qe=""+ue.key),We(ue)&&(Pn=ue.ref,It(ue,Ct));for(Ot in ue)Hn.call(ue,Ot)&&!mi.hasOwnProperty(Ot)&&(mt[Ot]=ue[Ot]);if(O&&O.defaultProps){var an=O.defaultProps;for(Ot in an)mt[Ot]===void 0&&(mt[Ot]=an[Ot])}if(Qe||Pn){var ln=typeof O=="function"?O.displayName||O.name||"Unknown":O;Qe&&Ft(mt,ln),Pn&&Ln(mt,ln)}return yn(O,Qe,Pn,Ct,Te,mn.current,mt)}}var Gt=he.ReactCurrentOwner,Jn=he.ReactDebugCurrentFrame;function Yt(O){if(O){var ue=O._owner,Ce=Wn(O.type,O._source,ue?ue.type:null);Jn.setExtraStackFrame(Ce)}else Jn.setExtraStackFrame(null)}var rn;rn=!1;function ra(O){return typeof O=="object"&&O!==null&&O.$$typeof===m}function Ni(){{if(Gt.current){var O=Ze(Gt.current.type);if(O)return` -Check the render method of \``+O+"`."}return""}}function Cl(O){return""}var so={};function co(O){{var ue=Ni();if(!ue){var Ee=typeof O=="string"?O:O.displayName||O.name;Ee&&(ue=` +Check the render method of \``+O+"`."}return""}}function El(O){return""}var so={};function co(O){{var ue=Ni();if(!ue){var Ce=typeof O=="string"?O:O.displayName||O.name;Ce&&(ue=` -Check the top-level render call using <`+Ee+">.")}return ue}}function El(O,ue){{if(!O._store||O._store.validated||O.key!=null)return;O._store.validated=!0;var Ee=co(ue);if(so[Ee])return;so[Ee]=!0;var Te="";O&&O._owner&&O._owner!==Gt.current&&(Te=" It was passed a child from "+Ze(O._owner.type)+"."),Yt(O),de('Each child in a list should have a unique "key" prop.%s%s See https://reactjs.org/link/warning-keys for more information.',Ee,Te),Yt(null)}}function ia(O,ue){{if(typeof O!="object")return;if(pi(O))for(var Ee=0;Ee",Qe=" Did you accidentally export a JSX literal instead of a component?"):an=typeof O,de("React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",an,Qe)}var ln=Cr(O,ue,Ee,Et,Ot);if(ln==null)return ln;if(mt){var Er=ue.children;if(Er!==void 0)if(Te)if(pi(Er)){for(var wa=0;wa.")}return ue}}function Cl(O,ue){{if(!O._store||O._store.validated||O.key!=null)return;O._store.validated=!0;var Ce=co(ue);if(so[Ce])return;so[Ce]=!0;var Te="";O&&O._owner&&O._owner!==Gt.current&&(Te=" It was passed a child from "+Ze(O._owner.type)+"."),Yt(O),de('Each child in a list should have a unique "key" prop.%s%s See https://reactjs.org/link/warning-keys for more information.',Ce,Te),Yt(null)}}function ia(O,ue){{if(typeof O!="object")return;if(pi(O))for(var Ce=0;Ce",Qe=" Did you accidentally export a JSX literal instead of a component?"):an=typeof O,de("React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",an,Qe)}var ln=Er(O,ue,Ce,Ct,Ot);if(ln==null)return ln;if(mt){var Cr=ue.children;if(Cr!==void 0)if(Te)if(pi(Cr)){for(var wa=0;wa.")}return ue}}function El(O,ue){{ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Kw;function aT(){return Kw||(Kw=1,function(c){function p(re,De){var L=re.length;re.push(De);e:for(;0>>1,Oe=re[te];if(0>>1;teT(wt,L))ptT(St,wt)?(re[te]=St,re[pt]=L,te=pt):(re[te]=wt,re[Ke]=L,te=Ke);else if(ptT(St,L))re[te]=St,re[pt]=L,te=pt;else break e}}return De}function T(re,De){var L=re.sortIndex-De.sortIndex;return L!==0?L:re.id-De.id}if(typeof performance=="object"&&typeof performance.now=="function"){var N=performance;c.unstable_now=function(){return N.now()}}else{var k=Date,g=k.now();c.unstable_now=function(){return k.now()-g}}var U=[],H=[],B=1,ae=null,ee=3,x=!1,z=!1,_=!1,Z=typeof setTimeout=="function"?setTimeout:null,me=typeof clearTimeout=="function"?clearTimeout:null,he=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function de(re){for(var De=m(H);De!==null;){if(De.callback===null)S(H);else if(De.startTime<=re)S(H),De.sortIndex=De.expirationTime,p(U,De);else break;De=m(H)}}function ce(re){if(_=!1,de(re),!z)if(m(U)!==null)z=!0,bt(ze);else{var De=m(H);De!==null&&nt(ce,De.startTime-re)}}function ze(re,De){z=!1,_&&(_=!1,me(xe),xe=-1),x=!0;var L=ee;try{for(de(De),ae=m(U);ae!==null&&(!(ae.expirationTime>De)||re&&!Dt());){var te=ae.callback;if(typeof te=="function"){ae.callback=null,ee=ae.priorityLevel;var Oe=te(ae.expirationTime<=De);De=c.unstable_now(),typeof Oe=="function"?ae.callback=Oe:ae===m(U)&&S(U),de(De)}else S(U);ae=m(U)}if(ae!==null)var Pe=!0;else{var Ke=m(H);Ke!==null&&nt(ce,Ke.startTime-De),Pe=!1}return Pe}finally{ae=null,ee=L,x=!1}}var ve=!1,be=null,xe=-1,Ct=5,Ut=-1;function Dt(){return!(c.unstable_now()-Utre||125te?(re.sortIndex=L,p(H,re),m(U)===null&&re===m(H)&&(_?(me(xe),xe=-1):_=!0,nt(ce,L-te))):(re.sortIndex=Oe,p(U,re),z||x||(z=!0,bt(ze))),re},c.unstable_shouldYield=Dt,c.unstable_wrapCallback=function(re){var De=ee;return function(){var L=ee;ee=De;try{return re.apply(this,arguments)}finally{ee=L}}}}(Iy)),Iy}var Yy={},Jw;function lT(){return Jw||(Jw=1,function(c){var p={};/** + */var Kw;function lT(){return Kw||(Kw=1,function(c){function p(re,De){var L=re.length;re.push(De);e:for(;0>>1,Oe=re[te];if(0>>1;tex(wt,L))ptx(St,wt)?(re[te]=St,re[pt]=L,te=pt):(re[te]=wt,re[Ke]=L,te=Ke);else if(ptx(St,L))re[te]=St,re[pt]=L,te=pt;else break e}}return De}function x(re,De){var L=re.sortIndex-De.sortIndex;return L!==0?L:re.id-De.id}if(typeof performance=="object"&&typeof performance.now=="function"){var N=performance;c.unstable_now=function(){return N.now()}}else{var k=Date,g=k.now();c.unstable_now=function(){return k.now()-g}}var U=[],H=[],B=1,ae=null,ee=3,T=!1,z=!1,_=!1,Z=typeof setTimeout=="function"?setTimeout:null,me=typeof clearTimeout=="function"?clearTimeout:null,he=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function de(re){for(var De=m(H);De!==null;){if(De.callback===null)S(H);else if(De.startTime<=re)S(H),De.sortIndex=De.expirationTime,p(U,De);else break;De=m(H)}}function ce(re){if(_=!1,de(re),!z)if(m(U)!==null)z=!0,bt(ze);else{var De=m(H);De!==null&&nt(ce,De.startTime-re)}}function ze(re,De){z=!1,_&&(_=!1,me(xe),xe=-1),T=!0;var L=ee;try{for(de(De),ae=m(U);ae!==null&&(!(ae.expirationTime>De)||re&&!Dt());){var te=ae.callback;if(typeof te=="function"){ae.callback=null,ee=ae.priorityLevel;var Oe=te(ae.expirationTime<=De);De=c.unstable_now(),typeof Oe=="function"?ae.callback=Oe:ae===m(U)&&S(U),de(De)}else S(U);ae=m(U)}if(ae!==null)var Pe=!0;else{var Ke=m(H);Ke!==null&&nt(ce,Ke.startTime-De),Pe=!1}return Pe}finally{ae=null,ee=L,T=!1}}var ve=!1,be=null,xe=-1,Et=5,Ut=-1;function Dt(){return!(c.unstable_now()-Utre||125te?(re.sortIndex=L,p(H,re),m(U)===null&&re===m(H)&&(_?(me(xe),xe=-1):_=!0,nt(ce,L-te))):(re.sortIndex=Oe,p(U,re),z||T||(z=!0,bt(ze))),re},c.unstable_shouldYield=Dt,c.unstable_wrapCallback=function(re){var De=ee;return function(){var L=ee;ee=De;try{return re.apply(this,arguments)}finally{ee=L}}}}(Iy)),Iy}var Yy={},Jw;function oT(){return Jw||(Jw=1,function(c){var p={};/** * @license React * scheduler.development.js * @@ -74,7 +76,7 @@ Check the top-level render call using <`+Ee+">.")}return ue}}function El(O,ue){{ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */p.NODE_ENV!=="production"&&function(){typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart=="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error);var m=!1,S=!1,T=5;function N(Ce,We){var ht=Ce.length;Ce.push(We),U(Ce,We,ht)}function k(Ce){return Ce.length===0?null:Ce[0]}function g(Ce){if(Ce.length===0)return null;var We=Ce[0],ht=Ce.pop();return ht!==We&&(Ce[0]=ht,H(Ce,ht,0)),We}function U(Ce,We,ht){for(var It=ht;It>0;){var Ft=It-1>>>1,Ln=Ce[Ft];if(B(Ln,We)>0)Ce[Ft]=We,Ce[It]=Ln,It=Ft;else return}}function H(Ce,We,ht){for(var It=ht,Ft=Ce.length,Ln=Ft>>>1;Itht&&(!Ce||Sr()));){var It=Ze.callback;if(typeof It=="function"){Ze.callback=null,at=Ze.priorityLevel;var Ft=Ze.expirationTime<=ht,Ln=It(Ft);ht=c.unstable_now(),typeof Ln=="function"?Ze.callback=Ln:Ze===k(Dt)&&g(Dt),Oe(ht)}else g(Dt);Ze=k(Dt)}if(Ze!==null)return!0;var yn=k(ot);return yn!==null&&mn(Pe,yn.startTime-ht),!1}function pt(Ce,We){switch(Ce){case ae:case ee:case x:case z:case _:break;default:Ce=x}var ht=at;at=Ce;try{return We()}finally{at=ht}}function St(Ce){var We;switch(at){case ae:case ee:case x:We=x;break;default:We=at;break}var ht=at;at=We;try{return Ce()}finally{at=ht}}function vt(Ce){var We=at;return function(){var ht=at;at=We;try{return Ce.apply(this,arguments)}finally{at=ht}}}function dt(Ce,We,ht){var It=c.unstable_now(),Ft;if(typeof ht=="object"&&ht!==null){var Ln=ht.delay;typeof Ln=="number"&&Ln>0?Ft=It+Ln:Ft=It}else Ft=It;var yn;switch(Ce){case ae:yn=ve;break;case ee:yn=be;break;case _:yn=Ut;break;case z:yn=Ct;break;case x:default:yn=xe;break}var Cr=Ft+yn,Gt={id:Nt++,callback:We,priorityLevel:Ce,startTime:Ft,expirationTime:Cr,sortIndex:-1};return Ft>It?(Gt.sortIndex=Ft,N(ot,Gt),k(Dt)===null&&Gt===k(ot)&&(re?mi():re=!0,mn(Pe,Ft-It))):(Gt.sortIndex=Cr,N(Dt,Gt),!nt&&!bt&&(nt=!0,Or(Ke))),Gt}function en(){}function Bt(){!nt&&!bt&&(nt=!0,Or(Ke))}function Yn(){return k(Dt)}function wr(Ce){Ce.callback=null}function xn(){return at}var ar=!1,Wn=null,Hn=-1,$n=T,Dr=-1;function Sr(){var Ce=c.unstable_now()-Dr;return!(Ce<$n)}function Qn(){}function di(Ce){if(Ce<0||Ce>125){console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported");return}Ce>0?$n=Math.floor(1e3/Ce):$n=T}var pi=function(){if(Wn!==null){var Ce=c.unstable_now();Dr=Ce;var We=!0,ht=!0;try{ht=Wn(We,Ce)}finally{ht?br():(ar=!1,Wn=null)}}else ar=!1},br;if(typeof te=="function")br=function(){te(pi)};else if(typeof MessageChannel<"u"){var vi=new MessageChannel,lr=vi.port2;vi.port1.onmessage=pi,br=function(){lr.postMessage(null)}}else br=function(){De(pi,0)};function Or(Ce){Wn=Ce,ar||(ar=!0,br())}function mn(Ce,We){Hn=De(function(){Ce(c.unstable_now())},We)}function mi(){L(Hn),Hn=-1}var hi=Qn,yi=null;c.unstable_IdlePriority=_,c.unstable_ImmediatePriority=ae,c.unstable_LowPriority=z,c.unstable_NormalPriority=x,c.unstable_Profiling=yi,c.unstable_UserBlockingPriority=ee,c.unstable_cancelCallback=wr,c.unstable_continueExecution=Bt,c.unstable_forceFrameRate=di,c.unstable_getCurrentPriorityLevel=xn,c.unstable_getFirstCallbackNode=Yn,c.unstable_next=St,c.unstable_pauseExecution=en,c.unstable_requestPaint=hi,c.unstable_runWithPriority=pt,c.unstable_scheduleCallback=dt,c.unstable_shouldYield=Sr,c.unstable_wrapCallback=vt,typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop=="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error)}()}(Yy)),Yy}var eS;function tS(){if(eS)return Av.exports;eS=1;var c={};return c.NODE_ENV==="production"?Av.exports=aT():Av.exports=lT(),Av.exports}/** + */p.NODE_ENV!=="production"&&function(){typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart=="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error);var m=!1,S=!1,x=5;function N(Ee,We){var ht=Ee.length;Ee.push(We),U(Ee,We,ht)}function k(Ee){return Ee.length===0?null:Ee[0]}function g(Ee){if(Ee.length===0)return null;var We=Ee[0],ht=Ee.pop();return ht!==We&&(Ee[0]=ht,H(Ee,ht,0)),We}function U(Ee,We,ht){for(var It=ht;It>0;){var Ft=It-1>>>1,Ln=Ee[Ft];if(B(Ln,We)>0)Ee[Ft]=We,Ee[It]=Ln,It=Ft;else return}}function H(Ee,We,ht){for(var It=ht,Ft=Ee.length,Ln=Ft>>>1;Itht&&(!Ee||Sr()));){var It=Ze.callback;if(typeof It=="function"){Ze.callback=null,at=Ze.priorityLevel;var Ft=Ze.expirationTime<=ht,Ln=It(Ft);ht=c.unstable_now(),typeof Ln=="function"?Ze.callback=Ln:Ze===k(Dt)&&g(Dt),Oe(ht)}else g(Dt);Ze=k(Dt)}if(Ze!==null)return!0;var yn=k(ot);return yn!==null&&mn(Pe,yn.startTime-ht),!1}function pt(Ee,We){switch(Ee){case ae:case ee:case T:case z:case _:break;default:Ee=T}var ht=at;at=Ee;try{return We()}finally{at=ht}}function St(Ee){var We;switch(at){case ae:case ee:case T:We=T;break;default:We=at;break}var ht=at;at=We;try{return Ee()}finally{at=ht}}function vt(Ee){var We=at;return function(){var ht=at;at=We;try{return Ee.apply(this,arguments)}finally{at=ht}}}function dt(Ee,We,ht){var It=c.unstable_now(),Ft;if(typeof ht=="object"&&ht!==null){var Ln=ht.delay;typeof Ln=="number"&&Ln>0?Ft=It+Ln:Ft=It}else Ft=It;var yn;switch(Ee){case ae:yn=ve;break;case ee:yn=be;break;case _:yn=Ut;break;case z:yn=Et;break;case T:default:yn=xe;break}var Er=Ft+yn,Gt={id:Nt++,callback:We,priorityLevel:Ee,startTime:Ft,expirationTime:Er,sortIndex:-1};return Ft>It?(Gt.sortIndex=Ft,N(ot,Gt),k(Dt)===null&&Gt===k(ot)&&(re?mi():re=!0,mn(Pe,Ft-It))):(Gt.sortIndex=Er,N(Dt,Gt),!nt&&!bt&&(nt=!0,Or(Ke))),Gt}function en(){}function Bt(){!nt&&!bt&&(nt=!0,Or(Ke))}function Yn(){return k(Dt)}function wr(Ee){Ee.callback=null}function xn(){return at}var ar=!1,Wn=null,Hn=-1,$n=x,Dr=-1;function Sr(){var Ee=c.unstable_now()-Dr;return!(Ee<$n)}function Qn(){}function di(Ee){if(Ee<0||Ee>125){console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported");return}Ee>0?$n=Math.floor(1e3/Ee):$n=x}var pi=function(){if(Wn!==null){var Ee=c.unstable_now();Dr=Ee;var We=!0,ht=!0;try{ht=Wn(We,Ee)}finally{ht?br():(ar=!1,Wn=null)}}else ar=!1},br;if(typeof te=="function")br=function(){te(pi)};else if(typeof MessageChannel<"u"){var vi=new MessageChannel,lr=vi.port2;vi.port1.onmessage=pi,br=function(){lr.postMessage(null)}}else br=function(){De(pi,0)};function Or(Ee){Wn=Ee,ar||(ar=!0,br())}function mn(Ee,We){Hn=De(function(){Ee(c.unstable_now())},We)}function mi(){L(Hn),Hn=-1}var hi=Qn,yi=null;c.unstable_IdlePriority=_,c.unstable_ImmediatePriority=ae,c.unstable_LowPriority=z,c.unstable_NormalPriority=T,c.unstable_Profiling=yi,c.unstable_UserBlockingPriority=ee,c.unstable_cancelCallback=wr,c.unstable_continueExecution=Bt,c.unstable_forceFrameRate=di,c.unstable_getCurrentPriorityLevel=xn,c.unstable_getFirstCallbackNode=Yn,c.unstable_next=St,c.unstable_pauseExecution=en,c.unstable_requestPaint=hi,c.unstable_runWithPriority=pt,c.unstable_scheduleCallback=dt,c.unstable_shouldYield=Sr,c.unstable_wrapCallback=vt,typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop=="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error)}()}(Yy)),Yy}var eS;function tS(){if(eS)return Av.exports;eS=1;var c={};return c.NODE_ENV==="production"?Av.exports=lT():Av.exports=oT(),Av.exports}/** * @license React * react-dom.production.min.js * @@ -82,14 +84,14 @@ Check the top-level render call using <`+Ee+">.")}return ue}}function El(O,ue){{ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var nS;function oT(){if(nS)return Li;nS=1;var c=I,p=tS();function m(n){for(var r="https://reactjs.org/docs/error-decoder.html?invariant="+n,l=1;l"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),U=Object.prototype.hasOwnProperty,H=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,B={},ae={};function ee(n){return U.call(ae,n)?!0:U.call(B,n)?!1:H.test(n)?ae[n]=!0:(B[n]=!0,!1)}function x(n,r,l,u){if(l!==null&&l.type===0)return!1;switch(typeof r){case"function":case"symbol":return!0;case"boolean":return u?!1:l!==null?!l.acceptsBooleans:(n=n.toLowerCase().slice(0,5),n!=="data-"&&n!=="aria-");default:return!1}}function z(n,r,l,u){if(r===null||typeof r>"u"||x(n,r,l,u))return!0;if(u)return!1;if(l!==null)switch(l.type){case 3:return!r;case 4:return r===!1;case 5:return isNaN(r);case 6:return isNaN(r)||1>r}return!1}function _(n,r,l,u,f,v,b){this.acceptsBooleans=r===2||r===3||r===4,this.attributeName=u,this.attributeNamespace=f,this.mustUseProperty=l,this.propertyName=n,this.type=r,this.sanitizeURL=v,this.removeEmptyString=b}var Z={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(n){Z[n]=new _(n,0,!1,n,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(n){var r=n[0];Z[r]=new _(r,1,!1,n[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(n){Z[n]=new _(n,2,!1,n.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(n){Z[n]=new _(n,2,!1,n,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(n){Z[n]=new _(n,3,!1,n.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(n){Z[n]=new _(n,3,!0,n,null,!1,!1)}),["capture","download"].forEach(function(n){Z[n]=new _(n,4,!1,n,null,!1,!1)}),["cols","rows","size","span"].forEach(function(n){Z[n]=new _(n,6,!1,n,null,!1,!1)}),["rowSpan","start"].forEach(function(n){Z[n]=new _(n,5,!1,n.toLowerCase(),null,!1,!1)});var me=/[\-:]([a-z])/g;function he(n){return n[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(n){var r=n.replace(me,he);Z[r]=new _(r,1,!1,n,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(n){var r=n.replace(me,he);Z[r]=new _(r,1,!1,n,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(n){var r=n.replace(me,he);Z[r]=new _(r,1,!1,n,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(n){Z[n]=new _(n,1,!1,n.toLowerCase(),null,!1,!1)}),Z.xlinkHref=new _("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(n){Z[n]=new _(n,1,!1,n.toLowerCase(),null,!0,!0)});function de(n,r,l,u){var f=Z.hasOwnProperty(r)?Z[r]:null;(f!==null?f.type!==0:u||!(2"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),U=Object.prototype.hasOwnProperty,H=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,B={},ae={};function ee(n){return U.call(ae,n)?!0:U.call(B,n)?!1:H.test(n)?ae[n]=!0:(B[n]=!0,!1)}function T(n,r,l,u){if(l!==null&&l.type===0)return!1;switch(typeof r){case"function":case"symbol":return!0;case"boolean":return u?!1:l!==null?!l.acceptsBooleans:(n=n.toLowerCase().slice(0,5),n!=="data-"&&n!=="aria-");default:return!1}}function z(n,r,l,u){if(r===null||typeof r>"u"||T(n,r,l,u))return!0;if(u)return!1;if(l!==null)switch(l.type){case 3:return!r;case 4:return r===!1;case 5:return isNaN(r);case 6:return isNaN(r)||1>r}return!1}function _(n,r,l,u,f,v,b){this.acceptsBooleans=r===2||r===3||r===4,this.attributeName=u,this.attributeNamespace=f,this.mustUseProperty=l,this.propertyName=n,this.type=r,this.sanitizeURL=v,this.removeEmptyString=b}var Z={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(n){Z[n]=new _(n,0,!1,n,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(n){var r=n[0];Z[r]=new _(r,1,!1,n[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(n){Z[n]=new _(n,2,!1,n.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(n){Z[n]=new _(n,2,!1,n,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(n){Z[n]=new _(n,3,!1,n.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(n){Z[n]=new _(n,3,!0,n,null,!1,!1)}),["capture","download"].forEach(function(n){Z[n]=new _(n,4,!1,n,null,!1,!1)}),["cols","rows","size","span"].forEach(function(n){Z[n]=new _(n,6,!1,n,null,!1,!1)}),["rowSpan","start"].forEach(function(n){Z[n]=new _(n,5,!1,n.toLowerCase(),null,!1,!1)});var me=/[\-:]([a-z])/g;function he(n){return n[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(n){var r=n.replace(me,he);Z[r]=new _(r,1,!1,n,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(n){var r=n.replace(me,he);Z[r]=new _(r,1,!1,n,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(n){var r=n.replace(me,he);Z[r]=new _(r,1,!1,n,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(n){Z[n]=new _(n,1,!1,n.toLowerCase(),null,!1,!1)}),Z.xlinkHref=new _("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(n){Z[n]=new _(n,1,!1,n.toLowerCase(),null,!0,!0)});function de(n,r,l,u){var f=Z.hasOwnProperty(r)?Z[r]:null;(f!==null?f.type!==0:u||!(2R||f[b]!==v[R]){var M=` -`+f[b].replace(" at new "," at ");return n.displayName&&M.includes("")&&(M=M.replace("",n.displayName)),M}while(1<=b&&0<=R);break}}}finally{Pe=!1,Error.prepareStackTrace=l}return(n=n?n.displayName||n.name:"")?Oe(n):""}function wt(n){switch(n.tag){case 5:return Oe(n.type);case 16:return Oe("Lazy");case 13:return Oe("Suspense");case 19:return Oe("SuspenseList");case 0:case 2:case 15:return n=Ke(n.type,!1),n;case 11:return n=Ke(n.type.render,!1),n;case 1:return n=Ke(n.type,!0),n;default:return""}}function pt(n){if(n==null)return null;if(typeof n=="function")return n.displayName||n.name||null;if(typeof n=="string")return n;switch(n){case be:return"Fragment";case ve:return"Portal";case Ct:return"Profiler";case xe:return"StrictMode";case Nt:return"Suspense";case Ze:return"SuspenseList"}if(typeof n=="object")switch(n.$$typeof){case Dt:return(n.displayName||"Context")+".Consumer";case Ut:return(n._context.displayName||"Context")+".Provider";case ot:var r=n.render;return n=n.displayName,n||(n=r.displayName||r.name||"",n=n!==""?"ForwardRef("+n+")":"ForwardRef"),n;case at:return r=n.displayName||null,r!==null?r:pt(n.type)||"Memo";case bt:r=n._payload,n=n._init;try{return pt(n(r))}catch{}}return null}function St(n){var r=n.type;switch(n.tag){case 24:return"Cache";case 9:return(r.displayName||"Context")+".Consumer";case 10:return(r._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return n=r.render,n=n.displayName||n.name||"",r.displayName||(n!==""?"ForwardRef("+n+")":"ForwardRef");case 7:return"Fragment";case 5:return r;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return pt(r);case 8:return r===xe?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof r=="function")return r.displayName||r.name||null;if(typeof r=="string")return r}return null}function vt(n){switch(typeof n){case"boolean":case"number":case"string":case"undefined":return n;case"object":return n;default:return""}}function dt(n){var r=n.type;return(n=n.nodeName)&&n.toLowerCase()==="input"&&(r==="checkbox"||r==="radio")}function en(n){var r=dt(n)?"checked":"value",l=Object.getOwnPropertyDescriptor(n.constructor.prototype,r),u=""+n[r];if(!n.hasOwnProperty(r)&&typeof l<"u"&&typeof l.get=="function"&&typeof l.set=="function"){var f=l.get,v=l.set;return Object.defineProperty(n,r,{configurable:!0,get:function(){return f.call(this)},set:function(b){u=""+b,v.call(this,b)}}),Object.defineProperty(n,r,{enumerable:l.enumerable}),{getValue:function(){return u},setValue:function(b){u=""+b},stopTracking:function(){n._valueTracker=null,delete n[r]}}}}function Bt(n){n._valueTracker||(n._valueTracker=en(n))}function Yn(n){if(!n)return!1;var r=n._valueTracker;if(!r)return!0;var l=r.getValue(),u="";return n&&(u=dt(n)?n.checked?"true":"false":n.value),n=u,n!==l?(r.setValue(n),!0):!1}function wr(n){if(n=n||(typeof document<"u"?document:void 0),typeof n>"u")return null;try{return n.activeElement||n.body}catch{return n.body}}function xn(n,r){var l=r.checked;return L({},r,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:l??n._wrapperState.initialChecked})}function ar(n,r){var l=r.defaultValue==null?"":r.defaultValue,u=r.checked!=null?r.checked:r.defaultChecked;l=vt(r.value!=null?r.value:l),n._wrapperState={initialChecked:u,initialValue:l,controlled:r.type==="checkbox"||r.type==="radio"?r.checked!=null:r.value!=null}}function Wn(n,r){r=r.checked,r!=null&&de(n,"checked",r,!1)}function Hn(n,r){Wn(n,r);var l=vt(r.value),u=r.type;if(l!=null)u==="number"?(l===0&&n.value===""||n.value!=l)&&(n.value=""+l):n.value!==""+l&&(n.value=""+l);else if(u==="submit"||u==="reset"){n.removeAttribute("value");return}r.hasOwnProperty("value")?Dr(n,r.type,l):r.hasOwnProperty("defaultValue")&&Dr(n,r.type,vt(r.defaultValue)),r.checked==null&&r.defaultChecked!=null&&(n.defaultChecked=!!r.defaultChecked)}function $n(n,r,l){if(r.hasOwnProperty("value")||r.hasOwnProperty("defaultValue")){var u=r.type;if(!(u!=="submit"&&u!=="reset"||r.value!==void 0&&r.value!==null))return;r=""+n._wrapperState.initialValue,l||r===n.value||(n.value=r),n.defaultValue=r}l=n.name,l!==""&&(n.name=""),n.defaultChecked=!!n._wrapperState.initialChecked,l!==""&&(n.name=l)}function Dr(n,r,l){(r!=="number"||wr(n.ownerDocument)!==n)&&(l==null?n.defaultValue=""+n._wrapperState.initialValue:n.defaultValue!==""+l&&(n.defaultValue=""+l))}var Sr=Array.isArray;function Qn(n,r,l,u){if(n=n.options,r){r={};for(var f=0;f"+r.valueOf().toString()+"",r=mn.firstChild;n.firstChild;)n.removeChild(n.firstChild);for(;r.firstChild;)n.appendChild(r.firstChild)}});function hi(n,r){if(r){var l=n.firstChild;if(l&&l===n.lastChild&&l.nodeType===3){l.nodeValue=r;return}}n.textContent=r}var yi={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Ce=["Webkit","ms","Moz","O"];Object.keys(yi).forEach(function(n){Ce.forEach(function(r){r=r+n.charAt(0).toUpperCase()+n.substring(1),yi[r]=yi[n]})});function We(n,r,l){return r==null||typeof r=="boolean"||r===""?"":l||typeof r!="number"||r===0||yi.hasOwnProperty(n)&&yi[n]?(""+r).trim():r+"px"}function ht(n,r){n=n.style;for(var l in r)if(r.hasOwnProperty(l)){var u=l.indexOf("--")===0,f=We(l,r[l],u);l==="float"&&(l="cssFloat"),u?n.setProperty(l,f):n[l]=f}}var It=L({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Ft(n,r){if(r){if(It[n]&&(r.children!=null||r.dangerouslySetInnerHTML!=null))throw Error(m(137,n));if(r.dangerouslySetInnerHTML!=null){if(r.children!=null)throw Error(m(60));if(typeof r.dangerouslySetInnerHTML!="object"||!("__html"in r.dangerouslySetInnerHTML))throw Error(m(61))}if(r.style!=null&&typeof r.style!="object")throw Error(m(62))}}function Ln(n,r){if(n.indexOf("-")===-1)return typeof r.is=="string";switch(n){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var yn=null;function Cr(n){return n=n.target||n.srcElement||window,n.correspondingUseElement&&(n=n.correspondingUseElement),n.nodeType===3?n.parentNode:n}var Gt=null,Jn=null,Yt=null;function rn(n){if(n=Ta(n)){if(typeof Gt!="function")throw Error(m(280));var r=n.stateNode;r&&(r=Xc(r),Gt(n.stateNode,n.type,r))}}function ra(n){Jn?Yt?Yt.push(n):Yt=[n]:Jn=n}function Ni(){if(Jn){var n=Jn,r=Yt;if(Yt=Jn=null,rn(n),r)for(n=0;n>>=0,n===0?32:31-(gs(n)/ws|0)|0}var po=64,au=4194304;function Wa(n){switch(n&-n){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return n&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return n&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return n}}function ba(n,r){var l=n.pendingLanes;if(l===0)return 0;var u=0,f=n.suspendedLanes,v=n.pingedLanes,b=l&268435455;if(b!==0){var R=b&~f;R!==0?u=Wa(R):(v&=b,v!==0&&(u=Wa(v)))}else b=l&~f,b!==0?u=Wa(b):v!==0&&(u=Wa(v));if(u===0)return 0;if(r!==0&&r!==u&&!(r&f)&&(f=u&-u,v=r&-r,f>=v||f===16&&(v&4194240)!==0))return r;if(u&4&&(u|=l&16),r=n.entangledLanes,r!==0)for(n=n.entanglements,r&=u;0l;l++)r.push(n);return r}function Rl(n,r,l){n.pendingLanes|=r,r!==536870912&&(n.suspendedLanes=0,n.pingedLanes=0),n=n.eventTimes,r=31-Lr(r),n[r]=l}function bs(n,r){var l=n.pendingLanes&~r;n.pendingLanes=r,n.suspendedLanes=0,n.pingedLanes=0,n.expiredLanes&=r,n.mutableReadLanes&=r,n.entangledLanes&=r,r=n.entanglements;var u=n.eventTimes;for(n=n.expirationTimes;0=wi),rm=" ",vu=!1;function im(n,r){switch(n){case"keyup":return Ka.indexOf(r.keyCode)!==-1;case"keydown":return r.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function am(n){return n=n.detail,typeof n=="object"&&"data"in n?n.data:null}var Ll=!1;function Rg(n,r){switch(n){case"compositionend":return am(r);case"keypress":return r.which!==32?null:(vu=!0,rm);case"textInput":return n=r.data,n===rm&&vu?null:n;default:return null}}function kg(n,r){if(Ll)return n==="compositionend"||!Tn&&im(n,r)?(n=D(),w=du=ua=null,Ll=!1,n):null;switch(n){case"paste":return null;case"keypress":if(!(r.ctrlKey||r.altKey||r.metaKey)||r.ctrlKey&&r.altKey){if(r.char&&1=r)return{node:l,offset:r-n};n=u}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=dm(l)}}function vm(n,r){return n&&r?n===r?!0:n&&n.nodeType===3?!1:r&&r.nodeType===3?vm(n,r.parentNode):"contains"in n?n.contains(r):n.compareDocumentPosition?!!(n.compareDocumentPosition(r)&16):!1:!1}function mm(){for(var n=window,r=wr();r instanceof n.HTMLIFrameElement;){try{var l=typeof r.contentWindow.location.href=="string"}catch{l=!1}if(l)n=r.contentWindow;else break;r=wr(n.document)}return r}function zs(n){var r=n&&n.nodeName&&n.nodeName.toLowerCase();return r&&(r==="input"&&(n.type==="text"||n.type==="search"||n.type==="tel"||n.type==="url"||n.type==="password")||r==="textarea"||n.contentEditable==="true")}function wo(n){var r=mm(),l=n.focusedElem,u=n.selectionRange;if(r!==l&&l&&l.ownerDocument&&vm(l.ownerDocument.documentElement,l)){if(u!==null&&zs(l)){if(r=u.start,n=u.end,n===void 0&&(n=r),"selectionStart"in l)l.selectionStart=r,l.selectionEnd=Math.min(n,l.value.length);else if(n=(r=l.ownerDocument||document)&&r.defaultView||window,n.getSelection){n=n.getSelection();var f=l.textContent.length,v=Math.min(u.start,f);u=u.end===void 0?v:Math.min(u.end,f),!n.extend&&v>u&&(f=u,u=v,v=f),f=pm(l,v);var b=pm(l,u);f&&b&&(n.rangeCount!==1||n.anchorNode!==f.node||n.anchorOffset!==f.offset||n.focusNode!==b.node||n.focusOffset!==b.offset)&&(r=r.createRange(),r.setStart(f.node,f.offset),n.removeAllRanges(),v>u?(n.addRange(r),n.extend(b.node,b.offset)):(r.setEnd(b.node,b.offset),n.addRange(r)))}}for(r=[],n=l;n=n.parentNode;)n.nodeType===1&&r.push({element:n,left:n.scrollLeft,top:n.scrollTop});for(typeof l.focus=="function"&&l.focus(),l=0;l=document.documentMode,So=null,Ml=null,As=null,Qd=!1;function hm(n,r,l){var u=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;Qd||So==null||So!==wr(u)||(u=So,"selectionStart"in u&&zs(u)?u={start:u.selectionStart,end:u.selectionEnd}:(u=(u.ownerDocument&&u.ownerDocument.defaultView||window).getSelection(),u={anchorNode:u.anchorNode,anchorOffset:u.anchorOffset,focusNode:u.focusNode,focusOffset:u.focusOffset}),As&&mu(As,u)||(As=u,u=Gc(Ml,"onSelect"),0fa||(n.current=Ge[fa],Ge[fa]=null,fa--)}function Ht(n,r){fa++,Ge[fa]=n.current,n.current=r}var Hi={},er=nn(Hi),ut=nn(!1),Mr=Hi;function bi(n,r){var l=n.type.contextTypes;if(!l)return Hi;var u=n.stateNode;if(u&&u.__reactInternalMemoizedUnmaskedChildContext===r)return u.__reactInternalMemoizedMaskedChildContext;var f={},v;for(v in l)f[v]=r[v];return u&&(n=n.stateNode,n.__reactInternalMemoizedUnmaskedChildContext=r,n.__reactInternalMemoizedMaskedChildContext=f),f}function Vn(n){return n=n.childContextTypes,n!=null}function Zr(){Ve(ut),Ve(er)}function Ra(n,r,l){if(er.current!==Hi)throw Error(m(168));Ht(er,r),Ht(ut,l)}function zl(n,r,l){var u=n.stateNode;if(r=r.childContextTypes,typeof u.getChildContext!="function")return l;u=u.getChildContext();for(var f in u)if(!(f in r))throw Error(m(108,St(n)||"Unknown",f));return L({},l,u)}function Eo(n){return n=(n=n.stateNode)&&n.__reactInternalMemoizedMergedChildContext||Hi,Mr=er.current,Ht(er,n),Ht(ut,ut.current),!0}function km(n,r,l){var u=n.stateNode;if(!u)throw Error(m(169));l?(n=zl(n,r,Mr),u.__reactInternalMemoizedMergedChildContext=n,Ve(ut),Ve(er),Ht(er,n)):Ve(ut),Ht(ut,l)}var il=null,Al=!1,ur=!1;function Zc(n){il===null?il=[n]:il.push(n)}function _m(n){Al=!0,Zc(n)}function ka(){if(!ur&&il!==null){ur=!0;var n=0,r=kt;try{var l=il;for(kt=1;n>=b,f-=b,ei=1<<32-Lr(r)+f|l<tt?(Zn=je,je=null):Zn=je.sibling;var $t=le($,je,Y[tt],ge);if($t===null){je===null&&(je=Zn);break}n&&je&&$t.alternate===null&&r($,je),j=v($t,j,tt),et===null?Be=$t:et.sibling=$t,et=$t,je=Zn}if(tt===Y.length)return l($,je),dn&&To($,tt),Be;if(je===null){for(;tttt?(Zn=je,je=null):Zn=je.sibling;var cl=le($,je,$t.value,ge);if(cl===null){je===null&&(je=Zn);break}n&&je&&cl.alternate===null&&r($,je),j=v(cl,j,tt),et===null?Be=cl:et.sibling=cl,et=cl,je=Zn}if($t.done)return l($,je),dn&&To($,tt),Be;if(je===null){for(;!$t.done;tt++,$t=Y.next())$t=fe($,$t.value,ge),$t!==null&&(j=v($t,j,tt),et===null?Be=$t:et.sibling=$t,et=$t);return dn&&To($,tt),Be}for(je=u($,je);!$t.done;tt++,$t=Y.next())$t=ke(je,$,tt,$t.value,ge),$t!==null&&(n&&$t.alternate!==null&&je.delete($t.key===null?tt:$t.key),j=v($t,j,tt),et===null?Be=$t:et.sibling=$t,et=$t);return n&&je.forEach(function(n0){return r($,n0)}),dn&&To($,tt),Be}function jn($,j,Y,ge){if(typeof Y=="object"&&Y!==null&&Y.type===be&&Y.key===null&&(Y=Y.props.children),typeof Y=="object"&&Y!==null){switch(Y.$$typeof){case ze:e:{for(var Be=Y.key,et=j;et!==null;){if(et.key===Be){if(Be=Y.type,Be===be){if(et.tag===7){l($,et.sibling),j=f(et,Y.props.children),j.return=$,$=j;break e}}else if(et.elementType===Be||typeof Be=="object"&&Be!==null&&Be.$$typeof===bt&&Um(Be)===et.type){l($,et.sibling),j=f(et,Y.props),j.ref=Eu($,et,Y),j.return=$,$=j;break e}l($,et);break}else r($,et);et=et.sibling}Y.type===be?(j=Po(Y.props.children,$.mode,ge,Y.key),j.return=$,$=j):(ge=jf(Y.type,Y.key,Y.props,null,$.mode,ge),ge.ref=Eu($,j,Y),ge.return=$,$=ge)}return b($);case ve:e:{for(et=Y.key;j!==null;){if(j.key===et)if(j.tag===4&&j.stateNode.containerInfo===Y.containerInfo&&j.stateNode.implementation===Y.implementation){l($,j.sibling),j=f(j,Y.children||[]),j.return=$,$=j;break e}else{l($,j);break}else r($,j);j=j.sibling}j=dc(Y,$.mode,ge),j.return=$,$=j}return b($);case bt:return et=Y._init,jn($,j,et(Y._payload),ge)}if(Sr(Y))return Ue($,j,Y,ge);if(De(Y))return $e($,j,Y,ge);Qs($,Y)}return typeof Y=="string"&&Y!==""||typeof Y=="number"?(Y=""+Y,j!==null&&j.tag===6?(l($,j.sibling),j=f(j,Y),j.return=$,$=j):(l($,j),j=Hf(Y,$.mode,ge),j.return=$,$=j),b($)):l($,j)}return jn}var xu=jm(!0),Fm=jm(!1),Gs={},Da=nn(Gs),Tu=nn(Gs),qs=nn(Gs);function Pl(n){if(n===Gs)throw Error(m(174));return n}function vp(n,r){switch(Ht(qs,r),Ht(Tu,n),Ht(Da,Gs),n=r.nodeType,n){case 9:case 11:r=(r=r.documentElement)?r.namespaceURI:Or(null,"");break;default:n=n===8?r.parentNode:r,r=n.namespaceURI||null,n=n.tagName,r=Or(r,n)}Ve(Da),Ht(Da,r)}function Ru(){Ve(Da),Ve(Tu),Ve(qs)}function af(n){Pl(qs.current);var r=Pl(Da.current),l=Or(r,n.type);r!==l&&(Ht(Tu,n),Ht(Da,l))}function qe(n){Tu.current===n&&(Ve(Da),Ve(Tu))}var Ie=nn(0);function jt(n){for(var r=n;r!==null;){if(r.tag===13){var l=r.memoizedState;if(l!==null&&(l=l.dehydrated,l===null||l.data==="$?"||l.data==="$!"))return r}else if(r.tag===19&&r.memoizedProps.revealOrder!==void 0){if(r.flags&128)return r}else if(r.child!==null){r.child.return=r,r=r.child;continue}if(r===n)break;for(;r.sibling===null;){if(r.return===null||r.return===n)return null;r=r.return}r.sibling.return=r.return,r=r.sibling}return null}var Rn=[];function Bi(){for(var n=0;nl?l:4,n(!0);var u=mp.transition;mp.transition={};try{n(!1),r()}finally{kt=l,mp.transition=u}}function Hm(){return ri().memoizedState}function sn(n,r,l){var u=sl(n);if(l={lane:u,action:l,hasEagerState:!1,eagerState:null,next:null},ec(n))Du(r,l);else if(l=Om(n,r,l,u),l!==null){var f=rr();bn(l,n,u,f),tc(l,r,u)}}function gf(n,r,l){var u=sl(n),f={lane:u,action:l,hasEagerState:!1,eagerState:null,next:null};if(ec(n))Du(r,f);else{var v=n.alternate;if(n.lanes===0&&(v===null||v.lanes===0)&&(v=r.lastRenderedReducer,v!==null))try{var b=r.lastRenderedState,R=v(b,l);if(f.hasEagerState=!0,f.eagerState=R,sa(R,b)){var M=r.interleaved;M===null?(f.next=f,xi(r)):(f.next=M.next,M.next=f),r.interleaved=f;return}}catch{}finally{}l=Om(n,r,f,u),l!==null&&(f=rr(),bn(l,n,u,f),tc(l,r,u))}}function ec(n){var r=n.alternate;return n===Sn||r!==null&&r===Sn}function Du(n,r){Je=zn=!0;var l=n.pending;l===null?r.next=r:(r.next=l.next,l.next=r),n.pending=r}function tc(n,r,l){if(l&4194240){var u=r.lanes;u&=n.pendingLanes,l|=u,r.lanes=l,Cs(n,l)}}var wf={readContext:we,useCallback:kn,useContext:kn,useEffect:kn,useImperativeHandle:kn,useInsertionEffect:kn,useLayoutEffect:kn,useMemo:kn,useReducer:kn,useRef:kn,useState:kn,useDebugValue:kn,useDeferredValue:kn,useTransition:kn,useMutableSource:kn,useSyncExternalStore:kn,useId:kn,unstable_isNewReconciler:!1},Fg={readContext:we,useCallback:function(n,r){return ni().memoizedState=[n,r===void 0?null:r],n},useContext:we,useEffect:Mo,useImperativeHandle:function(n,r,l){return l=l!=null?l.concat([n]):null,ku(4194308,4,yf.bind(null,r,n),l)},useLayoutEffect:function(n,r){return ku(4194308,4,n,r)},useInsertionEffect:function(n,r){return ku(4,2,n,r)},useMemo:function(n,r){var l=ni();return r=r===void 0?null:r,n=n(),l.memoizedState=[n,r],n},useReducer:function(n,r,l){var u=ni();return r=l!==void 0?l(r):r,u.memoizedState=u.baseState=r,n={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:n,lastRenderedState:r},u.queue=n,n=n.dispatch=sn.bind(null,Sn,n),[u.memoizedState,n]},useRef:function(n){var r=ni();return n={current:n},r.memoizedState=n},useState:pf,useDebugValue:No,useDeferredValue:function(n){return ni().memoizedState=n},useTransition:function(){var n=pf(!1),r=n[0];return n=Ti.bind(null,n[1]),ni().memoizedState=n,[r,n]},useMutableSource:function(){},useSyncExternalStore:function(n,r,l){var u=Sn,f=ni();if(dn){if(l===void 0)throw Error(m(407));l=l()}else{if(l=r(),Dn===null)throw Error(m(349));ko&30||uf(u,r,l)}f.memoizedState=l;var v={value:l,getSnapshot:r};return f.queue=v,Mo(cf.bind(null,u,v,n),[n]),u.flags|=2048,Oo(9,sf.bind(null,u,v,l,r),void 0,null),l},useId:function(){var n=ni(),r=Dn.identifierPrefix;if(dn){var l=sr,u=ei;l=(u&~(1<<32-Lr(u)-1)).toString(32)+l,r=":"+r+"R"+l,l=Oa++,0")&&(M=M.replace("",n.displayName)),M}while(1<=b&&0<=R);break}}}finally{Pe=!1,Error.prepareStackTrace=l}return(n=n?n.displayName||n.name:"")?Oe(n):""}function wt(n){switch(n.tag){case 5:return Oe(n.type);case 16:return Oe("Lazy");case 13:return Oe("Suspense");case 19:return Oe("SuspenseList");case 0:case 2:case 15:return n=Ke(n.type,!1),n;case 11:return n=Ke(n.type.render,!1),n;case 1:return n=Ke(n.type,!0),n;default:return""}}function pt(n){if(n==null)return null;if(typeof n=="function")return n.displayName||n.name||null;if(typeof n=="string")return n;switch(n){case be:return"Fragment";case ve:return"Portal";case Et:return"Profiler";case xe:return"StrictMode";case Nt:return"Suspense";case Ze:return"SuspenseList"}if(typeof n=="object")switch(n.$$typeof){case Dt:return(n.displayName||"Context")+".Consumer";case Ut:return(n._context.displayName||"Context")+".Provider";case ot:var r=n.render;return n=n.displayName,n||(n=r.displayName||r.name||"",n=n!==""?"ForwardRef("+n+")":"ForwardRef"),n;case at:return r=n.displayName||null,r!==null?r:pt(n.type)||"Memo";case bt:r=n._payload,n=n._init;try{return pt(n(r))}catch{}}return null}function St(n){var r=n.type;switch(n.tag){case 24:return"Cache";case 9:return(r.displayName||"Context")+".Consumer";case 10:return(r._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return n=r.render,n=n.displayName||n.name||"",r.displayName||(n!==""?"ForwardRef("+n+")":"ForwardRef");case 7:return"Fragment";case 5:return r;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return pt(r);case 8:return r===xe?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof r=="function")return r.displayName||r.name||null;if(typeof r=="string")return r}return null}function vt(n){switch(typeof n){case"boolean":case"number":case"string":case"undefined":return n;case"object":return n;default:return""}}function dt(n){var r=n.type;return(n=n.nodeName)&&n.toLowerCase()==="input"&&(r==="checkbox"||r==="radio")}function en(n){var r=dt(n)?"checked":"value",l=Object.getOwnPropertyDescriptor(n.constructor.prototype,r),u=""+n[r];if(!n.hasOwnProperty(r)&&typeof l<"u"&&typeof l.get=="function"&&typeof l.set=="function"){var f=l.get,v=l.set;return Object.defineProperty(n,r,{configurable:!0,get:function(){return f.call(this)},set:function(b){u=""+b,v.call(this,b)}}),Object.defineProperty(n,r,{enumerable:l.enumerable}),{getValue:function(){return u},setValue:function(b){u=""+b},stopTracking:function(){n._valueTracker=null,delete n[r]}}}}function Bt(n){n._valueTracker||(n._valueTracker=en(n))}function Yn(n){if(!n)return!1;var r=n._valueTracker;if(!r)return!0;var l=r.getValue(),u="";return n&&(u=dt(n)?n.checked?"true":"false":n.value),n=u,n!==l?(r.setValue(n),!0):!1}function wr(n){if(n=n||(typeof document<"u"?document:void 0),typeof n>"u")return null;try{return n.activeElement||n.body}catch{return n.body}}function xn(n,r){var l=r.checked;return L({},r,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:l??n._wrapperState.initialChecked})}function ar(n,r){var l=r.defaultValue==null?"":r.defaultValue,u=r.checked!=null?r.checked:r.defaultChecked;l=vt(r.value!=null?r.value:l),n._wrapperState={initialChecked:u,initialValue:l,controlled:r.type==="checkbox"||r.type==="radio"?r.checked!=null:r.value!=null}}function Wn(n,r){r=r.checked,r!=null&&de(n,"checked",r,!1)}function Hn(n,r){Wn(n,r);var l=vt(r.value),u=r.type;if(l!=null)u==="number"?(l===0&&n.value===""||n.value!=l)&&(n.value=""+l):n.value!==""+l&&(n.value=""+l);else if(u==="submit"||u==="reset"){n.removeAttribute("value");return}r.hasOwnProperty("value")?Dr(n,r.type,l):r.hasOwnProperty("defaultValue")&&Dr(n,r.type,vt(r.defaultValue)),r.checked==null&&r.defaultChecked!=null&&(n.defaultChecked=!!r.defaultChecked)}function $n(n,r,l){if(r.hasOwnProperty("value")||r.hasOwnProperty("defaultValue")){var u=r.type;if(!(u!=="submit"&&u!=="reset"||r.value!==void 0&&r.value!==null))return;r=""+n._wrapperState.initialValue,l||r===n.value||(n.value=r),n.defaultValue=r}l=n.name,l!==""&&(n.name=""),n.defaultChecked=!!n._wrapperState.initialChecked,l!==""&&(n.name=l)}function Dr(n,r,l){(r!=="number"||wr(n.ownerDocument)!==n)&&(l==null?n.defaultValue=""+n._wrapperState.initialValue:n.defaultValue!==""+l&&(n.defaultValue=""+l))}var Sr=Array.isArray;function Qn(n,r,l,u){if(n=n.options,r){r={};for(var f=0;f"+r.valueOf().toString()+"",r=mn.firstChild;n.firstChild;)n.removeChild(n.firstChild);for(;r.firstChild;)n.appendChild(r.firstChild)}});function hi(n,r){if(r){var l=n.firstChild;if(l&&l===n.lastChild&&l.nodeType===3){l.nodeValue=r;return}}n.textContent=r}var yi={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Ee=["Webkit","ms","Moz","O"];Object.keys(yi).forEach(function(n){Ee.forEach(function(r){r=r+n.charAt(0).toUpperCase()+n.substring(1),yi[r]=yi[n]})});function We(n,r,l){return r==null||typeof r=="boolean"||r===""?"":l||typeof r!="number"||r===0||yi.hasOwnProperty(n)&&yi[n]?(""+r).trim():r+"px"}function ht(n,r){n=n.style;for(var l in r)if(r.hasOwnProperty(l)){var u=l.indexOf("--")===0,f=We(l,r[l],u);l==="float"&&(l="cssFloat"),u?n.setProperty(l,f):n[l]=f}}var It=L({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Ft(n,r){if(r){if(It[n]&&(r.children!=null||r.dangerouslySetInnerHTML!=null))throw Error(m(137,n));if(r.dangerouslySetInnerHTML!=null){if(r.children!=null)throw Error(m(60));if(typeof r.dangerouslySetInnerHTML!="object"||!("__html"in r.dangerouslySetInnerHTML))throw Error(m(61))}if(r.style!=null&&typeof r.style!="object")throw Error(m(62))}}function Ln(n,r){if(n.indexOf("-")===-1)return typeof r.is=="string";switch(n){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var yn=null;function Er(n){return n=n.target||n.srcElement||window,n.correspondingUseElement&&(n=n.correspondingUseElement),n.nodeType===3?n.parentNode:n}var Gt=null,Jn=null,Yt=null;function rn(n){if(n=Ta(n)){if(typeof Gt!="function")throw Error(m(280));var r=n.stateNode;r&&(r=Zc(r),Gt(n.stateNode,n.type,r))}}function ra(n){Jn?Yt?Yt.push(n):Yt=[n]:Jn=n}function Ni(){if(Jn){var n=Jn,r=Yt;if(Yt=Jn=null,rn(n),r)for(n=0;n>>=0,n===0?32:31-(gs(n)/ws|0)|0}var po=64,au=4194304;function Wa(n){switch(n&-n){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return n&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return n&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return n}}function ba(n,r){var l=n.pendingLanes;if(l===0)return 0;var u=0,f=n.suspendedLanes,v=n.pingedLanes,b=l&268435455;if(b!==0){var R=b&~f;R!==0?u=Wa(R):(v&=b,v!==0&&(u=Wa(v)))}else b=l&~f,b!==0?u=Wa(b):v!==0&&(u=Wa(v));if(u===0)return 0;if(r!==0&&r!==u&&!(r&f)&&(f=u&-u,v=r&-r,f>=v||f===16&&(v&4194240)!==0))return r;if(u&4&&(u|=l&16),r=n.entangledLanes,r!==0)for(n=n.entanglements,r&=u;0l;l++)r.push(n);return r}function Rl(n,r,l){n.pendingLanes|=r,r!==536870912&&(n.suspendedLanes=0,n.pingedLanes=0),n=n.eventTimes,r=31-Lr(r),n[r]=l}function bs(n,r){var l=n.pendingLanes&~r;n.pendingLanes=r,n.suspendedLanes=0,n.pingedLanes=0,n.expiredLanes&=r,n.mutableReadLanes&=r,n.entangledLanes&=r,r=n.entanglements;var u=n.eventTimes;for(n=n.expirationTimes;0=wi),rm=" ",vu=!1;function im(n,r){switch(n){case"keyup":return Ka.indexOf(r.keyCode)!==-1;case"keydown":return r.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function am(n){return n=n.detail,typeof n=="object"&&"data"in n?n.data:null}var Ll=!1;function Rg(n,r){switch(n){case"compositionend":return am(r);case"keypress":return r.which!==32?null:(vu=!0,rm);case"textInput":return n=r.data,n===rm&&vu?null:n;default:return null}}function kg(n,r){if(Ll)return n==="compositionend"||!Tn&&im(n,r)?(n=D(),w=du=ua=null,Ll=!1,n):null;switch(n){case"paste":return null;case"keypress":if(!(r.ctrlKey||r.altKey||r.metaKey)||r.ctrlKey&&r.altKey){if(r.char&&1=r)return{node:l,offset:r-n};n=u}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=dm(l)}}function vm(n,r){return n&&r?n===r?!0:n&&n.nodeType===3?!1:r&&r.nodeType===3?vm(n,r.parentNode):"contains"in n?n.contains(r):n.compareDocumentPosition?!!(n.compareDocumentPosition(r)&16):!1:!1}function mm(){for(var n=window,r=wr();r instanceof n.HTMLIFrameElement;){try{var l=typeof r.contentWindow.location.href=="string"}catch{l=!1}if(l)n=r.contentWindow;else break;r=wr(n.document)}return r}function zs(n){var r=n&&n.nodeName&&n.nodeName.toLowerCase();return r&&(r==="input"&&(n.type==="text"||n.type==="search"||n.type==="tel"||n.type==="url"||n.type==="password")||r==="textarea"||n.contentEditable==="true")}function wo(n){var r=mm(),l=n.focusedElem,u=n.selectionRange;if(r!==l&&l&&l.ownerDocument&&vm(l.ownerDocument.documentElement,l)){if(u!==null&&zs(l)){if(r=u.start,n=u.end,n===void 0&&(n=r),"selectionStart"in l)l.selectionStart=r,l.selectionEnd=Math.min(n,l.value.length);else if(n=(r=l.ownerDocument||document)&&r.defaultView||window,n.getSelection){n=n.getSelection();var f=l.textContent.length,v=Math.min(u.start,f);u=u.end===void 0?v:Math.min(u.end,f),!n.extend&&v>u&&(f=u,u=v,v=f),f=pm(l,v);var b=pm(l,u);f&&b&&(n.rangeCount!==1||n.anchorNode!==f.node||n.anchorOffset!==f.offset||n.focusNode!==b.node||n.focusOffset!==b.offset)&&(r=r.createRange(),r.setStart(f.node,f.offset),n.removeAllRanges(),v>u?(n.addRange(r),n.extend(b.node,b.offset)):(r.setEnd(b.node,b.offset),n.addRange(r)))}}for(r=[],n=l;n=n.parentNode;)n.nodeType===1&&r.push({element:n,left:n.scrollLeft,top:n.scrollTop});for(typeof l.focus=="function"&&l.focus(),l=0;l=document.documentMode,So=null,Ml=null,As=null,Qd=!1;function hm(n,r,l){var u=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;Qd||So==null||So!==wr(u)||(u=So,"selectionStart"in u&&zs(u)?u={start:u.selectionStart,end:u.selectionEnd}:(u=(u.ownerDocument&&u.ownerDocument.defaultView||window).getSelection(),u={anchorNode:u.anchorNode,anchorOffset:u.anchorOffset,focusNode:u.focusNode,focusOffset:u.focusOffset}),As&&mu(As,u)||(As=u,u=qc(Ml,"onSelect"),0fa||(n.current=Ge[fa],Ge[fa]=null,fa--)}function Ht(n,r){fa++,Ge[fa]=n.current,n.current=r}var Hi={},er=nn(Hi),ut=nn(!1),Mr=Hi;function bi(n,r){var l=n.type.contextTypes;if(!l)return Hi;var u=n.stateNode;if(u&&u.__reactInternalMemoizedUnmaskedChildContext===r)return u.__reactInternalMemoizedMaskedChildContext;var f={},v;for(v in l)f[v]=r[v];return u&&(n=n.stateNode,n.__reactInternalMemoizedUnmaskedChildContext=r,n.__reactInternalMemoizedMaskedChildContext=f),f}function Vn(n){return n=n.childContextTypes,n!=null}function Zr(){Ve(ut),Ve(er)}function Ra(n,r,l){if(er.current!==Hi)throw Error(m(168));Ht(er,r),Ht(ut,l)}function zl(n,r,l){var u=n.stateNode;if(r=r.childContextTypes,typeof u.getChildContext!="function")return l;u=u.getChildContext();for(var f in u)if(!(f in r))throw Error(m(108,St(n)||"Unknown",f));return L({},l,u)}function Co(n){return n=(n=n.stateNode)&&n.__reactInternalMemoizedMergedChildContext||Hi,Mr=er.current,Ht(er,n),Ht(ut,ut.current),!0}function km(n,r,l){var u=n.stateNode;if(!u)throw Error(m(169));l?(n=zl(n,r,Mr),u.__reactInternalMemoizedMergedChildContext=n,Ve(ut),Ve(er),Ht(er,n)):Ve(ut),Ht(ut,l)}var il=null,Al=!1,ur=!1;function Kc(n){il===null?il=[n]:il.push(n)}function _m(n){Al=!0,Kc(n)}function ka(){if(!ur&&il!==null){ur=!0;var n=0,r=kt;try{var l=il;for(kt=1;n>=b,f-=b,ei=1<<32-Lr(r)+f|l<tt?(Zn=je,je=null):Zn=je.sibling;var $t=oe($,je,Y[tt],ge);if($t===null){je===null&&(je=Zn);break}n&&je&&$t.alternate===null&&r($,je),j=v($t,j,tt),et===null?Be=$t:et.sibling=$t,et=$t,je=Zn}if(tt===Y.length)return l($,je),dn&&To($,tt),Be;if(je===null){for(;tttt?(Zn=je,je=null):Zn=je.sibling;var cl=oe($,je,$t.value,ge);if(cl===null){je===null&&(je=Zn);break}n&&je&&cl.alternate===null&&r($,je),j=v(cl,j,tt),et===null?Be=cl:et.sibling=cl,et=cl,je=Zn}if($t.done)return l($,je),dn&&To($,tt),Be;if(je===null){for(;!$t.done;tt++,$t=Y.next())$t=fe($,$t.value,ge),$t!==null&&(j=v($t,j,tt),et===null?Be=$t:et.sibling=$t,et=$t);return dn&&To($,tt),Be}for(je=u($,je);!$t.done;tt++,$t=Y.next())$t=ke(je,$,tt,$t.value,ge),$t!==null&&(n&&$t.alternate!==null&&je.delete($t.key===null?tt:$t.key),j=v($t,j,tt),et===null?Be=$t:et.sibling=$t,et=$t);return n&&je.forEach(function(n0){return r($,n0)}),dn&&To($,tt),Be}function jn($,j,Y,ge){if(typeof Y=="object"&&Y!==null&&Y.type===be&&Y.key===null&&(Y=Y.props.children),typeof Y=="object"&&Y!==null){switch(Y.$$typeof){case ze:e:{for(var Be=Y.key,et=j;et!==null;){if(et.key===Be){if(Be=Y.type,Be===be){if(et.tag===7){l($,et.sibling),j=f(et,Y.props.children),j.return=$,$=j;break e}}else if(et.elementType===Be||typeof Be=="object"&&Be!==null&&Be.$$typeof===bt&&Um(Be)===et.type){l($,et.sibling),j=f(et,Y.props),j.ref=Cu($,et,Y),j.return=$,$=j;break e}l($,et);break}else r($,et);et=et.sibling}Y.type===be?(j=Po(Y.props.children,$.mode,ge,Y.key),j.return=$,$=j):(ge=Ff(Y.type,Y.key,Y.props,null,$.mode,ge),ge.ref=Cu($,j,Y),ge.return=$,$=ge)}return b($);case ve:e:{for(et=Y.key;j!==null;){if(j.key===et)if(j.tag===4&&j.stateNode.containerInfo===Y.containerInfo&&j.stateNode.implementation===Y.implementation){l($,j.sibling),j=f(j,Y.children||[]),j.return=$,$=j;break e}else{l($,j);break}else r($,j);j=j.sibling}j=dc(Y,$.mode,ge),j.return=$,$=j}return b($);case bt:return et=Y._init,jn($,j,et(Y._payload),ge)}if(Sr(Y))return Ue($,j,Y,ge);if(De(Y))return $e($,j,Y,ge);Qs($,Y)}return typeof Y=="string"&&Y!==""||typeof Y=="number"?(Y=""+Y,j!==null&&j.tag===6?(l($,j.sibling),j=f(j,Y),j.return=$,$=j):(l($,j),j=$f(Y,$.mode,ge),j.return=$,$=j),b($)):l($,j)}return jn}var xu=jm(!0),Fm=jm(!1),Gs={},Da=nn(Gs),Tu=nn(Gs),qs=nn(Gs);function Pl(n){if(n===Gs)throw Error(m(174));return n}function vp(n,r){switch(Ht(qs,r),Ht(Tu,n),Ht(Da,Gs),n=r.nodeType,n){case 9:case 11:r=(r=r.documentElement)?r.namespaceURI:Or(null,"");break;default:n=n===8?r.parentNode:r,r=n.namespaceURI||null,n=n.tagName,r=Or(r,n)}Ve(Da),Ht(Da,r)}function Ru(){Ve(Da),Ve(Tu),Ve(qs)}function lf(n){Pl(qs.current);var r=Pl(Da.current),l=Or(r,n.type);r!==l&&(Ht(Tu,n),Ht(Da,l))}function qe(n){Tu.current===n&&(Ve(Da),Ve(Tu))}var Ie=nn(0);function jt(n){for(var r=n;r!==null;){if(r.tag===13){var l=r.memoizedState;if(l!==null&&(l=l.dehydrated,l===null||l.data==="$?"||l.data==="$!"))return r}else if(r.tag===19&&r.memoizedProps.revealOrder!==void 0){if(r.flags&128)return r}else if(r.child!==null){r.child.return=r,r=r.child;continue}if(r===n)break;for(;r.sibling===null;){if(r.return===null||r.return===n)return null;r=r.return}r.sibling.return=r.return,r=r.sibling}return null}var Rn=[];function Bi(){for(var n=0;nl?l:4,n(!0);var u=mp.transition;mp.transition={};try{n(!1),r()}finally{kt=l,mp.transition=u}}function Hm(){return ri().memoizedState}function sn(n,r,l){var u=sl(n);if(l={lane:u,action:l,hasEagerState:!1,eagerState:null,next:null},ec(n))Du(r,l);else if(l=Om(n,r,l,u),l!==null){var f=rr();bn(l,n,u,f),tc(l,r,u)}}function wf(n,r,l){var u=sl(n),f={lane:u,action:l,hasEagerState:!1,eagerState:null,next:null};if(ec(n))Du(r,f);else{var v=n.alternate;if(n.lanes===0&&(v===null||v.lanes===0)&&(v=r.lastRenderedReducer,v!==null))try{var b=r.lastRenderedState,R=v(b,l);if(f.hasEagerState=!0,f.eagerState=R,sa(R,b)){var M=r.interleaved;M===null?(f.next=f,xi(r)):(f.next=M.next,M.next=f),r.interleaved=f;return}}catch{}finally{}l=Om(n,r,f,u),l!==null&&(f=rr(),bn(l,n,u,f),tc(l,r,u))}}function ec(n){var r=n.alternate;return n===Sn||r!==null&&r===Sn}function Du(n,r){Je=zn=!0;var l=n.pending;l===null?r.next=r:(r.next=l.next,l.next=r),n.pending=r}function tc(n,r,l){if(l&4194240){var u=r.lanes;u&=n.pendingLanes,l|=u,r.lanes=l,Es(n,l)}}var Sf={readContext:we,useCallback:kn,useContext:kn,useEffect:kn,useImperativeHandle:kn,useInsertionEffect:kn,useLayoutEffect:kn,useMemo:kn,useReducer:kn,useRef:kn,useState:kn,useDebugValue:kn,useDeferredValue:kn,useTransition:kn,useMutableSource:kn,useSyncExternalStore:kn,useId:kn,unstable_isNewReconciler:!1},Fg={readContext:we,useCallback:function(n,r){return ni().memoizedState=[n,r===void 0?null:r],n},useContext:we,useEffect:Mo,useImperativeHandle:function(n,r,l){return l=l!=null?l.concat([n]):null,ku(4194308,4,gf.bind(null,r,n),l)},useLayoutEffect:function(n,r){return ku(4194308,4,n,r)},useInsertionEffect:function(n,r){return ku(4,2,n,r)},useMemo:function(n,r){var l=ni();return r=r===void 0?null:r,n=n(),l.memoizedState=[n,r],n},useReducer:function(n,r,l){var u=ni();return r=l!==void 0?l(r):r,u.memoizedState=u.baseState=r,n={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:n,lastRenderedState:r},u.queue=n,n=n.dispatch=sn.bind(null,Sn,n),[u.memoizedState,n]},useRef:function(n){var r=ni();return n={current:n},r.memoizedState=n},useState:vf,useDebugValue:No,useDeferredValue:function(n){return ni().memoizedState=n},useTransition:function(){var n=vf(!1),r=n[0];return n=Ti.bind(null,n[1]),ni().memoizedState=n,[r,n]},useMutableSource:function(){},useSyncExternalStore:function(n,r,l){var u=Sn,f=ni();if(dn){if(l===void 0)throw Error(m(407));l=l()}else{if(l=r(),Dn===null)throw Error(m(349));ko&30||sf(u,r,l)}f.memoizedState=l;var v={value:l,getSnapshot:r};return f.queue=v,Mo(ff.bind(null,u,v,n),[n]),u.flags|=2048,Oo(9,cf.bind(null,u,v,l,r),void 0,null),l},useId:function(){var n=ni(),r=Dn.identifierPrefix;if(dn){var l=sr,u=ei;l=(u&~(1<<32-Lr(u)-1)).toString(32)+l,r=":"+r+"R"+l,l=Oa++,0<\/script>",n=n.removeChild(n.firstChild)):typeof u.is=="string"?n=b.createElement(l,{is:u.is}):(n=b.createElement(l),l==="select"&&(b=n,u.multiple?b.multiple=!0:u.size&&(b.size=u.size))):n=b.createElementNS(n,l),n[Fi]=r,n[Is]=u,La(n,r,!1,!1),r.stateNode=n;e:{switch(b=Ln(l,u),l){case"dialog":tn("cancel",n),tn("close",n),f=u;break;case"iframe":case"object":case"embed":tn("load",n),f=u;break;case"video":case"audio":for(f=0;fVu&&(r.flags|=128,u=!0,_n(v,!1),r.lanes=4194304)}else{if(!u)if(n=jt(b),n!==null){if(r.flags|=128,u=!0,l=n.updateQueue,l!==null&&(r.updateQueue=l,r.flags|=4),_n(v,!0),v.tail===null&&v.tailMode==="hidden"&&!b.alternate&&!dn)return Rr(r),null}else 2*on()-v.renderingStartTime>Vu&&l!==1073741824&&(r.flags|=128,u=!0,_n(v,!1),r.lanes=4194304);v.isBackwards?(b.sibling=r.child,r.child=b):(l=v.last,l!==null?l.sibling=b:r.child=b,v.last=b)}return v.tail!==null?(r=v.tail,v.rendering=r,v.tail=r.sibling,v.renderingStartTime=on(),r.sibling=null,l=Ie.current,Ht(Ie,u?l&1|2:l&1),r):(Rr(r),null);case 22:case 23:return Af(),u=r.memoizedState!==null,n!==null&&n.memoizedState!==null!==u&&(r.flags|=8192),u&&r.mode&1?li&1073741824&&(Rr(r),r.subtreeFlags&6&&(r.flags|=8192)):Rr(r),null;case 24:return null;case 25:return null}throw Error(m(156,r.tag))}function Bg(n,r){switch(Jc(r),r.tag){case 1:return Vn(r.type)&&Zr(),n=r.flags,n&65536?(r.flags=n&-65537|128,r):null;case 3:return Ru(),Ve(ut),Ve(er),Bi(),n=r.flags,n&65536&&!(n&128)?(r.flags=n&-65537|128,r):null;case 5:return qe(r),null;case 13:if(Ve(Ie),n=r.memoizedState,n!==null&&n.dehydrated!==null){if(r.alternate===null)throw Error(m(340));wu()}return n=r.flags,n&65536?(r.flags=n&-65537|128,r):null;case 19:return Ve(Ie),null;case 4:return Ru(),null;case 10:return cp(r.type._context),null;case 22:case 23:return Af(),null;case 24:return null;default:return null}}var Nu=!1,cr=!1,Tf=typeof WeakSet=="function"?WeakSet:Set,Ae=null;function zu(n,r){var l=n.ref;if(l!==null)if(typeof l=="function")try{l(null)}catch(u){On(n,r,u)}else l.current=null}function Cp(n,r,l){try{l()}catch(u){On(n,r,u)}}var Rf=!1;function Ig(n,r){if(Jd=su,n=mm(),zs(n)){if("selectionStart"in n)var l={start:n.selectionStart,end:n.selectionEnd};else e:{l=(l=n.ownerDocument)&&l.defaultView||window;var u=l.getSelection&&l.getSelection();if(u&&u.rangeCount!==0){l=u.anchorNode;var f=u.anchorOffset,v=u.focusNode;u=u.focusOffset;try{l.nodeType,v.nodeType}catch{l=null;break e}var b=0,R=-1,M=-1,q=0,se=0,fe=n,le=null;t:for(;;){for(var ke;fe!==l||f!==0&&fe.nodeType!==3||(R=b+f),fe!==v||u!==0&&fe.nodeType!==3||(M=b+u),fe.nodeType===3&&(b+=fe.nodeValue.length),(ke=fe.firstChild)!==null;)le=fe,fe=ke;for(;;){if(fe===n)break t;if(le===l&&++q===f&&(R=b),le===v&&++se===u&&(M=b),(ke=fe.nextSibling)!==null)break;fe=le,le=fe.parentNode}fe=ke}l=R===-1||M===-1?null:{start:R,end:M}}else l=null}l=l||{start:0,end:0}}else l=null;for(ep={focusedElem:n,selectionRange:l},su=!1,Ae=r;Ae!==null;)if(r=Ae,n=r.child,(r.subtreeFlags&1028)!==0&&n!==null)n.return=r,Ae=n;else for(;Ae!==null;){r=Ae;try{var Ue=r.alternate;if(r.flags&1024)switch(r.tag){case 0:case 11:case 15:break;case 1:if(Ue!==null){var $e=Ue.memoizedProps,jn=Ue.memoizedState,$=r.stateNode,j=$.getSnapshotBeforeUpdate(r.elementType===r.type?$e:ti(r.type,$e),jn);$.__reactInternalSnapshotBeforeUpdate=j}break;case 3:var Y=r.stateNode.containerInfo;Y.nodeType===1?Y.textContent="":Y.nodeType===9&&Y.documentElement&&Y.removeChild(Y.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(m(163))}}catch(ge){On(r,r.return,ge)}if(n=r.sibling,n!==null){n.return=r.return,Ae=n;break}Ae=r.return}return Ue=Rf,Rf=!1,Ue}function Au(n,r,l){var u=r.updateQueue;if(u=u!==null?u.lastEffect:null,u!==null){var f=u=u.next;do{if((f.tag&n)===n){var v=f.destroy;f.destroy=void 0,v!==void 0&&Cp(r,l,v)}f=f.next}while(f!==u)}}function kf(n,r){if(r=r.updateQueue,r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&n)===n){var u=l.create;l.destroy=u()}l=l.next}while(l!==r)}}function _f(n){var r=n.ref;if(r!==null){var l=n.stateNode;switch(n.tag){case 5:n=l;break;default:n=l}typeof r=="function"?r(n):r.current=n}}function Gm(n){var r=n.alternate;r!==null&&(n.alternate=null,Gm(r)),n.child=null,n.deletions=null,n.sibling=null,n.tag===5&&(r=n.stateNode,r!==null&&(delete r[Fi],delete r[Is],delete r[ip],delete r[Ag],delete r[Ug])),n.stateNode=null,n.return=null,n.dependencies=null,n.memoizedProps=null,n.memoizedState=null,n.pendingProps=null,n.stateNode=null,n.updateQueue=null}function Ep(n){return n.tag===5||n.tag===3||n.tag===4}function qm(n){e:for(;;){for(;n.sibling===null;){if(n.return===null||Ep(n.return))return null;n=n.return}for(n.sibling.return=n.return,n=n.sibling;n.tag!==5&&n.tag!==6&&n.tag!==18;){if(n.flags&2||n.child===null||n.tag===4)continue e;n.child.return=n,n=n.child}if(!(n.flags&2))return n.stateNode}}function oc(n,r,l){var u=n.tag;if(u===5||u===6)n=n.stateNode,r?l.nodeType===8?l.parentNode.insertBefore(n,r):l.insertBefore(n,r):(l.nodeType===8?(r=l.parentNode,r.insertBefore(n,l)):(r=l,r.appendChild(n)),l=l._reactRootContainer,l!=null||r.onclick!==null||(r.onclick=qc));else if(u!==4&&(n=n.child,n!==null))for(oc(n,r,l),n=n.sibling;n!==null;)oc(n,r,l),n=n.sibling}function Uu(n,r,l){var u=n.tag;if(u===5||u===6)n=n.stateNode,r?l.insertBefore(n,r):l.appendChild(n);else if(u!==4&&(n=n.child,n!==null))for(Uu(n,r,l),n=n.sibling;n!==null;)Uu(n,r,l),n=n.sibling}var hn=null,tr=!1;function Nr(n,r,l){for(l=l.child;l!==null;)ju(n,r,l),l=l.sibling}function ju(n,r,l){if(Qr&&typeof Qr.onCommitFiberUnmount=="function")try{Qr.onCommitFiberUnmount(xl,l)}catch{}switch(l.tag){case 5:cr||zu(l,r);case 6:var u=hn,f=tr;hn=null,Nr(n,r,l),hn=u,tr=f,hn!==null&&(tr?(n=hn,l=l.stateNode,n.nodeType===8?n.parentNode.removeChild(l):n.removeChild(l)):hn.removeChild(l.stateNode));break;case 18:hn!==null&&(tr?(n=hn,l=l.stateNode,n.nodeType===8?rp(n.parentNode,l):n.nodeType===1&&rp(n,l),ji(n)):rp(hn,l.stateNode));break;case 4:u=hn,f=tr,hn=l.stateNode.containerInfo,tr=!0,Nr(n,r,l),hn=u,tr=f;break;case 0:case 11:case 14:case 15:if(!cr&&(u=l.updateQueue,u!==null&&(u=u.lastEffect,u!==null))){f=u=u.next;do{var v=f,b=v.destroy;v=v.tag,b!==void 0&&(v&2||v&4)&&Cp(l,r,b),f=f.next}while(f!==u)}Nr(n,r,l);break;case 1:if(!cr&&(zu(l,r),u=l.stateNode,typeof u.componentWillUnmount=="function"))try{u.props=l.memoizedProps,u.state=l.memoizedState,u.componentWillUnmount()}catch(R){On(l,r,R)}Nr(n,r,l);break;case 21:Nr(n,r,l);break;case 22:l.mode&1?(cr=(u=cr)||l.memoizedState!==null,Nr(n,r,l),cr=u):Nr(n,r,l);break;default:Nr(n,r,l)}}function Fu(n){var r=n.updateQueue;if(r!==null){n.updateQueue=null;var l=n.stateNode;l===null&&(l=n.stateNode=new Tf),r.forEach(function(u){var f=Zg.bind(null,n,u);l.has(u)||(l.add(u),u.then(f,f))})}}function nr(n,r){var l=r.deletions;if(l!==null)for(var u=0;uf&&(f=b),u&=~v}if(u=f,u=on()-u,u=(120>u?120:480>u?480:1080>u?1080:1920>u?1920:3e3>u?3e3:4320>u?4320:1960*Wg(u/1960))-u,10n?16:n,Gi===null)var u=!1;else{if(n=Gi,Gi=null,Mf=0,xt&6)throw Error(m(331));var f=xt;for(xt|=4,Ae=n.current;Ae!==null;){var v=Ae,b=v.child;if(Ae.flags&16){var R=v.deletions;if(R!==null){for(var M=0;Mon()-Of?$o(n,0):Rp|=l),Xn(n,r)}function rh(n,r){r===0&&(n.mode&1?(r=au,au<<=1,!(au&130023424)&&(au=4194304)):r=1);var l=rr();n=al(n,r),n!==null&&(Rl(n,r,l),Xn(n,l))}function Lp(n){var r=n.memoizedState,l=0;r!==null&&(l=r.retryLane),rh(n,l)}function Zg(n,r){var l=0;switch(n.tag){case 13:var u=n.stateNode,f=n.memoizedState;f!==null&&(l=f.retryLane);break;case 19:u=n.stateNode;break;default:throw Error(m(314))}u!==null&&u.delete(r),rh(n,l)}var ih;ih=function(n,r,l){if(n!==null)if(n.memoizedProps!==r.pendingProps||ut.current)ii=!0;else{if(!(n.lanes&l)&&!(r.flags&128))return ii=!1,ol(n,r,l);ii=!!(n.flags&131072)}else ii=!1,dn&&r.flags&1048576&&jl(r,xo,r.index);switch(r.lanes=0,r.tag){case 2:var u=r.type;lc(n,r),n=r.pendingProps;var f=bi(r,er.current);Mn(r,l),f=_o(null,r,u,n,f,l);var v=Vl();return r.flags|=1,typeof f=="object"&&f!==null&&typeof f.render=="function"&&f.$$typeof===void 0?(r.tag=1,r.memoizedState=null,r.updateQueue=null,Vn(u)?(v=!0,Eo(r)):v=!1,r.memoizedState=f.state!==null&&f.state!==void 0?f.state:null,fp(r),f.updater=rf,r.stateNode=f,f._reactInternals=r,pp(r,u,n,l),r=bf(null,r,u,!0,v,l)):(r.tag=0,dn&&v&&Kc(r),An(null,r,f,l),r=r.child),r;case 16:u=r.elementType;e:{switch(lc(n,r),n=r.pendingProps,f=u._init,u=f(u._payload),r.type=u,f=r.tag=Kg(u),n=ti(u,n),f){case 0:r=ft(null,r,u,n,l);break e;case 1:r=ic(null,r,u,n,l);break e;case 11:r=Ou(null,r,u,n,l);break e;case 14:r=Yl(null,r,u,ti(u.type,n),l);break e}throw Error(m(306,u,""))}return r;case 0:return u=r.type,f=r.pendingProps,f=r.elementType===u?f:ti(u,f),ft(n,r,u,f,l);case 1:return u=r.type,f=r.pendingProps,f=r.elementType===u?f:ti(u,f),ic(n,r,u,f,l);case 3:e:{if(Pg(r),n===null)throw Error(m(387));u=r.pendingProps,v=r.memoizedState,f=v.element,Lm(n,r),Ws(r,u,null,l);var b=r.memoizedState;if(u=b.element,v.isDehydrated)if(v={element:u,isDehydrated:!1,cache:b.cache,pendingSuspenseBoundaries:b.pendingSuspenseBoundaries,transitions:b.transitions},r.updateQueue.baseState=v,r.memoizedState=v,r.flags&256){f=Il(Error(m(423)),r),r=Ym(n,r,u,l,f);break e}else if(u!==f){f=Il(Error(m(424)),r),r=Ym(n,r,u,l,f);break e}else for(Ei=Si(r.stateNode.containerInfo.firstChild),Ci=r,dn=!0,Vi=null,l=Fm(r,null,u,l),r.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling;else{if(wu(),u===f){r=Un(n,r,l);break e}An(n,r,u,l)}r=r.child}return r;case 5:return af(r),n===null&&ef(r),u=r.type,f=r.pendingProps,v=n!==null?n.memoizedProps:null,b=f.children,Co(u,f)?b=null:v!==null&&Co(u,v)&&(r.flags|=32),zo(n,r),An(n,r,b,l),r.child;case 6:return n===null&&ef(r),null;case 13:return Wm(n,r,l);case 4:return vp(r,r.stateNode.containerInfo),u=r.pendingProps,n===null?r.child=xu(r,null,u,l):An(n,r,u,l),r.child;case 11:return u=r.type,f=r.pendingProps,f=r.elementType===u?f:ti(u,f),Ou(n,r,u,f,l);case 7:return An(n,r,r.pendingProps,l),r.child;case 8:return An(n,r,r.pendingProps.children,l),r.child;case 12:return An(n,r,r.pendingProps.children,l),r.child;case 10:e:{if(u=r.type._context,f=r.pendingProps,v=r.memoizedProps,b=f.value,Ht(Su,u._currentValue),u._currentValue=b,v!==null)if(sa(v.value,b)){if(v.children===f.children&&!ut.current){r=Un(n,r,l);break e}}else for(v=r.child,v!==null&&(v.return=r);v!==null;){var R=v.dependencies;if(R!==null){b=v.child;for(var M=R.firstContext;M!==null;){if(M.context===u){if(v.tag===1){M=un(-1,l&-l),M.tag=2;var q=v.updateQueue;if(q!==null){q=q.shared;var se=q.pending;se===null?M.next=M:(M.next=se.next,se.next=M),q.pending=M}}v.lanes|=l,M=v.alternate,M!==null&&(M.lanes|=l),Fl(v.return,l,r),R.lanes|=l;break}M=M.next}}else if(v.tag===10)b=v.type===r.type?null:v.child;else if(v.tag===18){if(b=v.return,b===null)throw Error(m(341));b.lanes|=l,R=b.alternate,R!==null&&(R.lanes|=l),Fl(b,l,r),b=v.sibling}else b=v.child;if(b!==null)b.return=v;else for(b=v;b!==null;){if(b===r){b=null;break}if(v=b.sibling,v!==null){v.return=b.return,b=v;break}b=b.return}v=b}An(n,r,f.children,l),r=r.child}return r;case 9:return f=r.type,u=r.pendingProps.children,Mn(r,l),f=we(f),u=u(f),r.flags|=1,An(n,r,u,l),r.child;case 14:return u=r.type,f=ti(u,r.pendingProps),f=ti(u.type,f),Yl(n,r,u,f,l);case 15:return Sf(n,r,r.type,r.pendingProps,l);case 17:return u=r.type,f=r.pendingProps,f=r.elementType===u?f:ti(u,f),lc(n,r),r.tag=1,Vn(u)?(n=!0,Eo(r)):n=!1,Mn(r,l),zm(r,u,f),pp(r,u,f,l),bf(null,r,u,!0,n,l);case 19:return bp(n,r,l);case 22:return ai(n,r,l)}throw Error(m(156,r.tag))};function ah(n,r){return an(n,r)}function lh(n,r,l,u){this.tag=n,this.key=l,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=r,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=u,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function qi(n,r,l,u){return new lh(n,r,l,u)}function Mp(n){return n=n.prototype,!(!n||!n.isReactComponent)}function Kg(n){if(typeof n=="function")return Mp(n)?1:0;if(n!=null){if(n=n.$$typeof,n===ot)return 11;if(n===at)return 14}return 2}function Gl(n,r){var l=n.alternate;return l===null?(l=qi(n.tag,r,n.key,n.mode),l.elementType=n.elementType,l.type=n.type,l.stateNode=n.stateNode,l.alternate=n,n.alternate=l):(l.pendingProps=r,l.type=n.type,l.flags=0,l.subtreeFlags=0,l.deletions=null),l.flags=n.flags&14680064,l.childLanes=n.childLanes,l.lanes=n.lanes,l.child=n.child,l.memoizedProps=n.memoizedProps,l.memoizedState=n.memoizedState,l.updateQueue=n.updateQueue,r=n.dependencies,l.dependencies=r===null?null:{lanes:r.lanes,firstContext:r.firstContext},l.sibling=n.sibling,l.index=n.index,l.ref=n.ref,l}function jf(n,r,l,u,f,v){var b=2;if(u=n,typeof n=="function")Mp(n)&&(b=1);else if(typeof n=="string")b=5;else e:switch(n){case be:return Po(l.children,f,v,r);case xe:b=8,f|=8;break;case Ct:return n=qi(12,l,r,f|2),n.elementType=Ct,n.lanes=v,n;case Nt:return n=qi(13,l,r,f),n.elementType=Nt,n.lanes=v,n;case Ze:return n=qi(19,l,r,f),n.elementType=Ze,n.lanes=v,n;case nt:return Ff(l,f,v,r);default:if(typeof n=="object"&&n!==null)switch(n.$$typeof){case Ut:b=10;break e;case Dt:b=9;break e;case ot:b=11;break e;case at:b=14;break e;case bt:b=16,u=null;break e}throw Error(m(130,n==null?n:typeof n,""))}return r=qi(b,l,r,f),r.elementType=n,r.type=u,r.lanes=v,r}function Po(n,r,l,u){return n=qi(7,n,u,r),n.lanes=l,n}function Ff(n,r,l,u){return n=qi(22,n,u,r),n.elementType=nt,n.lanes=l,n.stateNode={isHidden:!1},n}function Hf(n,r,l){return n=qi(6,n,null,r),n.lanes=l,n}function dc(n,r,l){return r=qi(4,n.children!==null?n.children:[],n.key,r),r.lanes=l,r.stateNode={containerInfo:n.containerInfo,pendingChildren:null,implementation:n.implementation},r}function pc(n,r,l,u,f){this.tag=r,this.containerInfo=n,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=mo(0),this.expirationTimes=mo(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=mo(0),this.identifierPrefix=u,this.onRecoverableError=f,this.mutableSourceEagerHydrationData=null}function Np(n,r,l,u,f,v,b,R,M){return n=new pc(n,r,l,R,M),r===1?(r=1,v===!0&&(r|=8)):r=0,v=qi(3,null,null,r),n.current=v,v.stateNode=n,v.memoizedState={element:u,isDehydrated:l,cache:null,transitions:null,pendingSuspenseBoundaries:null},fp(v),n}function oh(n,r,l){var u=3<\/script>",n=n.removeChild(n.firstChild)):typeof u.is=="string"?n=b.createElement(l,{is:u.is}):(n=b.createElement(l),l==="select"&&(b=n,u.multiple?b.multiple=!0:u.size&&(b.size=u.size))):n=b.createElementNS(n,l),n[Fi]=r,n[Is]=u,La(n,r,!1,!1),r.stateNode=n;e:{switch(b=Ln(l,u),l){case"dialog":tn("cancel",n),tn("close",n),f=u;break;case"iframe":case"object":case"embed":tn("load",n),f=u;break;case"video":case"audio":for(f=0;fVu&&(r.flags|=128,u=!0,_n(v,!1),r.lanes=4194304)}else{if(!u)if(n=jt(b),n!==null){if(r.flags|=128,u=!0,l=n.updateQueue,l!==null&&(r.updateQueue=l,r.flags|=4),_n(v,!0),v.tail===null&&v.tailMode==="hidden"&&!b.alternate&&!dn)return Rr(r),null}else 2*on()-v.renderingStartTime>Vu&&l!==1073741824&&(r.flags|=128,u=!0,_n(v,!1),r.lanes=4194304);v.isBackwards?(b.sibling=r.child,r.child=b):(l=v.last,l!==null?l.sibling=b:r.child=b,v.last=b)}return v.tail!==null?(r=v.tail,v.rendering=r,v.tail=r.sibling,v.renderingStartTime=on(),r.sibling=null,l=Ie.current,Ht(Ie,u?l&1|2:l&1),r):(Rr(r),null);case 22:case 23:return Uf(),u=r.memoizedState!==null,n!==null&&n.memoizedState!==null!==u&&(r.flags|=8192),u&&r.mode&1?li&1073741824&&(Rr(r),r.subtreeFlags&6&&(r.flags|=8192)):Rr(r),null;case 24:return null;case 25:return null}throw Error(m(156,r.tag))}function Bg(n,r){switch(ef(r),r.tag){case 1:return Vn(r.type)&&Zr(),n=r.flags,n&65536?(r.flags=n&-65537|128,r):null;case 3:return Ru(),Ve(ut),Ve(er),Bi(),n=r.flags,n&65536&&!(n&128)?(r.flags=n&-65537|128,r):null;case 5:return qe(r),null;case 13:if(Ve(Ie),n=r.memoizedState,n!==null&&n.dehydrated!==null){if(r.alternate===null)throw Error(m(340));wu()}return n=r.flags,n&65536?(r.flags=n&-65537|128,r):null;case 19:return Ve(Ie),null;case 4:return Ru(),null;case 10:return cp(r.type._context),null;case 22:case 23:return Uf(),null;case 24:return null;default:return null}}var Nu=!1,cr=!1,Rf=typeof WeakSet=="function"?WeakSet:Set,Ae=null;function zu(n,r){var l=n.ref;if(l!==null)if(typeof l=="function")try{l(null)}catch(u){On(n,r,u)}else l.current=null}function Ep(n,r,l){try{l()}catch(u){On(n,r,u)}}var kf=!1;function Ig(n,r){if(Jd=su,n=mm(),zs(n)){if("selectionStart"in n)var l={start:n.selectionStart,end:n.selectionEnd};else e:{l=(l=n.ownerDocument)&&l.defaultView||window;var u=l.getSelection&&l.getSelection();if(u&&u.rangeCount!==0){l=u.anchorNode;var f=u.anchorOffset,v=u.focusNode;u=u.focusOffset;try{l.nodeType,v.nodeType}catch{l=null;break e}var b=0,R=-1,M=-1,q=0,se=0,fe=n,oe=null;t:for(;;){for(var ke;fe!==l||f!==0&&fe.nodeType!==3||(R=b+f),fe!==v||u!==0&&fe.nodeType!==3||(M=b+u),fe.nodeType===3&&(b+=fe.nodeValue.length),(ke=fe.firstChild)!==null;)oe=fe,fe=ke;for(;;){if(fe===n)break t;if(oe===l&&++q===f&&(R=b),oe===v&&++se===u&&(M=b),(ke=fe.nextSibling)!==null)break;fe=oe,oe=fe.parentNode}fe=ke}l=R===-1||M===-1?null:{start:R,end:M}}else l=null}l=l||{start:0,end:0}}else l=null;for(ep={focusedElem:n,selectionRange:l},su=!1,Ae=r;Ae!==null;)if(r=Ae,n=r.child,(r.subtreeFlags&1028)!==0&&n!==null)n.return=r,Ae=n;else for(;Ae!==null;){r=Ae;try{var Ue=r.alternate;if(r.flags&1024)switch(r.tag){case 0:case 11:case 15:break;case 1:if(Ue!==null){var $e=Ue.memoizedProps,jn=Ue.memoizedState,$=r.stateNode,j=$.getSnapshotBeforeUpdate(r.elementType===r.type?$e:ti(r.type,$e),jn);$.__reactInternalSnapshotBeforeUpdate=j}break;case 3:var Y=r.stateNode.containerInfo;Y.nodeType===1?Y.textContent="":Y.nodeType===9&&Y.documentElement&&Y.removeChild(Y.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(m(163))}}catch(ge){On(r,r.return,ge)}if(n=r.sibling,n!==null){n.return=r.return,Ae=n;break}Ae=r.return}return Ue=kf,kf=!1,Ue}function Au(n,r,l){var u=r.updateQueue;if(u=u!==null?u.lastEffect:null,u!==null){var f=u=u.next;do{if((f.tag&n)===n){var v=f.destroy;f.destroy=void 0,v!==void 0&&Ep(r,l,v)}f=f.next}while(f!==u)}}function _f(n,r){if(r=r.updateQueue,r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&n)===n){var u=l.create;l.destroy=u()}l=l.next}while(l!==r)}}function Df(n){var r=n.ref;if(r!==null){var l=n.stateNode;switch(n.tag){case 5:n=l;break;default:n=l}typeof r=="function"?r(n):r.current=n}}function Gm(n){var r=n.alternate;r!==null&&(n.alternate=null,Gm(r)),n.child=null,n.deletions=null,n.sibling=null,n.tag===5&&(r=n.stateNode,r!==null&&(delete r[Fi],delete r[Is],delete r[ip],delete r[Ag],delete r[Ug])),n.stateNode=null,n.return=null,n.dependencies=null,n.memoizedProps=null,n.memoizedState=null,n.pendingProps=null,n.stateNode=null,n.updateQueue=null}function Cp(n){return n.tag===5||n.tag===3||n.tag===4}function qm(n){e:for(;;){for(;n.sibling===null;){if(n.return===null||Cp(n.return))return null;n=n.return}for(n.sibling.return=n.return,n=n.sibling;n.tag!==5&&n.tag!==6&&n.tag!==18;){if(n.flags&2||n.child===null||n.tag===4)continue e;n.child.return=n,n=n.child}if(!(n.flags&2))return n.stateNode}}function oc(n,r,l){var u=n.tag;if(u===5||u===6)n=n.stateNode,r?l.nodeType===8?l.parentNode.insertBefore(n,r):l.insertBefore(n,r):(l.nodeType===8?(r=l.parentNode,r.insertBefore(n,l)):(r=l,r.appendChild(n)),l=l._reactRootContainer,l!=null||r.onclick!==null||(r.onclick=Xc));else if(u!==4&&(n=n.child,n!==null))for(oc(n,r,l),n=n.sibling;n!==null;)oc(n,r,l),n=n.sibling}function Uu(n,r,l){var u=n.tag;if(u===5||u===6)n=n.stateNode,r?l.insertBefore(n,r):l.appendChild(n);else if(u!==4&&(n=n.child,n!==null))for(Uu(n,r,l),n=n.sibling;n!==null;)Uu(n,r,l),n=n.sibling}var hn=null,tr=!1;function Nr(n,r,l){for(l=l.child;l!==null;)ju(n,r,l),l=l.sibling}function ju(n,r,l){if(Qr&&typeof Qr.onCommitFiberUnmount=="function")try{Qr.onCommitFiberUnmount(xl,l)}catch{}switch(l.tag){case 5:cr||zu(l,r);case 6:var u=hn,f=tr;hn=null,Nr(n,r,l),hn=u,tr=f,hn!==null&&(tr?(n=hn,l=l.stateNode,n.nodeType===8?n.parentNode.removeChild(l):n.removeChild(l)):hn.removeChild(l.stateNode));break;case 18:hn!==null&&(tr?(n=hn,l=l.stateNode,n.nodeType===8?rp(n.parentNode,l):n.nodeType===1&&rp(n,l),ji(n)):rp(hn,l.stateNode));break;case 4:u=hn,f=tr,hn=l.stateNode.containerInfo,tr=!0,Nr(n,r,l),hn=u,tr=f;break;case 0:case 11:case 14:case 15:if(!cr&&(u=l.updateQueue,u!==null&&(u=u.lastEffect,u!==null))){f=u=u.next;do{var v=f,b=v.destroy;v=v.tag,b!==void 0&&(v&2||v&4)&&Ep(l,r,b),f=f.next}while(f!==u)}Nr(n,r,l);break;case 1:if(!cr&&(zu(l,r),u=l.stateNode,typeof u.componentWillUnmount=="function"))try{u.props=l.memoizedProps,u.state=l.memoizedState,u.componentWillUnmount()}catch(R){On(l,r,R)}Nr(n,r,l);break;case 21:Nr(n,r,l);break;case 22:l.mode&1?(cr=(u=cr)||l.memoizedState!==null,Nr(n,r,l),cr=u):Nr(n,r,l);break;default:Nr(n,r,l)}}function Fu(n){var r=n.updateQueue;if(r!==null){n.updateQueue=null;var l=n.stateNode;l===null&&(l=n.stateNode=new Rf),r.forEach(function(u){var f=Zg.bind(null,n,u);l.has(u)||(l.add(u),u.then(f,f))})}}function nr(n,r){var l=r.deletions;if(l!==null)for(var u=0;uf&&(f=b),u&=~v}if(u=f,u=on()-u,u=(120>u?120:480>u?480:1080>u?1080:1920>u?1920:3e3>u?3e3:4320>u?4320:1960*Wg(u/1960))-u,10n?16:n,Gi===null)var u=!1;else{if(n=Gi,Gi=null,Nf=0,xt&6)throw Error(m(331));var f=xt;for(xt|=4,Ae=n.current;Ae!==null;){var v=Ae,b=v.child;if(Ae.flags&16){var R=v.deletions;if(R!==null){for(var M=0;Mon()-Lf?$o(n,0):Rp|=l),Xn(n,r)}function rh(n,r){r===0&&(n.mode&1?(r=au,au<<=1,!(au&130023424)&&(au=4194304)):r=1);var l=rr();n=al(n,r),n!==null&&(Rl(n,r,l),Xn(n,l))}function Lp(n){var r=n.memoizedState,l=0;r!==null&&(l=r.retryLane),rh(n,l)}function Zg(n,r){var l=0;switch(n.tag){case 13:var u=n.stateNode,f=n.memoizedState;f!==null&&(l=f.retryLane);break;case 19:u=n.stateNode;break;default:throw Error(m(314))}u!==null&&u.delete(r),rh(n,l)}var ih;ih=function(n,r,l){if(n!==null)if(n.memoizedProps!==r.pendingProps||ut.current)ii=!0;else{if(!(n.lanes&l)&&!(r.flags&128))return ii=!1,ol(n,r,l);ii=!!(n.flags&131072)}else ii=!1,dn&&r.flags&1048576&&jl(r,xo,r.index);switch(r.lanes=0,r.tag){case 2:var u=r.type;lc(n,r),n=r.pendingProps;var f=bi(r,er.current);Mn(r,l),f=_o(null,r,u,n,f,l);var v=Vl();return r.flags|=1,typeof f=="object"&&f!==null&&typeof f.render=="function"&&f.$$typeof===void 0?(r.tag=1,r.memoizedState=null,r.updateQueue=null,Vn(u)?(v=!0,Co(r)):v=!1,r.memoizedState=f.state!==null&&f.state!==void 0?f.state:null,fp(r),f.updater=af,r.stateNode=f,f._reactInternals=r,pp(r,u,n,l),r=Ef(null,r,u,!0,v,l)):(r.tag=0,dn&&v&&Jc(r),An(null,r,f,l),r=r.child),r;case 16:u=r.elementType;e:{switch(lc(n,r),n=r.pendingProps,f=u._init,u=f(u._payload),r.type=u,f=r.tag=Kg(u),n=ti(u,n),f){case 0:r=ft(null,r,u,n,l);break e;case 1:r=ic(null,r,u,n,l);break e;case 11:r=Ou(null,r,u,n,l);break e;case 14:r=Yl(null,r,u,ti(u.type,n),l);break e}throw Error(m(306,u,""))}return r;case 0:return u=r.type,f=r.pendingProps,f=r.elementType===u?f:ti(u,f),ft(n,r,u,f,l);case 1:return u=r.type,f=r.pendingProps,f=r.elementType===u?f:ti(u,f),ic(n,r,u,f,l);case 3:e:{if(Pg(r),n===null)throw Error(m(387));u=r.pendingProps,v=r.memoizedState,f=v.element,Lm(n,r),Ws(r,u,null,l);var b=r.memoizedState;if(u=b.element,v.isDehydrated)if(v={element:u,isDehydrated:!1,cache:b.cache,pendingSuspenseBoundaries:b.pendingSuspenseBoundaries,transitions:b.transitions},r.updateQueue.baseState=v,r.memoizedState=v,r.flags&256){f=Il(Error(m(423)),r),r=Ym(n,r,u,l,f);break e}else if(u!==f){f=Il(Error(m(424)),r),r=Ym(n,r,u,l,f);break e}else for(Ci=Si(r.stateNode.containerInfo.firstChild),Ei=r,dn=!0,Vi=null,l=Fm(r,null,u,l),r.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling;else{if(wu(),u===f){r=Un(n,r,l);break e}An(n,r,u,l)}r=r.child}return r;case 5:return lf(r),n===null&&tf(r),u=r.type,f=r.pendingProps,v=n!==null?n.memoizedProps:null,b=f.children,Eo(u,f)?b=null:v!==null&&Eo(u,v)&&(r.flags|=32),zo(n,r),An(n,r,b,l),r.child;case 6:return n===null&&tf(r),null;case 13:return Wm(n,r,l);case 4:return vp(r,r.stateNode.containerInfo),u=r.pendingProps,n===null?r.child=xu(r,null,u,l):An(n,r,u,l),r.child;case 11:return u=r.type,f=r.pendingProps,f=r.elementType===u?f:ti(u,f),Ou(n,r,u,f,l);case 7:return An(n,r,r.pendingProps,l),r.child;case 8:return An(n,r,r.pendingProps.children,l),r.child;case 12:return An(n,r,r.pendingProps.children,l),r.child;case 10:e:{if(u=r.type._context,f=r.pendingProps,v=r.memoizedProps,b=f.value,Ht(Su,u._currentValue),u._currentValue=b,v!==null)if(sa(v.value,b)){if(v.children===f.children&&!ut.current){r=Un(n,r,l);break e}}else for(v=r.child,v!==null&&(v.return=r);v!==null;){var R=v.dependencies;if(R!==null){b=v.child;for(var M=R.firstContext;M!==null;){if(M.context===u){if(v.tag===1){M=un(-1,l&-l),M.tag=2;var q=v.updateQueue;if(q!==null){q=q.shared;var se=q.pending;se===null?M.next=M:(M.next=se.next,se.next=M),q.pending=M}}v.lanes|=l,M=v.alternate,M!==null&&(M.lanes|=l),Fl(v.return,l,r),R.lanes|=l;break}M=M.next}}else if(v.tag===10)b=v.type===r.type?null:v.child;else if(v.tag===18){if(b=v.return,b===null)throw Error(m(341));b.lanes|=l,R=b.alternate,R!==null&&(R.lanes|=l),Fl(b,l,r),b=v.sibling}else b=v.child;if(b!==null)b.return=v;else for(b=v;b!==null;){if(b===r){b=null;break}if(v=b.sibling,v!==null){v.return=b.return,b=v;break}b=b.return}v=b}An(n,r,f.children,l),r=r.child}return r;case 9:return f=r.type,u=r.pendingProps.children,Mn(r,l),f=we(f),u=u(f),r.flags|=1,An(n,r,u,l),r.child;case 14:return u=r.type,f=ti(u,r.pendingProps),f=ti(u.type,f),Yl(n,r,u,f,l);case 15:return bf(n,r,r.type,r.pendingProps,l);case 17:return u=r.type,f=r.pendingProps,f=r.elementType===u?f:ti(u,f),lc(n,r),r.tag=1,Vn(u)?(n=!0,Co(r)):n=!1,Mn(r,l),zm(r,u,f),pp(r,u,f,l),Ef(null,r,u,!0,n,l);case 19:return bp(n,r,l);case 22:return ai(n,r,l)}throw Error(m(156,r.tag))};function ah(n,r){return an(n,r)}function lh(n,r,l,u){this.tag=n,this.key=l,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=r,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=u,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function qi(n,r,l,u){return new lh(n,r,l,u)}function Mp(n){return n=n.prototype,!(!n||!n.isReactComponent)}function Kg(n){if(typeof n=="function")return Mp(n)?1:0;if(n!=null){if(n=n.$$typeof,n===ot)return 11;if(n===at)return 14}return 2}function Gl(n,r){var l=n.alternate;return l===null?(l=qi(n.tag,r,n.key,n.mode),l.elementType=n.elementType,l.type=n.type,l.stateNode=n.stateNode,l.alternate=n,n.alternate=l):(l.pendingProps=r,l.type=n.type,l.flags=0,l.subtreeFlags=0,l.deletions=null),l.flags=n.flags&14680064,l.childLanes=n.childLanes,l.lanes=n.lanes,l.child=n.child,l.memoizedProps=n.memoizedProps,l.memoizedState=n.memoizedState,l.updateQueue=n.updateQueue,r=n.dependencies,l.dependencies=r===null?null:{lanes:r.lanes,firstContext:r.firstContext},l.sibling=n.sibling,l.index=n.index,l.ref=n.ref,l}function Ff(n,r,l,u,f,v){var b=2;if(u=n,typeof n=="function")Mp(n)&&(b=1);else if(typeof n=="string")b=5;else e:switch(n){case be:return Po(l.children,f,v,r);case xe:b=8,f|=8;break;case Et:return n=qi(12,l,r,f|2),n.elementType=Et,n.lanes=v,n;case Nt:return n=qi(13,l,r,f),n.elementType=Nt,n.lanes=v,n;case Ze:return n=qi(19,l,r,f),n.elementType=Ze,n.lanes=v,n;case nt:return Hf(l,f,v,r);default:if(typeof n=="object"&&n!==null)switch(n.$$typeof){case Ut:b=10;break e;case Dt:b=9;break e;case ot:b=11;break e;case at:b=14;break e;case bt:b=16,u=null;break e}throw Error(m(130,n==null?n:typeof n,""))}return r=qi(b,l,r,f),r.elementType=n,r.type=u,r.lanes=v,r}function Po(n,r,l,u){return n=qi(7,n,u,r),n.lanes=l,n}function Hf(n,r,l,u){return n=qi(22,n,u,r),n.elementType=nt,n.lanes=l,n.stateNode={isHidden:!1},n}function $f(n,r,l){return n=qi(6,n,null,r),n.lanes=l,n}function dc(n,r,l){return r=qi(4,n.children!==null?n.children:[],n.key,r),r.lanes=l,r.stateNode={containerInfo:n.containerInfo,pendingChildren:null,implementation:n.implementation},r}function pc(n,r,l,u,f){this.tag=r,this.containerInfo=n,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=mo(0),this.expirationTimes=mo(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=mo(0),this.identifierPrefix=u,this.onRecoverableError=f,this.mutableSourceEagerHydrationData=null}function Np(n,r,l,u,f,v,b,R,M){return n=new pc(n,r,l,R,M),r===1?(r=1,v===!0&&(r|=8)):r=0,v=qi(3,null,null,r),n.current=v,v.stateNode=n,v.memoizedState={element:u,isDehydrated:l,cache:null,transitions:null,pendingSuspenseBoundaries:null},fp(v),n}function oh(n,r,l){var u=31?t-1:0),a=1;a1?t-1:0),a=1;a2&&(e[0]==="o"||e[0]==="O")&&(e[1]==="n"||e[1]==="N")}function Cr(e,t,i,a){if(i!==null&&i.type===vi)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":{if(a)return!1;if(i!==null)return!i.acceptsBooleans;var o=e.toLowerCase().slice(0,5);return o!=="data-"&&o!=="aria-"}default:return!1}}function Gt(e,t,i,a){if(t===null||typeof t>"u"||Cr(e,t,i,a))return!0;if(a)return!1;if(i!==null)switch(i.type){case mn:return!t;case mi:return t===!1;case hi:return isNaN(t);case yi:return isNaN(t)||t<1}return!1}function Jn(e){return rn.hasOwnProperty(e)?rn[e]:null}function Yt(e,t,i,a,o,s,d){this.acceptsBooleans=t===Or||t===mn||t===mi,this.attributeName=a,this.attributeNamespace=o,this.mustUseProperty=i,this.propertyName=e,this.type=t,this.sanitizeURL=s,this.removeEmptyString=d}var rn={},ra=["children","dangerouslySetInnerHTML","defaultValue","defaultChecked","innerHTML","suppressContentEditableWarning","suppressHydrationWarning","style"];ra.forEach(function(e){rn[e]=new Yt(e,vi,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0],i=e[1];rn[t]=new Yt(t,lr,!1,i,null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){rn[e]=new Yt(e,Or,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){rn[e]=new Yt(e,Or,!1,e,null,!1,!1)}),["allowFullScreen","async","autoFocus","autoPlay","controls","default","defer","disabled","disablePictureInPicture","disableRemotePlayback","formNoValidate","hidden","loop","noModule","noValidate","open","playsInline","readOnly","required","reversed","scoped","seamless","itemScope"].forEach(function(e){rn[e]=new Yt(e,mn,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){rn[e]=new Yt(e,mn,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){rn[e]=new Yt(e,mi,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){rn[e]=new Yt(e,yi,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){rn[e]=new Yt(e,hi,!1,e.toLowerCase(),null,!1,!1)});var Ni=/[\-\:]([a-z])/g,Cl=function(e){return e[1].toUpperCase()};["accent-height","alignment-baseline","arabic-form","baseline-shift","cap-height","clip-path","clip-rule","color-interpolation","color-interpolation-filters","color-profile","color-rendering","dominant-baseline","enable-background","fill-opacity","fill-rule","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","glyph-name","glyph-orientation-horizontal","glyph-orientation-vertical","horiz-adv-x","horiz-origin-x","image-rendering","letter-spacing","lighting-color","marker-end","marker-mid","marker-start","overline-position","overline-thickness","paint-order","panose-1","pointer-events","rendering-intent","shape-rendering","stop-color","stop-opacity","strikethrough-position","strikethrough-thickness","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","text-anchor","text-decoration","text-rendering","underline-position","underline-thickness","unicode-bidi","unicode-range","units-per-em","v-alphabetic","v-hanging","v-ideographic","v-mathematical","vector-effect","vert-adv-y","vert-origin-x","vert-origin-y","word-spacing","writing-mode","xmlns:xlink","x-height"].forEach(function(e){var t=e.replace(Ni,Cl);rn[t]=new Yt(t,lr,!1,e,null,!1,!1)}),["xlink:actuate","xlink:arcrole","xlink:role","xlink:show","xlink:title","xlink:type"].forEach(function(e){var t=e.replace(Ni,Cl);rn[t]=new Yt(t,lr,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Ni,Cl);rn[t]=new Yt(t,lr,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){rn[e]=new Yt(e,lr,!1,e.toLowerCase(),null,!1,!1)});var so="xlinkHref";rn[so]=new Yt("xlinkHref",lr,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){rn[e]=new Yt(e,lr,!1,e.toLowerCase(),null,!0,!0)});var co=/^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i,El=!1;function ia(e){!El&&co.test(e)&&(El=!0,g("A future version of React will block javascript: URLs as a security precaution. Use event handlers instead if you can. If you need to generate unsafe HTML try using dangerouslySetInnerHTML instead. React was passed %s.",JSON.stringify(e)))}function aa(e,t,i,a){if(a.mustUseProperty){var o=a.propertyName;return e[o]}else{Dr(i,t),a.sanitizeURL&&ia(""+i);var s=a.attributeName,d=null;if(a.type===mi){if(e.hasAttribute(s)){var h=e.getAttribute(s);return h===""?!0:Gt(t,i,a,!1)?h:h===""+i?i:h}}else if(e.hasAttribute(s)){if(Gt(t,i,a,!1))return e.getAttribute(s);if(a.type===mn)return i;d=e.getAttribute(s)}return Gt(t,i,a,!1)?d===null?i:d:d===""+i?i:d}}function zi(e,t,i,a){{if(!Ln(t))return;if(!e.hasAttribute(t))return i===void 0?void 0:null;var o=e.getAttribute(t);return Dr(i,t),o===""+i?i:o}}function Ba(e,t,i,a){var o=Jn(t);if(!yn(t,o,a)){if(Gt(t,i,o,a)&&(i=null),a||o===null){if(Ln(t)){var s=t;i===null?e.removeAttribute(s):(Dr(i,t),e.setAttribute(s,""+i))}return}var d=o.mustUseProperty;if(d){var h=o.propertyName;if(i===null){var y=o.type;e[h]=y===mn?!1:""}else e[h]=i;return}var C=o.attributeName,E=o.attributeNamespace;if(i===null)e.removeAttribute(C);else{var F=o.type,A;F===mn||F===mi&&i===!0?A="":(Dr(i,C),A=""+i,o.sanitizeURL&&ia(A.toString())),E?e.setAttributeNS(E,C,A):e.setAttribute(C,A)}}}var Ir=Symbol.for("react.element"),Yr=Symbol.for("react.portal"),gi=Symbol.for("react.fragment"),Ia=Symbol.for("react.strict_mode"),O=Symbol.for("react.profiler"),ue=Symbol.for("react.provider"),Ee=Symbol.for("react.context"),Te=Symbol.for("react.forward_ref"),Et=Symbol.for("react.suspense"),Ot=Symbol.for("react.suspense_list"),mt=Symbol.for("react.memo"),Qe=Symbol.for("react.lazy"),Pn=Symbol.for("react.scope"),an=Symbol.for("react.debug_trace_mode"),ln=Symbol.for("react.offscreen"),Er=Symbol.for("react.legacy_hidden"),wa=Symbol.for("react.cache"),on=Symbol.for("react.tracing_marker"),Wr=Symbol.iterator,hs="@@iterator";function Sa(e){if(e===null||typeof e!="object")return null;var t=Wr&&e[Wr]||e[hs];return typeof t=="function"?t:null}var yt=Object.assign,fo=0,Ya,xl,Qr,ys,Lr,gs,ws;function Ss(){}Ss.__reactDisabledLog=!0;function po(){{if(fo===0){Ya=console.log,xl=console.info,Qr=console.warn,ys=console.error,Lr=console.group,gs=console.groupCollapsed,ws=console.groupEnd;var e={configurable:!0,enumerable:!0,value:Ss,writable:!0};Object.defineProperties(console,{info:e,log:e,warn:e,error:e,group:e,groupCollapsed:e,groupEnd:e})}fo++}}function au(){{if(fo--,fo===0){var e={configurable:!0,enumerable:!0,writable:!0};Object.defineProperties(console,{log:yt({},e,{value:Ya}),info:yt({},e,{value:xl}),warn:yt({},e,{value:Qr}),error:yt({},e,{value:ys}),group:yt({},e,{value:Lr}),groupCollapsed:yt({},e,{value:gs}),groupEnd:yt({},e,{value:ws})})}fo<0&&g("disabledDepth fell below zero. This is a bug in React. Please file an issue.")}}var Wa=S.ReactCurrentDispatcher,ba;function Ai(e,t,i){{if(ba===void 0)try{throw Error()}catch(o){var a=o.stack.trim().match(/\n( *(at )?)/);ba=a&&a[1]||""}return` -`+ba+e}}var Tl=!1,Ca;{var vo=typeof WeakMap=="function"?WeakMap:Map;Ca=new vo}function mo(e,t){if(!e||Tl)return"";{var i=Ca.get(e);if(i!==void 0)return i}var a;Tl=!0;var o=Error.prepareStackTrace;Error.prepareStackTrace=void 0;var s;s=Wa.current,Wa.current=null,po();try{if(t){var d=function(){throw Error()};if(Object.defineProperty(d.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(d,[])}catch(G){a=G}Reflect.construct(e,[],d)}else{try{d.call()}catch(G){a=G}e.call(d.prototype)}}else{try{throw Error()}catch(G){a=G}e()}}catch(G){if(G&&a&&typeof G.stack=="string"){for(var h=G.stack.split(` + */return c.NODE_ENV!=="production"&&function(){typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart=="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error);var p=I,m=tS(),S=p.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,x=!1;function N(e){x=e}function k(e){if(!x){for(var t=arguments.length,i=new Array(t>1?t-1:0),a=1;a1?t-1:0),a=1;a2&&(e[0]==="o"||e[0]==="O")&&(e[1]==="n"||e[1]==="N")}function Er(e,t,i,a){if(i!==null&&i.type===vi)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":{if(a)return!1;if(i!==null)return!i.acceptsBooleans;var o=e.toLowerCase().slice(0,5);return o!=="data-"&&o!=="aria-"}default:return!1}}function Gt(e,t,i,a){if(t===null||typeof t>"u"||Er(e,t,i,a))return!0;if(a)return!1;if(i!==null)switch(i.type){case mn:return!t;case mi:return t===!1;case hi:return isNaN(t);case yi:return isNaN(t)||t<1}return!1}function Jn(e){return rn.hasOwnProperty(e)?rn[e]:null}function Yt(e,t,i,a,o,s,d){this.acceptsBooleans=t===Or||t===mn||t===mi,this.attributeName=a,this.attributeNamespace=o,this.mustUseProperty=i,this.propertyName=e,this.type=t,this.sanitizeURL=s,this.removeEmptyString=d}var rn={},ra=["children","dangerouslySetInnerHTML","defaultValue","defaultChecked","innerHTML","suppressContentEditableWarning","suppressHydrationWarning","style"];ra.forEach(function(e){rn[e]=new Yt(e,vi,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0],i=e[1];rn[t]=new Yt(t,lr,!1,i,null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){rn[e]=new Yt(e,Or,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){rn[e]=new Yt(e,Or,!1,e,null,!1,!1)}),["allowFullScreen","async","autoFocus","autoPlay","controls","default","defer","disabled","disablePictureInPicture","disableRemotePlayback","formNoValidate","hidden","loop","noModule","noValidate","open","playsInline","readOnly","required","reversed","scoped","seamless","itemScope"].forEach(function(e){rn[e]=new Yt(e,mn,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){rn[e]=new Yt(e,mn,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){rn[e]=new Yt(e,mi,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){rn[e]=new Yt(e,yi,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){rn[e]=new Yt(e,hi,!1,e.toLowerCase(),null,!1,!1)});var Ni=/[\-\:]([a-z])/g,El=function(e){return e[1].toUpperCase()};["accent-height","alignment-baseline","arabic-form","baseline-shift","cap-height","clip-path","clip-rule","color-interpolation","color-interpolation-filters","color-profile","color-rendering","dominant-baseline","enable-background","fill-opacity","fill-rule","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","glyph-name","glyph-orientation-horizontal","glyph-orientation-vertical","horiz-adv-x","horiz-origin-x","image-rendering","letter-spacing","lighting-color","marker-end","marker-mid","marker-start","overline-position","overline-thickness","paint-order","panose-1","pointer-events","rendering-intent","shape-rendering","stop-color","stop-opacity","strikethrough-position","strikethrough-thickness","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","text-anchor","text-decoration","text-rendering","underline-position","underline-thickness","unicode-bidi","unicode-range","units-per-em","v-alphabetic","v-hanging","v-ideographic","v-mathematical","vector-effect","vert-adv-y","vert-origin-x","vert-origin-y","word-spacing","writing-mode","xmlns:xlink","x-height"].forEach(function(e){var t=e.replace(Ni,El);rn[t]=new Yt(t,lr,!1,e,null,!1,!1)}),["xlink:actuate","xlink:arcrole","xlink:role","xlink:show","xlink:title","xlink:type"].forEach(function(e){var t=e.replace(Ni,El);rn[t]=new Yt(t,lr,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Ni,El);rn[t]=new Yt(t,lr,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){rn[e]=new Yt(e,lr,!1,e.toLowerCase(),null,!1,!1)});var so="xlinkHref";rn[so]=new Yt("xlinkHref",lr,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){rn[e]=new Yt(e,lr,!1,e.toLowerCase(),null,!0,!0)});var co=/^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i,Cl=!1;function ia(e){!Cl&&co.test(e)&&(Cl=!0,g("A future version of React will block javascript: URLs as a security precaution. Use event handlers instead if you can. If you need to generate unsafe HTML try using dangerouslySetInnerHTML instead. React was passed %s.",JSON.stringify(e)))}function aa(e,t,i,a){if(a.mustUseProperty){var o=a.propertyName;return e[o]}else{Dr(i,t),a.sanitizeURL&&ia(""+i);var s=a.attributeName,d=null;if(a.type===mi){if(e.hasAttribute(s)){var h=e.getAttribute(s);return h===""?!0:Gt(t,i,a,!1)?h:h===""+i?i:h}}else if(e.hasAttribute(s)){if(Gt(t,i,a,!1))return e.getAttribute(s);if(a.type===mn)return i;d=e.getAttribute(s)}return Gt(t,i,a,!1)?d===null?i:d:d===""+i?i:d}}function zi(e,t,i,a){{if(!Ln(t))return;if(!e.hasAttribute(t))return i===void 0?void 0:null;var o=e.getAttribute(t);return Dr(i,t),o===""+i?i:o}}function Ba(e,t,i,a){var o=Jn(t);if(!yn(t,o,a)){if(Gt(t,i,o,a)&&(i=null),a||o===null){if(Ln(t)){var s=t;i===null?e.removeAttribute(s):(Dr(i,t),e.setAttribute(s,""+i))}return}var d=o.mustUseProperty;if(d){var h=o.propertyName;if(i===null){var y=o.type;e[h]=y===mn?!1:""}else e[h]=i;return}var E=o.attributeName,C=o.attributeNamespace;if(i===null)e.removeAttribute(E);else{var F=o.type,A;F===mn||F===mi&&i===!0?A="":(Dr(i,E),A=""+i,o.sanitizeURL&&ia(A.toString())),C?e.setAttributeNS(C,E,A):e.setAttribute(E,A)}}}var Ir=Symbol.for("react.element"),Yr=Symbol.for("react.portal"),gi=Symbol.for("react.fragment"),Ia=Symbol.for("react.strict_mode"),O=Symbol.for("react.profiler"),ue=Symbol.for("react.provider"),Ce=Symbol.for("react.context"),Te=Symbol.for("react.forward_ref"),Ct=Symbol.for("react.suspense"),Ot=Symbol.for("react.suspense_list"),mt=Symbol.for("react.memo"),Qe=Symbol.for("react.lazy"),Pn=Symbol.for("react.scope"),an=Symbol.for("react.debug_trace_mode"),ln=Symbol.for("react.offscreen"),Cr=Symbol.for("react.legacy_hidden"),wa=Symbol.for("react.cache"),on=Symbol.for("react.tracing_marker"),Wr=Symbol.iterator,hs="@@iterator";function Sa(e){if(e===null||typeof e!="object")return null;var t=Wr&&e[Wr]||e[hs];return typeof t=="function"?t:null}var yt=Object.assign,fo=0,Ya,xl,Qr,ys,Lr,gs,ws;function Ss(){}Ss.__reactDisabledLog=!0;function po(){{if(fo===0){Ya=console.log,xl=console.info,Qr=console.warn,ys=console.error,Lr=console.group,gs=console.groupCollapsed,ws=console.groupEnd;var e={configurable:!0,enumerable:!0,value:Ss,writable:!0};Object.defineProperties(console,{info:e,log:e,warn:e,error:e,group:e,groupCollapsed:e,groupEnd:e})}fo++}}function au(){{if(fo--,fo===0){var e={configurable:!0,enumerable:!0,writable:!0};Object.defineProperties(console,{log:yt({},e,{value:Ya}),info:yt({},e,{value:xl}),warn:yt({},e,{value:Qr}),error:yt({},e,{value:ys}),group:yt({},e,{value:Lr}),groupCollapsed:yt({},e,{value:gs}),groupEnd:yt({},e,{value:ws})})}fo<0&&g("disabledDepth fell below zero. This is a bug in React. Please file an issue.")}}var Wa=S.ReactCurrentDispatcher,ba;function Ai(e,t,i){{if(ba===void 0)try{throw Error()}catch(o){var a=o.stack.trim().match(/\n( *(at )?)/);ba=a&&a[1]||""}return` +`+ba+e}}var Tl=!1,Ea;{var vo=typeof WeakMap=="function"?WeakMap:Map;Ea=new vo}function mo(e,t){if(!e||Tl)return"";{var i=Ea.get(e);if(i!==void 0)return i}var a;Tl=!0;var o=Error.prepareStackTrace;Error.prepareStackTrace=void 0;var s;s=Wa.current,Wa.current=null,po();try{if(t){var d=function(){throw Error()};if(Object.defineProperty(d.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(d,[])}catch(G){a=G}Reflect.construct(e,[],d)}else{try{d.call()}catch(G){a=G}e.call(d.prototype)}}else{try{throw Error()}catch(G){a=G}e()}}catch(G){if(G&&a&&typeof G.stack=="string"){for(var h=G.stack.split(` `),y=a.stack.split(` -`),C=h.length-1,E=y.length-1;C>=1&&E>=0&&h[C]!==y[E];)E--;for(;C>=1&&E>=0;C--,E--)if(h[C]!==y[E]){if(C!==1||E!==1)do if(C--,E--,E<0||h[C]!==y[E]){var F=` -`+h[C].replace(" at new "," at ");return e.displayName&&F.includes("")&&(F=F.replace("",e.displayName)),typeof e=="function"&&Ca.set(e,F),F}while(C>=1&&E>=0);break}}}finally{Tl=!1,Wa.current=s,au(),Error.prepareStackTrace=o}var A=e?e.displayName||e.name:"",Q=A?Ai(A):"";return typeof e=="function"&&Ca.set(e,Q),Q}function Rl(e,t,i){return mo(e,!0)}function bs(e,t,i){return mo(e,!1)}function Cs(e){var t=e.prototype;return!!(t&&t.isReactComponent)}function kt(e,t,i){if(e==null)return"";if(typeof e=="function")return mo(e,Cs(e));if(typeof e=="string")return Ai(e);switch(e){case Et:return Ai("Suspense");case Ot:return Ai("SuspenseList")}if(typeof e=="object")switch(e.$$typeof){case Te:return bs(e.render);case mt:return kt(e.type,t,i);case Qe:{var a=e,o=a._payload,s=a._init;try{return kt(s(o),t,i)}catch{}}}return""}function Es(e){switch(e._debugOwner&&e._debugOwner.type,e._debugSource,e.tag){case z:return Ai(e.type);case Ct:return Ai("Lazy");case ve:return Ai("Suspense");case ot:return Ai("SuspenseList");case H:case ae:case xe:return bs(e.type);case ce:return bs(e.type.render);case B:return Rl(e.type);default:return""}}function lu(e){try{var t="",i=e;do t+=Es(i),i=i.return;while(i);return t}catch(a){return` +`),E=h.length-1,C=y.length-1;E>=1&&C>=0&&h[E]!==y[C];)C--;for(;E>=1&&C>=0;E--,C--)if(h[E]!==y[C]){if(E!==1||C!==1)do if(E--,C--,C<0||h[E]!==y[C]){var F=` +`+h[E].replace(" at new "," at ");return e.displayName&&F.includes("")&&(F=F.replace("",e.displayName)),typeof e=="function"&&Ea.set(e,F),F}while(E>=1&&C>=0);break}}}finally{Tl=!1,Wa.current=s,au(),Error.prepareStackTrace=o}var A=e?e.displayName||e.name:"",Q=A?Ai(A):"";return typeof e=="function"&&Ea.set(e,Q),Q}function Rl(e,t,i){return mo(e,!0)}function bs(e,t,i){return mo(e,!1)}function Es(e){var t=e.prototype;return!!(t&&t.isReactComponent)}function kt(e,t,i){if(e==null)return"";if(typeof e=="function")return mo(e,Es(e));if(typeof e=="string")return Ai(e);switch(e){case Ct:return Ai("Suspense");case Ot:return Ai("SuspenseList")}if(typeof e=="object")switch(e.$$typeof){case Te:return bs(e.render);case mt:return kt(e.type,t,i);case Qe:{var a=e,o=a._payload,s=a._init;try{return kt(s(o),t,i)}catch{}}}return""}function Cs(e){switch(e._debugOwner&&e._debugOwner.type,e._debugSource,e.tag){case z:return Ai(e.type);case Et:return Ai("Lazy");case ve:return Ai("Suspense");case ot:return Ai("SuspenseList");case H:case ae:case xe:return bs(e.type);case ce:return bs(e.type.render);case B:return Rl(e.type);default:return""}}function lu(e){try{var t="",i=e;do t+=Cs(i),i=i.return;while(i);return t}catch(a){return` Error generating stack: `+a.message+` -`+a.stack}}function kl(e,t,i){var a=e.displayName;if(a)return a;var o=t.displayName||t.name||"";return o!==""?i+"("+o+")":i}function xs(e){return e.displayName||"Context"}function zt(e){if(e==null)return null;if(typeof e.tag=="number"&&g("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case gi:return"Fragment";case Yr:return"Portal";case O:return"Profiler";case Ia:return"StrictMode";case Et:return"Suspense";case Ot:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Ee:var t=e;return xs(t)+".Consumer";case ue:var i=e;return xs(i._context)+".Provider";case Te:return kl(e,e.render,"ForwardRef");case mt:var a=e.displayName||null;return a!==null?a:zt(e.type)||"Memo";case Qe:{var o=e,s=o._payload,d=o._init;try{return zt(d(s))}catch{return null}}}return null}function ou(e,t,i){var a=t.displayName||t.name||"";return e.displayName||(a!==""?i+"("+a+")":i)}function ho(e){return e.displayName||"Context"}function lt(e){var t=e.tag,i=e.type;switch(t){case bt:return"Cache";case he:var a=i;return ho(a)+".Consumer";case de:var o=i;return ho(o._context)+".Provider";case Dt:return"DehydratedFragment";case ce:return ou(i,i.render,"ForwardRef");case Z:return"Fragment";case z:return i;case x:return"Portal";case ee:return"Root";case _:return"Text";case Ct:return zt(i);case me:return i===Ia?"StrictMode":"Mode";case Ze:return"Offscreen";case ze:return"Profiler";case Nt:return"Scope";case ve:return"Suspense";case ot:return"SuspenseList";case nt:return"TracingMarker";case B:case H:case Ut:case ae:case be:case xe:if(typeof i=="function")return i.displayName||i.name||null;if(typeof i=="string")return i;break}return null}var la=S.ReactDebugCurrentFrame,gn=null,Gr=!1;function Ui(){{if(gn===null)return null;var e=gn._debugOwner;if(e!==null&&typeof e<"u")return lt(e)}return null}function _l(){return gn===null?"":lu(gn)}function cn(){la.getCurrentStack=null,gn=null,Gr=!1}function wn(e){la.getCurrentStack=e===null?null:_l,gn=e,Gr=!1}function Ts(){return gn}function or(e){Gr=e}function qr(e){return""+e}function oa(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return br(e),e;default:return""}}var uu={button:!0,checkbox:!0,image:!0,hidden:!0,radio:!0,reset:!0,submit:!0};function yo(e,t){uu[t.type]||t.onChange||t.onInput||t.readOnly||t.disabled||t.value==null||g("You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`."),t.onChange||t.readOnly||t.disabled||t.checked==null||g("You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.")}function go(e){var t=e.type,i=e.nodeName;return i&&i.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Dl(e){return e._valueTracker}function ji(e){e._valueTracker=null}function Qa(e){var t="";return e&&(go(e)?t=e.checked?"true":"false":t=e.value),t}function su(e){var t=go(e)?"checked":"value",i=Object.getOwnPropertyDescriptor(e.constructor.prototype,t);br(e[t]);var a=""+e[t];if(!(e.hasOwnProperty(t)||typeof i>"u"||typeof i.get!="function"||typeof i.set!="function")){var o=i.get,s=i.set;Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(h){br(h),a=""+h,s.call(this,h)}}),Object.defineProperty(e,t,{enumerable:i.enumerable});var d={getValue:function(){return a},setValue:function(h){br(h),a=""+h},stopTracking:function(){ji(e),delete e[t]}};return d}}function Ga(e){Dl(e)||(e._valueTracker=su(e))}function cu(e){if(!e)return!1;var t=Dl(e);if(!t)return!0;var i=t.getValue(),a=Qa(e);return a!==i?(t.setValue(a),!0):!1}function Ea(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var qa=!1,fu=!1,Rs=!1,ua=!1;function du(e){var t=e.type==="checkbox"||e.type==="radio";return t?e.checked!=null:e.value!=null}function w(e,t){var i=e,a=t.checked,o=yt({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:a??i._wrapperState.initialChecked});return o}function D(e,t){yo("input",t),t.checked!==void 0&&t.defaultChecked!==void 0&&!fu&&(g("%s contains an input of type %s with both checked and defaultChecked props. Input elements must be either controlled or uncontrolled (specify either the checked prop, or the defaultChecked prop, but not both). Decide between using a controlled or uncontrolled input element and remove one of these props. More info: https://reactjs.org/link/controlled-components",Ui()||"A component",t.type),fu=!0),t.value!==void 0&&t.defaultValue!==void 0&&!qa&&(g("%s contains an input of type %s with both value and defaultValue props. Input elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both). Decide between using a controlled or uncontrolled input element and remove one of these props. More info: https://reactjs.org/link/controlled-components",Ui()||"A component",t.type),qa=!0);var i=e,a=t.defaultValue==null?"":t.defaultValue;i._wrapperState={initialChecked:t.checked!=null?t.checked:t.defaultChecked,initialValue:oa(t.value!=null?t.value:a),controlled:du(t)}}function W(e,t){var i=e,a=t.checked;a!=null&&Ba(i,"checked",a,!1)}function X(e,t){var i=e;{var a=du(t);!i._wrapperState.controlled&&a&&!ua&&(g("A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components"),ua=!0),i._wrapperState.controlled&&!a&&!Rs&&(g("A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components"),Rs=!0)}W(e,t);var o=oa(t.value),s=t.type;if(o!=null)s==="number"?(o===0&&i.value===""||i.value!=o)&&(i.value=qr(o)):i.value!==qr(o)&&(i.value=qr(o));else if(s==="submit"||s==="reset"){i.removeAttribute("value");return}t.hasOwnProperty("value")?rt(i,t.type,o):t.hasOwnProperty("defaultValue")&&rt(i,t.type,oa(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(i.defaultChecked=!!t.defaultChecked)}function ye(e,t,i){var a=e;if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var o=t.type,s=o==="submit"||o==="reset";if(s&&(t.value===void 0||t.value===null))return;var d=qr(a._wrapperState.initialValue);i||d!==a.value&&(a.value=d),a.defaultValue=d}var h=a.name;h!==""&&(a.name=""),a.defaultChecked=!a.defaultChecked,a.defaultChecked=!!a._wrapperState.initialChecked,h!==""&&(a.name=h)}function He(e,t){var i=e;X(i,t),Le(i,t)}function Le(e,t){var i=t.name;if(t.type==="radio"&&i!=null){for(var a=e;a.parentNode;)a=a.parentNode;Dr(i,"name");for(var o=a.querySelectorAll("input[name="+JSON.stringify(""+i)+'][type="radio"]'),s=0;s.")))}):t.dangerouslySetInnerHTML!=null&&(qt||(qt=!0,g("Pass a `value` prop if you set dangerouslyInnerHTML so React knows which value should be selected.")))),t.selected!=null&&!gt&&(g("Use the `defaultValue` or `value` props on instead of setting `selected` on