commit 38e12526f7922b8877c20906d7512c23191c9373 Author: wehub-resource-sync Date: Mon Jul 13 12:30:54 2026 +0800 chore: import upstream snapshot with attribution diff --git a/.cursor/rules/global.mdc b/.cursor/rules/global.mdc new file mode 100644 index 0000000..311b29d --- /dev/null +++ b/.cursor/rules/global.mdc @@ -0,0 +1,148 @@ +--- +description: +globs: +alwaysApply: true +--- +# Tianji Project Cursor Rules + +IMPORTANT: Do not operate my git to add or rm files at any time. + +## Project Overview + +**Tianji** is the project name - a proprietary monitoring and analytics platform. Always use "Tianji" (not "tianji" or other variations) when referring to the project in documentation, comments, and user-facing text. + +## Project Structure (Monorepo) + +This is a monorepo with the following structure: + +``` +tianji/ +├── apps/ # Application packages +│ ├── appstore-review/ # App store review automation +│ ├── daily-ai-trigger/ # Daily AI trigger service +│ └── mcp-server/ # MCP server implementation +├── packages/ # Shared packages +│ ├── client-sdk/ # Client SDK for external integration +│ ├── react/ # React components sdk library +│ └── react-native/ # React Native sdk library +├── src/ +│ ├── client/ # Frontend application (React) +│ │ ├── components/ # React components +│ │ │ └── ui/ # Shadcn/ui components +│ │ ├── routes/ # Application routes +│ │ ├── hooks/ # Custom React hooks +│ │ ├── store/ # State management +│ │ └── utils/ # Client utilities +│ ├── server/ # Backend application (Node.js) +│ │ ├── model/ # Database models and business logic +│ │ ├── router/ # Express routes +│ │ ├── trpc/ # tRPC API definitions +│ │ ├── middleware/ # Express middleware +│ │ └── utils/ # Server utilities +│ ├── shared/ # Shared code between client/server +│ └── tracker/ # Tracking library +├── website/ # Documentation website (Docusaurus) +├── geo/ # Geographic data +├── docker/ # Docker configurations +└── example/ # Example applications + ├── expo/ # React Native example + └── web/ # Web example +``` + +## Component Usage Rules + +### Priority Order for Components + +1. **Shadcn/ui Components (Highest Priority)** + - Located in `src/client/components/ui/` + - Import as: `import { Button } from '@/components/ui/button'` + - These are the preferred components for new development + - Modern, accessible, and customizable + - Built with Radix UI primitives and Tailwind CSS + +2. **Antd Components (Secondary)** + - Use only when Shadcn/ui doesn't provide equivalent functionality + - Import as: `import { Form, Input } from 'antd'` + - Good for complex form components and data display + +### Available Shadcn/ui Components + +Always check `src/client/components/ui/` directory first. Available components include: + +- `alert`, `alert-dialog`, `avatar`, `badge`, `button`, `calendar`, `card`, `chart` +- `checkbox`, `collapsible`, `command`, `dialog`, `drawer`, `dropdown-menu` +- `form`, `input`, `label`, `menubar`, `popover`, `progress`, `radio-group` +- `resizable`, `scroll-area`, `select`, `separator`, `sheet`, `sonner`, `spinner` +- `switch`, `table`, `tabs`, `textarea`, `tooltip` + +### Component Development Guidelines + +1. **Use existing components** - Always check if a component already exists before creating new ones +2. **Shadcn/ui first** - Prefer Shadcn/ui components over Antd when possible +3. **Consistent styling** - Follow Tailwind CSS patterns used in existing components +4. **Accessibility** - Ensure all components are accessible (Shadcn/ui components include this by default) +5. **TypeScript** - All components must be properly typed + +### Example Component Usage + +```tsx +// ✅ Preferred - Using Shadcn/ui +import { Button } from '@/components/ui/button'; +import { Dialog, DialogContent, DialogHeader } from '@/components/ui/dialog'; +import { Form } from '@/components/ui/form'; + +// ✅ Acceptable - When Shadcn/ui doesn't have equivalent +import { Form, Input } from 'antd'; + +// ❌ Avoid - Don't mix when Shadcn/ui alternative exists +import { Button } from 'antd'; // Use Shadcn/ui Button instead +``` + +## Code Style Guidelines + +### Naming Conventions + +- **Project name**: Always use "Tianji" (capitalized) +- **Components**: PascalCase (e.g., `MonitorProvider`, `PushTokenForm`) +- **Files**: kebab-case for components (e.g., `push-monitor.tsx`) +- **Functions**: camelCase (e.g., `handleSubmit`, `validateForm`) +- **Constants**: SCREAMING_SNAKE_CASE (e.g., `API_BASE_URL`) + +### Import Order + +1. React and external libraries +2. Internal utilities and hooks +3. UI components (Shadcn/ui first, then Antd) +4. Types and interfaces +5. Relative imports + +```tsx +import React from 'react'; +import { useQuery } from '@tanstack/react-query'; + +import { useCurrentWorkspaceId } from '@/store/user'; +import { trpc } from '@/api/trpc'; + +import { Button } from '@/components/ui/button'; +import { Dialog } from '@/components/ui/dialog'; +import { Form, Input } from 'antd'; + +import type { MonitorInfo } from './types'; +``` + +### Language Usage + +- **Comments**: Always in English +- **User-facing text**: Use i18n translation keys +- **Documentation**: English preferred +- **Console logs**: English for development messages + +## API and Backend Guidelines + +- Use **tRPC** for type-safe API calls +- Follow **Prisma** patterns for database operations +- Implement proper **error handling** and **validation** +- Use **zod** for schema validation +- Follow **OpenAPI** standards for public endpoints + +This is a sophisticated, production-grade application with high standards for code quality, type safety, and user experience. diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..24f04af --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,12 @@ +FROM mcr.microsoft.com/devcontainers/javascript-node:1-18-bullseye + +# [Optional] Uncomment this section to install additional OS packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends iputils-ping + +# [Optional] Uncomment if you want to install an additional version of node using nvm +ARG EXTRA_NODE_VERSION=18 +RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" + +# [Optional] Uncomment if you want to install more global node modules +# RUN su node -c "npm install -g " diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..c426b1b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,56 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose +{ + "name": "Tianji Devcontainer", + + // Update the 'dockerComposeFile' list if you have more compose files or use different names. + // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make. + "dockerComposeFile": [ + "docker-compose.yml" + ], + + // The 'service' property is the name of the service for the container that VS Code should + // use. Update this value and .devcontainer/docker-compose.yml to the real service name. + "service": "devcontainer", + + // The optional 'workspaceFolder' property is the path VS Code should open by default when + // connected. This is typically a file mount in .devcontainer/docker-compose.yml + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "containerEnv": { + "DATABASE_URL": "postgresql://tianji:tianji@postgres:5432/tianji", + "JWT_SECRET": "tianji-any-string", + "ALLOW_REGISTER": "false", + "ALLOW_OPENAPI": "true" + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [12345], + + // Uncomment the next line if you want start specific services in your Docker Compose config. + // "runServices": [], + + // Uncomment the next line if you want to keep your containers running after VS Code shuts down. + // "shutdownAction": "none", + + // Uncomment the next line to run commands after the container is created. + "postCreateCommand": "pnpm install && pnpm db:migrate:apply", + + // Configure tool-specific properties. + "customizations": { + "vscode": { + "extensions": [ + "maattdd.gitless", + "esbenp.prettier-vscode", + "njzy.stats-bar", + "bradlc.vscode-tailwindcss", + "dbaeumer.vscode-eslint" + ] + } + }, + + // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "devcontainer" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..51611e6 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,44 @@ +version: '3.5' +services: + # Update this to the name of the service you want to work with in your docker-compose.yml file + devcontainer: + # Uncomment if you want to override the service's Dockerfile to one in the .devcontainer + # folder. Note that the path of the Dockerfile and context is relative to the *primary* + # docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile" + # array). The sample below assumes your primary file is in the root of your project. + # + build: + context: . + dockerfile: ./Dockerfile + + volumes: + # Update this to wherever you want VS Code to mount the folder of your project + - ..:/workspaces:cached + + # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust. + # cap_add: + # - SYS_PTRACE + # security_opt: + # - seccomp:unconfined + + # Overrides default command so things don't shut down after the process ends. + command: /bin/sh -c "while sleep 1000; do :; done" + + # Database + postgres: + image: postgres:15.4-alpine + environment: + POSTGRES_DB: tianji + POSTGRES_USER: tianji + POSTGRES_PASSWORD: tianji + volumes: + - tianji-db-data:/var/lib/postgresql/data + restart: always + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + tianji-db-data: diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d76c5e2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +apps +example +docker +node_modules +packages/* +!packages/client-sdk +!packages/client-sdk/** +website diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..47c944b --- /dev/null +++ b/.env.example @@ -0,0 +1,20 @@ +# postgresql url +DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public" + +# Whether allow feature +ALLOW_REGISTER=false +ALLOW_OPENAPI=true + +# For analyze tianji self +WEBSITE_ID= + +# For secury +JWT_SECRET=replace-with-random-string # default is `daily string` +JWT_ISSUER= # default is `tianji.msgbyte.com` +JWT_AUDIENCE= # default is `msgbyte.com` + +# ClickHouse +CLICKHOUSE_URL= +CLICKHOUSE_USER= +CLICKHOUSE_PASSWORD= +CLICKHOUSE_DATABASE= diff --git a/.github/workflows/ci-docker.yaml b/.github/workflows/ci-docker.yaml new file mode 100644 index 0000000..675c415 --- /dev/null +++ b/.github/workflows/ci-docker.yaml @@ -0,0 +1,38 @@ +name: "Docker Build CI" + +on: + push: + branches: + - master + paths: + - "Dockerfile" + workflow_dispatch: + +jobs: + docker: + runs-on: ubuntu-latest + + steps: + - name: Docker Setup QEMU + uses: docker/setup-qemu-action@v3 + id: qemu + with: + platforms: amd64,arm64 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: moonrailgun/tianji + tags: | + type=sha + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build + uses: docker/build-push-action@v5 + with: + # context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: false + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..fe4c5cc --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,44 @@ +name: "CI" + +on: + push: + branches: + - master + paths: + - "src/**" + - "package.json" + - "pnpm-lock.yaml" + workflow_dispatch: + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v6 + - name: Install Node.js + uses: actions/setup-node@v6 + with: + node-version: 22 + - name: Install pnpm + uses: pnpm/action-setup@v6 + with: + version: 10.27.0 + run_install: false + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + - uses: actions/cache@v5 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Check Type + run: pnpm check:type + - name: Check Build + run: pnpm build diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml new file mode 100644 index 0000000..07d79f3 --- /dev/null +++ b/.github/workflows/deploy-website.yml @@ -0,0 +1,26 @@ +name: "Deployment Website" + +on: + push: + branches: + - master + paths: + - "website/**" + workflow_dispatch: + +env: + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: prj_V3iY3AcH0wIXxMHW4oDy3W3XzfnN + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install Vercel CLI + run: npm install --global vercel@latest + - name: Deploy to Vercel + run: vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }} diff --git a/.github/workflows/docker-publish-canary.yml b/.github/workflows/docker-publish-canary.yml new file mode 100644 index 0000000..df131ac --- /dev/null +++ b/.github/workflows/docker-publish-canary.yml @@ -0,0 +1,54 @@ +name: "Docker Publish Canary" + +on: + workflow_dispatch: + +jobs: + docker: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 # peter-evans/dockerhub-description need checkout README.md + - name: Docker Setup QEMU + uses: docker/setup-qemu-action@v3 + id: qemu + with: + platforms: amd64,arm64 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + moonrailgun/tianji + ghcr.io/msgbyte/tianji + tags: | + type=sha + - name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Log into ghcr.io registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push + uses: docker/build-push-action@v5 + with: + # context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: VERSION=canary-${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..3fb9bcb --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,64 @@ +name: "Docker Publish" + +on: + workflow_dispatch: + push: + tags: + - 'v*.*.*' + +jobs: + docker: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 # peter-evans/dockerhub-description need checkout README.md + - name: Docker Setup QEMU + uses: docker/setup-qemu-action@v3 + id: qemu + with: + platforms: amd64,arm64 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + moonrailgun/tianji + ghcr.io/msgbyte/tianji + tags: | + type=semver,pattern={{version}} + - name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Log into ghcr.io registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push + uses: docker/build-push-action@v5 + with: + # context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + - name: Update repo description + uses: peter-evans/dockerhub-description@v3 + continue-on-error: true + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + repository: moonrailgun/tianji + readme-filepath: ./README.md diff --git a/.github/workflows/reporter-release.yml b/.github/workflows/reporter-release.yml new file mode 100644 index 0000000..68cca24 --- /dev/null +++ b/.github/workflows/reporter-release.yml @@ -0,0 +1,50 @@ +name: Build Reporter Release + +on: + release: + types: [created] + +jobs: + build-go-binary: + runs-on: ubuntu-latest + strategy: + matrix: + goos: [linux, windows, darwin] + goarch: [amd64, arm64] + exclude: + - goarch: arm64 + goos: windows + steps: + - uses: actions/checkout@v3 + - uses: wangyoucao577/go-release-action@v1.40 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + project_path: ./reporter + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + goversion: 1.25.11 + binary_name: "tianji-reporter" + compress_assets: "OFF" + asset_name: "tianji-reporter-${{ matrix.goos }}-${{ matrix.goarch }}" + build-go-binary-static: + runs-on: ubuntu-latest + strategy: + matrix: + goos: [linux] + goarch: [amd64, arm64] + steps: + - uses: actions/checkout@v3 + - uses: wangyoucao577/go-release-action@v1.40 + env: + CGO_ENABLED: 0 + GOOS: linux + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + project_path: ./reporter + ldflags: '-extldflags "-static"' + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + goversion: 1.25.11 + binary_name: "tianji-reporter" + compress_assets: "OFF" + asset_name: "tianji-reporter-${{ matrix.goos }}-${{ matrix.goarch }}-alpine" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..61fa100 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +.env +src/client/public/tracker.js +geo +docs/superpowers/ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*storybook.log +storybook-static diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..d19a420 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +package-manager-strict=true +package-manager-strict-version=false diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..423f238 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +src/client/routeTree.gen.ts diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..7cc1994 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,12 @@ +{ + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "bracketSpacing": true, + "arrowParens": "always", + "jsxBracketSameLine": false, + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/.release-it.json b/.release-it.json new file mode 100644 index 0000000..4f59afc --- /dev/null +++ b/.release-it.json @@ -0,0 +1,50 @@ +{ + "github": { + "release": true + }, + "git": { + "commitMessage": "chore: release v${version}", + "tag": true, + "tagName": "v${version}" + }, + "npm": { + "publish": false + }, + "hooks": { + "after:bump": "echo Version Upgrade Success. checkout more in CHANGELOG" + }, + "plugins": { + "@release-it/conventional-changelog": { + "infile": "CHANGELOG.md", + "preset": { + "name": "conventionalcommits", + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "docs", + "section": "Document" + }, + { + "type": "refactor", + "section": "Others" + }, + { + "type": "perf", + "section": "Others" + }, + { + "type": "chore", + "section": "Others" + } + ] + } + } + } +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f3cebdb --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,19 @@ +# Contributor Guide + +## Development Tips +- Install Node.js 22.14.0+ and pnpm 9.7.1. +- Run `pnpm install` in the repo root to bootstrap all workspace packages. +- Use `pnpm dev` to start the server and client concurrently for local development. +- Run `pnpm build` to generate production assets. + +## Translation Files +When generating code, **do not modify** any JSON files in `src/client/public/locales`. These translations are managed separately. + +## Testing Instructions +- CI configuration is under `.github/workflows`. +- Run `pnpm check:type` and `pnpm build` to mirror CI checks. +- Execute `pnpm test` to run Vitest across packages (or `pnpm -r test` for individual packages). +- Focus on one test with `pnpm vitest run -t ""`. + +## PR instructions +- Title your PR using Angular commit style, e.g. `feat: add new feature`. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a20f1b7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4038 @@ + + +## [1.32.16](https://github.com/msgbyte/tianji/compare/v1.32.15...v1.32.16) (2026-07-01) + +### Bug Fixes + +* **website:** log session lookup failures ([9d5430d](https://github.com/msgbyte/tianji/commit/9d5430d7469836b293ec71db201e854cc8261d9c)) + +## [1.32.15](https://github.com/msgbyte/tianji/compare/v1.32.14...v1.32.15) (2026-07-01) + +### Features + +* **ai-router:** show gateway log details from router logs ([0450a5b](https://github.com/msgbyte/tianji/commit/0450a5bba83c4e6c45b393927419719253418969)) + +## [1.32.14](https://github.com/msgbyte/tianji/compare/v1.32.13...v1.32.14) (2026-06-30) + +### Bug Fixes + +* **ai-router:** handle stream error failover ([b82b3f3](https://github.com/msgbyte/tianji/commit/b82b3f3da426509bd7fb25a3128f8630e718dda5)) + +## [1.32.13](https://github.com/msgbyte/tianji/compare/v1.32.12...v1.32.13) (2026-06-30) + +### Bug Fixes + +* **website:** handle stale event sessions ([a249e79](https://github.com/msgbyte/tianji/commit/a249e79cfc2aef82143f0ff387c030bf9e8fcd35)) + +## [1.32.12](https://github.com/msgbyte/tianji/compare/v1.32.11...v1.32.12) (2026-06-29) + +### Bug Fixes + +* **prisma:** avoid json array type generation bug ([f2780f5](https://github.com/msgbyte/tianji/commit/f2780f55a597a73fda3c960121750cbac248edff)) +* **website:** guard tweet entity arrays ([5151796](https://github.com/msgbyte/tianji/commit/51517964ebba66fafbff5ff3a9df3cae2a38821c)) +* **website:** remove deleted homepage tweet ([0c50a04](https://github.com/msgbyte/tianji/commit/0c50a04c8fbb2ee9982614b2a38f41be839dca53)) +* **website:** return 404 for unknown website events ([8b9092e](https://github.com/msgbyte/tianji/commit/8b9092e7f677c361a09b54af828c7949d3e00d57)) + +## [1.32.11](https://github.com/msgbyte/tianji/compare/v1.32.10...v1.32.11) (2026-06-28) + +### Features + +* **application:** add pie chart segment details ([64ec9cb](https://github.com/msgbyte/tianji/commit/64ec9cb6bc77ccd3f0a98e5cb2bc6da7afd0dbf1)) +* **worker:** add request payload metrics and controls ([b35fe6a](https://github.com/msgbyte/tianji/commit/b35fe6a8df09f5667b2ae5b5a148e9962bb94d49)) + +### Bug Fixes + +* **ai-router:** normalize router log attempt errors ([f600d39](https://github.com/msgbyte/tianji/commit/f600d39204b608abbda32e00d08c688b674f714f)) +* **worker:** show loading before hourly stats resolve ([6b2abf9](https://github.com/msgbyte/tianji/commit/6b2abf9626c53670e5e34d1510abca09dd64ff7b)) + +## [1.32.10](https://github.com/msgbyte/tianji/compare/v1.32.9...v1.32.10) (2026-06-25) + +### Bug Fixes + +* **worker:** avoid embedding payload in vm source ([af68fe0](https://github.com/msgbyte/tianji/commit/af68fe0aea1d094f7c4ae3b76526170e6170a97c)) + +## [1.32.9](https://github.com/msgbyte/tianji/compare/v1.32.8...v1.32.9) (2026-06-25) + +### Features + +* **ai-router:** fail over empty content responses ([bb98bef](https://github.com/msgbyte/tianji/commit/bb98befb2a2bf8a7c05c961903b3a92054969109)) + +### Bug Fixes + +* **ai-router:** avoid replaying empty content failures ([df685cd](https://github.com/msgbyte/tianji/commit/df685cd4edeb398e70c438c6ae3436c93c0fbd1c)) +* **ai-router:** ignore empty tool calls ([9f76b45](https://github.com/msgbyte/tianji/commit/9f76b45d85be93bfe440ac772cecd7575dc00db5)) + +### Document + +* **ai-router:** explain empty content failover ([0158467](https://github.com/msgbyte/tianji/commit/01584679e4284cfcbde834f280a0431bf9fca8f5)) + +## [1.32.8](https://github.com/msgbyte/tianji/compare/v1.32.7...v1.32.8) (2026-06-25) + +### Features + +* **ai-router:** add empty content route option ([b8a69fe](https://github.com/msgbyte/tianji/commit/b8a69fe54459077538a3bb97c9c649662148b2d7)) +* **ai-router:** persist empty content failover setting ([f5d8321](https://github.com/msgbyte/tianji/commit/f5d8321a1687e97a92dfcf45c5f4539fc72d05ad)) + +### Bug Fixes + +* **server:** release runtime resources ([0ae65b3](https://github.com/msgbyte/tianji/commit/0ae65b39a603b98b240160f9baf5f034473d89b4)) + +### Document + +* **ai-router:** add empty content failover design ([8b45dd9](https://github.com/msgbyte/tianji/commit/8b45dd9005f495803d05d750fb44fa82d5cc54b3)) + +## [1.32.7](https://github.com/msgbyte/tianji/compare/v1.32.6...v1.32.7) (2026-06-23) + +### Bug Fixes + +* **billing:** delay free tier quota tip ([de2d097](https://github.com/msgbyte/tianji/commit/de2d0974036af7876b83122427341641358752fb)) +* **docker:** downgrade node alpine base for hub scan ([ba5230c](https://github.com/msgbyte/tianji/commit/ba5230c21ab105cedee781255531c650e106eab3)) + +## [1.32.6](https://github.com/msgbyte/tianji/compare/v1.32.5...v1.32.6) (2026-06-21) + +### Bug Fixes + +* **ai-gateway:** flush stream keepalive headers ([49ec99b](https://github.com/msgbyte/tianji/commit/49ec99be9833176eb9b9911761db88a2249e9d94)) + +## [1.32.5](https://github.com/msgbyte/tianji/compare/v1.32.4...v1.32.5) (2026-06-21) + +### Bug Fixes + +* **ai-router:** add stream keepalive pings ([8f52442](https://github.com/msgbyte/tianji/commit/8f52442d93d153c0cd303f6a70034d93304b0e62)) + +## [1.32.4](https://github.com/msgbyte/tianji/compare/v1.32.3...v1.32.4) (2026-06-20) + +### Features + +* **ai-router:** add logs refresh button ([089579c](https://github.com/msgbyte/tianji/commit/089579cc13ca326deace07f1f72beda2d65c6ed4)) + +### Bug Fixes + +* **ai-gateway:** add stream keepalive pings ([907dd17](https://github.com/msgbyte/tianji/commit/907dd17f82aacdcbca4bb510e53425efceed09ff)) +* **server:** include execa in production deps ([c4960b6](https://github.com/msgbyte/tianji/commit/c4960b60a4e16dabe6709cd7823f99f2a7047cf5)) + +## [1.32.3](https://github.com/msgbyte/tianji/compare/v1.32.2...v1.32.3) (2026-06-20) + +### Features + +* **ai-router:** add models endpoint aggregation ([0c6f5b8](https://github.com/msgbyte/tianji/commit/0c6f5b87878901acee601c3b7a2c6a0915baa79f)) + +### Bug Fixes + +* **build:** remediate image cves and dev optimizer ([4dc5ea2](https://github.com/msgbyte/tianji/commit/4dc5ea2b8dd20ad62d2e15b1ab13a14114e9ea73)) +* **ci:** align pnpm version ([d9d3621](https://github.com/msgbyte/tianji/commit/d9d362100cf9d3ab7fa11a29225b9631f5e808da)) + +## [1.32.2](https://github.com/msgbyte/tianji/compare/v1.32.1...v1.32.2) (2026-06-15) + +### Bug Fixes + +* **vm:** upgrade and harden vm2 sandbox fallback ([0534445](https://github.com/msgbyte/tianji/commit/0534445a68d000ec40b6623597687bdf543ee838)) + +### Others + +* **ai:** update example model names ([ac0d772](https://github.com/msgbyte/tianji/commit/ac0d772907305d876f8bea0f5d8569ec20d0ba1f)) + +## [1.32.1](https://github.com/msgbyte/tianji/compare/v1.32.0...v1.32.1) (2026-06-15) + +### Bug Fixes + +* **ai-router:** proxy dev runtime routes and log invalid requests ([33a18a4](https://github.com/msgbyte/tianji/commit/33a18a4238f271425810b31c3208d688f6f75aef)) +* **survey:** prefill field type in edit form ([1689941](https://github.com/msgbyte/tianji/commit/1689941c91157231860474db2d00949c79cf5012)) + +### Others + +* remove unused index ([de8ab85](https://github.com/msgbyte/tianji/commit/de8ab85c0440b367936153c582e50e9a7b82bc66)) + +## [1.32.0](https://github.com/msgbyte/tianji/compare/v1.31.29...v1.32.0) (2026-06-14) + +### Features + +* add ai route feature ([446263d](https://github.com/msgbyte/tianji/commit/446263d95e5d02d0db56b9bc62992788c9d73dc5)) + +### Bug Fixes + +* **ai-gateway:** add cache write pricing support ([cd7dac9](https://github.com/msgbyte/tianji/commit/cd7dac950145516330927b0f22fde760304c03ff)) + +## [1.31.29](https://github.com/msgbyte/tianji/compare/v1.31.28...v1.31.29) (2026-06-10) + +### Features + +* **ai-gateway:** add custom model strategy pricing editor ([ef1c5f1](https://github.com/msgbyte/tianji/commit/ef1c5f1454ddfb71f365cf0afe13283f9655e47e)) + +### Bug Fixes + +* **security:** harden templates and insight sql ([132ff67](https://github.com/msgbyte/tianji/commit/132ff67907df897d222829ea8ad1a018ff74df48)) + +### Others + +* update model price list ([abf21c3](https://github.com/msgbyte/tianji/commit/abf21c326654d33f56605a1dc67121e7e7e06f60)) +* update translation ([b7a6efd](https://github.com/msgbyte/tianji/commit/b7a6efd08ebfaa95a0be2d369cee902d496a07c4)) + +## [1.31.28](https://github.com/msgbyte/tianji/compare/v1.31.27...v1.31.28) (2026-06-02) + +### Features + +* **aiGateway:** add openai responses support ([7dd7549](https://github.com/msgbyte/tianji/commit/7dd7549ce86822b4d4f0d8e0cf6ad85ad3128e8f)) +* **aigateway:** add tpot monitoring ([84ef6bc](https://github.com/msgbyte/tianji/commit/84ef6bcd989546d3dd6f550b9a9c6379f7a55d85)) + +### Bug Fixes + +* **aiGateway:** add scrolling to edit forms ([1e9e029](https://github.com/msgbyte/tianji/commit/1e9e029deeee66c733eb2172f11a8e359249d017)) + +## [1.31.27](https://github.com/msgbyte/tianji/compare/v1.31.26...v1.31.27) (2026-05-30) + +### Features + +* add models list API handlers for OpenAI and Anthropic ([c0fe7c5](https://github.com/msgbyte/tianji/commit/c0fe7c5a3f44ac940ffb0cca44300dc1fdf8e0f0)) +* implement auto-disable functionality for continuously down monitors ([a20ac02](https://github.com/msgbyte/tianji/commit/a20ac024f5400d66a49a61c265ee274eaa1991aa)) + +### Others + +* change skills position and add skill document ([6174760](https://github.com/msgbyte/tianji/commit/617476040a9b35221947b53c1bf6edb330d38674)) +* update deploy website action ([0e0358a](https://github.com/msgbyte/tianji/commit/0e0358a1f6ddefeaeec1f73d68d046632b9c4347)) +* update deploy website config ([374cb50](https://github.com/msgbyte/tianji/commit/374cb50e4a026185483779056715a7ee13d17f86)) +* update website ([90487ce](https://github.com/msgbyte/tianji/commit/90487cec5c5c7fabf86f6f542d724c57a593dab1)) + +## [1.31.26](https://github.com/msgbyte/tianji/compare/v1.31.25...v1.31.26) (2026-05-26) + +### Features + +* enhance SecretInput component with clear button and interaction handling ([4993f4c](https://github.com/msgbyte/tianji/commit/4993f4c20aa047f2848934225258020ba0bc04ff)) + +### Others + +* update deploy website config ([73799d1](https://github.com/msgbyte/tianji/commit/73799d1d5dc2425532287af98ddbfb2ffb61fbff)) +* update Tianji URLs to point to the new domain tianji.dev ([db04ca0](https://github.com/msgbyte/tianji/commit/db04ca00fbc6be82028100c79c484c01904f3295)) + +## [1.31.25](https://github.com/msgbyte/tianji/compare/v1.31.24...v1.31.25) (2026-05-07) + +### Others + +* update ErrorRateChart to handle missing data and improve rate calculation logic ([882894c](https://github.com/msgbyte/tianji/commit/882894cd855fa522b679a588a7a43d5509b9695e)) + +## [1.31.24](https://github.com/msgbyte/tianji/compare/v1.31.23...v1.31.24) (2026-05-06) + +### Features + +* implement ErrorRateChart in AIGatewayAnalytics to visualize error rates over time ([9d45e62](https://github.com/msgbyte/tianji/commit/9d45e62eeae33df848894fcbe521fb29e74acf0b)) + +### Bug Fixes + +* enhance error message in verifyUserApiKey to include partial input for better debugging ([f66ba76](https://github.com/msgbyte/tianji/commit/f66ba761fb4c5f4db1d4207c60ad3a9da3e86bf5)) + +### Others + +* modify DialogContent to support dynamic portal container selection ([6658ed1](https://github.com/msgbyte/tianji/commit/6658ed1d49e7f3130117089a268c384307dd5de4)) +* update SheetContent component to support dynamic portal container selection ([fa250d7](https://github.com/msgbyte/tianji/commit/fa250d70c06a2312023e2149f9be2b6242b99622)) + +## [1.31.23](https://github.com/msgbyte/tianji/compare/v1.31.22...v1.31.23) (2026-04-28) + +### Features + +* add provider analytics to AIGatewayAnalytics and log model provider in AIGatewayLogs ([c01435d](https://github.com/msgbyte/tianji/commit/c01435d70ea71b8cfb8110de59a98c2227baf27b)) + +## [1.31.22](https://github.com/msgbyte/tianji/compare/v1.31.21...v1.31.22) (2026-04-19) + +### Features + +* add rollback functionality to WorkerRevisionsSection with confirmation dialog and update TRPC router for revision rollback ([ddc5e0a](https://github.com/msgbyte/tianji/commit/ddc5e0a87aaa36c12cfae1e6dcde6631b615c5fe)) + +## [1.31.21](https://github.com/msgbyte/tianji/compare/v1.31.20...v1.31.21) (2026-04-15) + +### Features + +* add WorkerStatsSection and WorkerHourlyCharts components for enhanced worker performance visualization ([7e3e6b9](https://github.com/msgbyte/tianji/commit/7e3e6b9a55b73e113b68a9233c4c59090bc68674)) +* implement SurveyResultCount component to display survey response counts and update survey list rendering ([33a3c29](https://github.com/msgbyte/tianji/commit/33a3c296f79362f830089712db9534bd72ea6212)) + +### Document + +* Add one-click "Add to Kiro" install badge ([#262](https://github.com/msgbyte/tianji/issues/262)) ([cecee1c](https://github.com/msgbyte/tianji/commit/cecee1cd39c4c9cc92ec8e356610db5e968c16ea)) + +## [1.31.20](https://github.com/msgbyte/tianji/compare/v1.31.19...v1.31.20) (2026-03-27) + +### Bug Fixes + +* fix a bug which calc aigateway usage incorrect ([2d5d979](https://github.com/msgbyte/tianji/commit/2d5d97946fadc63f3a68ac61c4597c021255c0ab)) +* update hosted service URL from app-tianji.msgbyte.com to app.tianji.dev across multiple documentation files ([35b74cd](https://github.com/msgbyte/tianji/commit/35b74cdd2b69a97c0057324e56e892c23f6c55a1)) + +## [1.31.19](https://github.com/msgbyte/tianji/compare/v1.31.18...v1.31.19) (2026-03-26) + +### Others + +* rename cache input token to cache read token and add cache write token for improved clarity and tracking ([7cd02a4](https://github.com/msgbyte/tianji/commit/7cd02a4061fef4479773effaba43aef1f06dd384)) +* replace Card component with CodeBlock for improved JSON data rendering in AIGatewayLogTable ([10efc96](https://github.com/msgbyte/tianji/commit/10efc966df762b054a76b0d537344857b5fcdeb7)) + +## [1.31.18](https://github.com/msgbyte/tianji/compare/v1.31.17...v1.31.18) (2026-03-24) + +### Features + +* add cache input token tracking in AIGateway ([eafbf3d](https://github.com/msgbyte/tianji/commit/eafbf3d5ec03e83cea1e582b644ad8cda47e4a24)) + +### Bug Fixes + +* add refresh functionality to date range hooks in AIGateway components for improved data updates ([8816c8e](https://github.com/msgbyte/tianji/commit/8816c8e956f52ed7c4465c13382d31723741faf8)) +* update token initialization in AI Gateway handlers to default to zero for improved handling ([87e5115](https://github.com/msgbyte/tianji/commit/87e5115c65468e61dde1fd127e14ce92ecec5eba)) + +## [1.31.17](https://github.com/msgbyte/tianji/compare/v1.31.16...v1.31.17) (2026-03-16) + +### Bug Fixes + +* increase model pricing query limit from 10 to 50 for improved data retrieval ([e371ca2](https://github.com/msgbyte/tianji/commit/e371ca247c62791c8da410ad2b44efc664c0b896)) +* update response cost handling in Anthropic API integration for accurate pricing calculations ([41be2ad](https://github.com/msgbyte/tianji/commit/41be2ad22cc834cb07888dfb88f4afb70df621f0)) + +## [1.31.16](https://github.com/msgbyte/tianji/compare/v1.31.15...v1.31.16) (2026-03-15) + +### Features + +* add daily clearing functions for worker execution data and payloads ([d76e782](https://github.com/msgbyte/tianji/commit/d76e782454b3b5699a5235831f607dc2a67843a1)) +* implement Anthropic API integration with message handling and code generation examples ([c4c10b2](https://github.com/msgbyte/tianji/commit/c4c10b2e5b58f86411ece8acb2f36dc17a8dca66)) + +### Others + +* streamline daily task execution in cronjob with improved error handling ([dda8669](https://github.com/msgbyte/tianji/commit/dda86695a0daec37cfddefcf8201fde21e2e3f7a)) + +## [1.31.15](https://github.com/msgbyte/tianji/compare/v1.31.14...v1.31.15) (2026-03-13) + +### Features + +* add {{url}} variable for http monitor [#190](https://github.com/msgbyte/tianji/issues/190) ([eafbd82](https://github.com/msgbyte/tianji/commit/eafbd82fa96e3d440dc903e691e7546475ee1cc7)) +* add daily log and payload clearing functions for AI Gateway ([a75119a](https://github.com/msgbyte/tianji/commit/a75119a47a78a777e92913029a596b5115616279)) +* add user analytics and logging enhancements to AI Gateway components ([8afa79a](https://github.com/msgbyte/tianji/commit/8afa79a750f11d1cced02895eaee1c5a37bf33ba)) +* enhance logging in cronjob functions and initialize system info logging ([bacdfc0](https://github.com/msgbyte/tianji/commit/bacdfc008475c013d4ee813802f5528e3ce31a75)) + +## [1.31.14](https://github.com/msgbyte/tianji/compare/v1.31.13...v1.31.14) (2026-03-12) + +### Features + +* add some audit log in important logic ([91c562b](https://github.com/msgbyte/tianji/commit/91c562bdd4937369cdee12c5ea9cd0248e7c902c)) +* add Tianji analytics skill with OpenAPI integration and endpoint documentation ([b3ba444](https://github.com/msgbyte/tianji/commit/b3ba444bf92f9b4d17239f566de44d964339a2fc)) +* implement session error tracking and handling in website session management ([0006c34](https://github.com/msgbyte/tianji/commit/0006c347e2501cb91db22a4dcde1ca7d1473b418)) +* update pricing logic to utilize response cost from API and improve handling of custom model prices ([3811467](https://github.com/msgbyte/tianji/commit/38114678f1e2d2ec71fb8741cade7d29b204b3f2)) + +### Document + +* update track-script documentation with new script attributes and examples ([8093993](https://github.com/msgbyte/tianji/commit/80939932a6c6d41cfd9c4588762746c8cbb005c9)) + +### Others + +* add Zod validation schemas for LLM model data fetching ([418a2ae](https://github.com/msgbyte/tianji/commit/418a2ae788b9f9dee013b18622c85217a77e151f)) +* simply website overview component with card layout and event tracking ([67d336e](https://github.com/msgbyte/tianji/commit/67d336efe1983929abb6b697490e36a188a3e0a8)) +* update model price ([2896db8](https://github.com/msgbyte/tianji/commit/2896db85c991b469f1ec4ed04510249783a8139c)) +* update model pricing ([cebfafd](https://github.com/msgbyte/tianji/commit/cebfafd63fb38275a765d604407bc693691af3c0)) + +## [1.31.13](https://github.com/msgbyte/tianji/compare/v1.31.12...v1.31.13) (2026-02-13) + +### Features + +* add graceful shutdown handling and batch writer flushing on termination ([1e301a0](https://github.com/msgbyte/tianji/commit/1e301a07b593c85a0a098dc22e2e8d0f009e5bf2)) +* enhance saveWebsiteEvent function to utilize batch writing for improved performance ([98aadb9](https://github.com/msgbyte/tianji/commit/98aadb95d2606382e4f4853cbf35a8ad3cc4bc2c)) + +## [1.31.12](https://github.com/msgbyte/tianji/compare/v1.31.11...v1.31.12) (2026-02-12) + +### Features + +* implement batch writer for efficient database operations in worker execution ([ef0382f](https://github.com/msgbyte/tianji/commit/ef0382f3a7d23314eace0d8464aa995a9e686a3d)) + +## [1.31.11](https://github.com/msgbyte/tianji/compare/v1.31.10...v1.31.11) (2026-02-12) + +### Features + +* add save functionality with keyboard shortcut in HtmlEditor ([c7f1a11](https://github.com/msgbyte/tianji/commit/c7f1a11611fc9e1393480a39e2c938dba8716674)) +* implement in-memory caching layer for buildQueryWithCache function ([8437170](https://github.com/msgbyte/tianji/commit/84371709b89b2601a37054a46b3b9d92f46e2fa2)) +* implement keyboard shortcut for saving in editor ([5be539b](https://github.com/msgbyte/tianji/commit/5be539ba23f091fa39bd002d053f13e20376c691)) + +### Others + +* migrate mod + k to useHotkeys to reduce usage ([bf7aab1](https://github.com/msgbyte/tianji/commit/bf7aab119a0e8c6c3aa131c8efa92bf28b0b0a64)) + +## [1.31.10](https://github.com/msgbyte/tianji/compare/v1.31.9...v1.31.10) (2026-02-04) + +### Features + +* add static page router for seo and caching mechanism ([52185e9](https://github.com/msgbyte/tianji/commit/52185e9da89c09fd0aac84c4391508f0d63ab086)) +* enhance custom domain handling with static and status page types ([10fe22d](https://github.com/msgbyte/tianji/commit/10fe22df71e0c55c58c7f29240de0b875fc90ce9)) + +## [1.31.9](https://github.com/msgbyte/tianji/compare/v1.31.8...v1.31.9) (2026-01-31) + +### Others + +* add title for more customize ([965b3d9](https://github.com/msgbyte/tianji/commit/965b3d99d8170ad7d0c731ab1a930e8381bee17c)) + +## [1.31.8](https://github.com/msgbyte/tianji/compare/v1.31.7...v1.31.8) (2026-01-19) + +### Features + +* add url as new field type of survey ([c8a6efe](https://github.com/msgbyte/tianji/commit/c8a6efe1982e7ed9a2c323abd7e515dbc593fa1b)) +* add wrap for survey event table to display long text ([1ea8d41](https://github.com/msgbyte/tianji/commit/1ea8d419ca38ab49cb800285e75deda519ac2608)) + +### Others + +* update model price ([f7daffa](https://github.com/msgbyte/tianji/commit/f7daffa3161c9db271b437516238dcf8087db04f)) +* update translation ([21d929a](https://github.com/msgbyte/tianji/commit/21d929a5d838b6b726e86c7ba57b4ae710c39e21)) + +## [1.31.7](https://github.com/msgbyte/tianji/compare/v1.31.6...v1.31.7) (2026-01-13) + +### Features + +* add download feature ([b855872](https://github.com/msgbyte/tianji/commit/b8558723d51364b8af2d4627c6d785fbe53039fc)) + +## [1.31.6](https://github.com/msgbyte/tianji/compare/v1.31.5...v1.31.6) (2026-01-12) + +### Features + +* add ai translation and ai category in survey event table ([6d28506](https://github.com/msgbyte/tianji/commit/6d28506042ecd46e59c9778304bc4258c7d718d1)) +* add DataTableColumnSelector for dynamic column visibility in SurveyEventTable ([0ba1299](https://github.com/msgbyte/tianji/commit/0ba12998bc0cc37ea85ff439b38e2e3b43ba3bd9)) +* add image render to survey event table ([45f4772](https://github.com/msgbyte/tianji/commit/45f477288d333b69c279c8e0671dfefc8db46917)) +* enhance DataTableColumnSelector with drag-and-drop functionality and local storage for column visibility and order ([6aa327b](https://github.com/msgbyte/tianji/commit/6aa327b87f95e449ed1baacc686baf96816d5442)) + +### Bug Fixes + +* correct date handling and toggle logic in insights events page ([4fd93ad](https://github.com/msgbyte/tianji/commit/4fd93ad108099b98c90bf08375fd6184e6ecec02)) +* fix layout and resize issue for VirtualizedInfiniteDataTable ([75524ae](https://github.com/msgbyte/tianji/commit/75524aee0595fe2a3d84c60e3c33ed573acfce13)) +* update tracker import path to use core module from tianji-client-sdk ([540e161](https://github.com/msgbyte/tianji/commit/540e161668ff2b7fb39bb5f7205778ab2069c4bc)) + +### Others + +* improve style of survey event table ([74543a3](https://github.com/msgbyte/tianji/commit/74543a3590ce599d2361c6c3415e72350a723c59)) + +## [1.31.5](https://github.com/msgbyte/tianji/compare/v1.31.4...v1.31.5) (2026-01-11) + +### Features + +* add event batch feature for website inject tracker ([1f58af3](https://github.com/msgbyte/tianji/commit/1f58af3a4cebdf92405680f1839960cf44714203)) +* update health check to conditionally verify database and cache connectivity based on query parameters ([34c3bb1](https://github.com/msgbyte/tianji/commit/34c3bb17f068fbc482649b9be77b9e54705fba8b)) + +## [1.31.4](https://github.com/msgbyte/tianji/compare/v1.31.3...v1.31.4) (2026-01-08) + +### Features + +* add Prometheus counter for monitor execution tracking ([946f280](https://github.com/msgbyte/tianji/commit/946f28039652030d3d49711e190fcdb251cc6241)) +* add query to improve query performance ([807f223](https://github.com/msgbyte/tianji/commit/807f223cbff8f646e13bbd6ffb2a63f8de89f4d2)) + +### Others + +* change create to upsert which maybe can resolve issue of competition ([fd401de](https://github.com/msgbyte/tianji/commit/fd401de51ace71a3a6473300d61971dc58308009)) +* update cache query identifiers for consistency across models ([d480edb](https://github.com/msgbyte/tianji/commit/d480edb35f02541fe7d0335d59fcc787b2d1f604)) +* update translation ([d4ed7a3](https://github.com/msgbyte/tianji/commit/d4ed7a32ad7b92ef758f63476fe1968b41f2d083)) + +## [1.31.3](https://github.com/msgbyte/tianji/compare/v1.31.2...v1.31.3) (2026-01-08) + +### Features + +* add country and OS distribution charts for application sessions ([47165b5](https://github.com/msgbyte/tianji/commit/47165b537d2f75171bb5e3f7b0b8cf0a2b525310)) +* add survey event fetch ([da931f4](https://github.com/msgbyte/tianji/commit/da931f427b3a7082ade554e1557b3733a0682ffa)) +* add version stats feature ([04070f7](https://github.com/msgbyte/tianji/commit/04070f7c118606c5484fd23fa49dfa70439f3fca)) +* enhance SurveyEventTable with virtualized data fetching and loading state management ([6f756c5](https://github.com/msgbyte/tianji/commit/6f756c53c5e530eeddd286078de5986ae7eeaadc)) + +### Bug Fixes + +* handle uncaught exceptions and unhandled rejections in server initialization ([6c0dbea](https://github.com/msgbyte/tianji/commit/6c0dbeab26b11cc6e6e6ab49118212ccb81ccd40)) + +### Others + +* add builtin session data key language ([97e0861](https://github.com/msgbyte/tianji/commit/97e0861c3d144aade757562d014a13b318791d0e)) +* update layout for application overview cards in the application details page ([0a39068](https://github.com/msgbyte/tianji/commit/0a39068744620648d8b9e1e35b27da8beed57ff5)) + +## [1.31.2](https://github.com/msgbyte/tianji/compare/v1.31.1...v1.31.2) (2026-01-05) + +### Others + +* add get location cache for improve usage for get ip ([ed27233](https://github.com/msgbyte/tianji/commit/ed27233ad98a6c8402d5357e6e3b26b0c4bc3274)) +* add survey counter for prometheus ([b1012c5](https://github.com/msgbyte/tianji/commit/b1012c511bfde9fe10aa993b16f4a38257fd6144)) +* add updatedAt in application session which can track update time of builtin info update ([ba9ba06](https://github.com/msgbyte/tianji/commit/ba9ba06fed25769f20dfcbdafcdacd5911bef5b1)) +* improve html render and update tabs panel style ([021530e](https://github.com/msgbyte/tianji/commit/021530e1f578c911f82fb512cb0a7341a8adf776)) +* improve page editor toolbar style and update translation ([16955ee](https://github.com/msgbyte/tianji/commit/16955eea4f5ba93e9b339e228f34a7092c8ae695)) +* record builtin info when save application session data ([8fad875](https://github.com/msgbyte/tianji/commit/8fad875309e5a4bcab1068f5b0e489e076364242)) + +## [1.31.1](https://github.com/msgbyte/tianji/compare/v1.31.0...v1.31.1) (2025-12-29) + +### Features + +* add HTMLRender to unify render way of static page ([a08b852](https://github.com/msgbyte/tianji/commit/a08b85241517ab610907931172d8fb937923f0d8)) +* add tianji theme inject to improve style display in in tianji's preview ([dcfbcba](https://github.com/msgbyte/tianji/commit/dcfbcba338ba987e43aa97481c432336e16e1510)) +* static page editor add save feature ([5bc2a1a](https://github.com/msgbyte/tianji/commit/5bc2a1a3984a4d7d32cd20235770b0320f7217f7)) + +### Others + +* dump google-play-scraper version ([f848eef](https://github.com/msgbyte/tianji/commit/f848eef2ba6deba50be43cad59bba82851cd5243)) + +## [1.31.0](https://github.com/msgbyte/tianji/compare/v1.30.25...v1.31.0) (2025-12-28) + +### Features + +* add html editor ([d222023](https://github.com/msgbyte/tianji/commit/d222023d28dd3ca19b8dd5d145d5252394960818)) +* add html editor for vibe coding prepare ([6dd7c7f](https://github.com/msgbyte/tianji/commit/6dd7c7f763c5b2442a6e2d521f3008cee4dd8653)) +* add new model page ([0fc6e03](https://github.com/msgbyte/tianji/commit/0fc6e0355017d38fff20be1af51606b72af4c9c4)) +* implement AI chat panel for HTML editor with AI tools for generating and modifying HTML ([320c094](https://github.com/msgbyte/tianji/commit/320c094ff1007ef1338998049c6e1ecea33003e7)) +* **routes:** add custom domain management for page routes and change page route ([657e501](https://github.com/msgbyte/tianji/commit/657e5015be004087e1b37d2143f9b5a6c5f8d207)) +* **routes:** add dynamic page routing and enhance status page editing with type selection ([594fa24](https://github.com/msgbyte/tianji/commit/594fa241ae0db2b0a17f95bb60fe79b87caa8947)) +* update streamdown to version 1.6.10 and enhance response component with theme support ([847095b](https://github.com/msgbyte/tianji/commit/847095b6ef944ee72ecd40b9263a594f3be76b38)) + +### Bug Fixes + +* fix a style issue for isCollapsed style with FreeTierTip ([5608a6f](https://github.com/msgbyte/tianji/commit/5608a6f69d0ca8ad402e8c8b9fdea94779b50f53)) + +### Others + +* migrate page file from old path to new path ([36099f1](https://github.com/msgbyte/tianji/commit/36099f13711daf3b294d250a610d9bee5ba2b719)) +* resolve type issue for ci ([62c62a4](https://github.com/msgbyte/tianji/commit/62c62a48fab0a95197d2e178679b90d2244cea4c)) +* update OpenAPI schema path and relocate JSON file to static directory ([0ecdd77](https://github.com/msgbyte/tianji/commit/0ecdd7759cb4ac63963fb37bf0535497fe4d63aa)) + +## [1.30.25](https://github.com/msgbyte/tianji/compare/v1.30.24...v1.30.25) (2025-12-22) + +### Features + +* **metrics:** add AI Gateway request counter to Prometheus metrics ([845f07d](https://github.com/msgbyte/tianji/commit/845f07df1c891cd0a6317080b050f2033a6a9a8d)) +* **metrics:** add Prometheus event counters for applications and websites ([8a14bf9](https://github.com/msgbyte/tianji/commit/8a14bf9372b4b85872f4671fb911ffaf1e240107)) +* **metrics:** add Prometheus metrics for worker execution tracking ([88ef452](https://github.com/msgbyte/tianji/commit/88ef45210a7fb14a1c0563e00fd2f7bf231f672e)) +* **worker:** enhance execWorker to include unique ID and improve logging for execution records ([d576d59](https://github.com/msgbyte/tianji/commit/d576d59542987bd52ef137d9acd7c4dd690cfed9)) + +## [1.30.24](https://github.com/msgbyte/tianji/compare/v1.30.23...v1.30.24) (2025-12-21) + +### Features + +* **sdk:** implement batch request management for application and website tracking ([dce22c2](https://github.com/msgbyte/tianji/commit/dce22c27b70ab454bfe22d8634fa1b00fb48faff)) + +### Others + +* allow custom prometheus metrics path with env ([74a5b42](https://github.com/msgbyte/tianji/commit/74a5b42724e310b534da2284731f012c4d2f39d5)) +* dump client sdk version to v1.5.0 ([269d108](https://github.com/msgbyte/tianji/commit/269d108aa9a95d9f7f3a58001eafd243f2d9815e)) +* dump react native sdk version to v1.1.0 ([ae45e2a](https://github.com/msgbyte/tianji/commit/ae45e2afa823609e07dc50e3027c9ada9c6c36b2)) +* remove migrate script for docker start ([c35671e](https://github.com/msgbyte/tianji/commit/c35671ec6442de33d407e51479217ec8553b7c20)) + +## [1.30.23](https://github.com/msgbyte/tianji/compare/v1.30.22...v1.30.23) (2025-12-18) + +### Features + +* add es-ES language support ([5a2d52e](https://github.com/msgbyte/tianji/commit/5a2d52eac3d3428e3d4d81838a418899ac980b5d)) + +### Others + +* update translation ([5f25c03](https://github.com/msgbyte/tianji/commit/5f25c03973ad5b71f4cbaf8726edf5c5e6e5ac7a)) +* update translation ([ad379df](https://github.com/msgbyte/tianji/commit/ad379df7ea839739e226fcdbaa6049eb454f0b82)) + +## [1.30.22](https://github.com/msgbyte/tianji/compare/v1.30.21...v1.30.22) (2025-12-11) + +### Others + +* remove unused index ([1497f05](https://github.com/msgbyte/tianji/commit/1497f05a3f83c8884d59b7ab5dceefe0568e2d3b)) + +## [1.30.21](https://github.com/msgbyte/tianji/compare/v1.30.20...v1.30.21) (2025-12-11) + +### Features + +* add description field to survey model and update forms for input and display ([4452a5a](https://github.com/msgbyte/tianji/commit/4452a5af086b50cd10879a9699386556cb0f5c0e)) +* add hidden field which not display in survey public page ([23423cb](https://github.com/msgbyte/tianji/commit/23423cb1653f0879f5c286aff6e425d00d092029)) + +### Document + +* update tianji social card ([74a9b9c](https://github.com/msgbyte/tianji/commit/74a9b9cbe4a874a499d82c1267df3b62162ec159)) + +## [1.30.20](https://github.com/msgbyte/tianji/compare/v1.30.19...v1.30.20) (2025-12-04) + +### Bug Fixes + +* update connection issue for new connection logic which will parse driver ([ae4ae90](https://github.com/msgbyte/tianji/commit/ae4ae90f7e629474eda894a33e5006ed5adaeeb8)) + +## [1.30.19](https://github.com/msgbyte/tianji/compare/v1.30.18...v1.30.19) (2025-12-04) + +### Features + +* add pg query support for sql query ([bf832ee](https://github.com/msgbyte/tianji/commit/bf832eea4bda9cfcd152b00418a44430581a8065)) +* add session count for event stats ([6a31851](https://github.com/msgbyte/tianji/commit/6a31851b8b1b16104cd7162863b1f36eac5d1c6b)) +* update ApplicationStatsChart to use new session count fields ([093aafd](https://github.com/msgbyte/tianji/commit/093aafdcd3fff0adc1f7c36b51726cc894652ad7)) + +### Others + +* improve query ([8487675](https://github.com/msgbyte/tianji/commit/84876751f526daa339beed14bb7206f9f1651548)) +* improve validate sql logic to make sure its can be work in many case ([880d80d](https://github.com/msgbyte/tianji/commit/880d80d68527cd76358a78da5ec62463535838ed)) + +## [1.30.18](https://github.com/msgbyte/tianji/compare/v1.30.17...v1.30.18) (2025-12-01) + +### Others + +* fix email not match issue ([5bd2e06](https://github.com/msgbyte/tianji/commit/5bd2e06ac7ebda8c6bed685faa4732f3937bf8c6)) + +## [1.30.17](https://github.com/msgbyte/tianji/compare/v1.30.16...v1.30.17) (2025-11-30) + +### Features + +* add secret input for secret key sense ([b0677b8](https://github.com/msgbyte/tianji/commit/b0677b89a8794a6fbbb797036afb10dff4c33717)) + +### Others + +* change style of quota setting to improve style of ai gateway ([06b0091](https://github.com/msgbyte/tianji/commit/06b00916b5570e93843c001a7398b3fe97be7aee)) + +## [1.30.16](https://github.com/msgbyte/tianji/compare/v1.30.15...v1.30.16) (2025-11-29) + +### Features + +* add toolType as useAIChat feature, and add sql chat which can help user generate sql which easy to query ([2dd3ea8](https://github.com/msgbyte/tianji/commit/2dd3ea83af4dd21a0fd802c5a20aa912362e9593)) + +### Bug Fixes + +* fix a bug which calc incorrect survey count ([e550e9c](https://github.com/msgbyte/tianji/commit/e550e9ccca292c6cc6a830e72c939b81b42148c2)) +* migrate cron runner status to cache manager to avoid multi-node async issue ([5a8f85c](https://github.com/msgbyte/tianji/commit/5a8f85c5422c2f83864c9c31eecb57dfc3ac61dd)) + +### Others + +* extract common aichatbot component which ready for reuse for other ai chat bot feature ([dde5219](https://github.com/msgbyte/tianji/commit/dde52192ba484b476c2ac834fba3e46a299bdffc)) +* improve email display for non-email ([76c3802](https://github.com/msgbyte/tianji/commit/76c3802a2fe57a6468ec090ff16e84d4e802a137)) +* rename warehouse insight hooks ([2bf0c61](https://github.com/msgbyte/tianji/commit/2bf0c619b927420cb2038f0b85a3a48ba76a70c4)) +* update translation ([de3401d](https://github.com/msgbyte/tianji/commit/de3401d8641ea1efe05f76d94aa1534f64ebd471)) + +## [1.30.15](https://github.com/msgbyte/tianji/compare/v1.30.14...v1.30.15) (2025-11-24) + +### Others + +* fix ci issue ([ae0e640](https://github.com/msgbyte/tianji/commit/ae0e640aa5f41be2705d433dcf1cc346c04f5b18)) + +## [1.30.14](https://github.com/msgbyte/tianji/compare/v1.30.13...v1.30.14) (2025-11-24) + +### Features + +* add ai chat persist feature ([d9ec593](https://github.com/msgbyte/tianji/commit/d9ec59313985747b897cee5e60113f4e9f119b5f)) +* add context usage for ai chat ([8322fcc](https://github.com/msgbyte/tianji/commit/8322fccb1646a02cc59dd1819bb71d45faa32678)) +* add download csv table for warehouse chart block ([efa1527](https://github.com/msgbyte/tianji/commit/efa15276331e416eea0df52257f8926115ee0d10)) +* add render complex which has group feature render chart in warehouse ai chat feature ([a5c9bd8](https://github.com/msgbyte/tianji/commit/a5c9bd836196b03edc096d846b879e6c8de95c6b)) +* add tip for large source data ([0de6235](https://github.com/msgbyte/tianji/commit/0de6235ef1624e84eed0c849e0b66f34a481c44c)) +* add warehouse ai chat allow stop feature ([bc969ce](https://github.com/msgbyte/tianji/commit/bc969ce198b98555f63e35a121941c92fa14dd2e)) +* add warehouse ai chat message actions: regenerate and copy feature ([e58034d](https://github.com/msgbyte/tianji/commit/e58034d9bb32e3076723dcab813cd8ff7a40bb55)) + +### Bug Fixes + +* adjust message content width based on role in AI response component ([f65f1e3](https://github.com/msgbyte/tianji/commit/f65f1e3ace5a543c55fba0f346369f9e52963339)) + +### Others + +* improve style of ai chat bot style and improve user interface ([0e1e21d](https://github.com/msgbyte/tianji/commit/0e1e21d8a568edfbf527afb12374fbb56c1b1710)) +* move processGroupedTimeSeriesData utils to share code ([45000ed](https://github.com/msgbyte/tianji/commit/45000edf7706bbb65d50b5e45aa0f25d418f461a)) + +## [1.30.13](https://github.com/msgbyte/tianji/compare/v1.30.12...v1.30.13) (2025-11-22) + +### Features + +* add manual recheck for workspace pause status by owners ([1247667](https://github.com/msgbyte/tianji/commit/12476676ea6adc63c55386bfecfa5905a229d064)) +* add tts feature for feed public page ([315b0bb](https://github.com/msgbyte/tianji/commit/315b0bb03ae1bb82c29a6fe80155df0fa70be587)) + +### Bug Fixes + +* fix a bug which ai gateway analytics will render negative number ([4d003cb](https://github.com/msgbyte/tianji/commit/4d003cbcc1176d01f16d1b4cd355e25f3633d451)) +* fix a bug which will use much resource when request will incorrect model name ([b9fc873](https://github.com/msgbyte/tianji/commit/b9fc873aa5f80f76e487a14bd39729a841b6a1de)) + +### Others + +* standardize error logging to use logger instead of console ([96d1cea](https://github.com/msgbyte/tianji/commit/96d1cead44c90473997fa2008f281214b53bdba8)) + +## [1.30.12](https://github.com/msgbyte/tianji/compare/v1.30.11...v1.30.12) (2025-11-20) + +### Bug Fixes + +* fix a bug which will make crash when print undefined value ([22ba8a2](https://github.com/msgbyte/tianji/commit/22ba8a2c007286ae387a6df2f825f0acbd48d474)) +* fix shortlink order incorrect issue ([58997d7](https://github.com/msgbyte/tianji/commit/58997d70aa86b3413c711af04b700b8145eccd63)) + +### Document + +* update tracking document with expo-router example ([c85996e](https://github.com/msgbyte/tianji/commit/c85996e2e0c1b77624d74c4505bf76d59d9620c2)) + +### Others + +* dump react-native package version to 1.0.5 ([d49a929](https://github.com/msgbyte/tianji/commit/d49a929df29d9493bbfa7e3705a21313ee431bbd)) +* update screen params handler ([e3027e6](https://github.com/msgbyte/tianji/commit/e3027e6437f37441ff56954d7747717c0f42c523)) + +## [1.30.11](https://github.com/msgbyte/tianji/compare/v1.30.10...v1.30.11) (2025-11-16) + +### Bug Fixes + +* fix survey count logic ([c2c8118](https://github.com/msgbyte/tianji/commit/c2c8118a997f067f97cd0ba5c94c6543d88d9347)) + +### Document + +* add more log ([d6a9d86](https://github.com/msgbyte/tianji/commit/d6a9d866cb8970a959fbfb70c790edca24d483a2)) +* update api document and resolve render issue for website api page ([94c824f](https://github.com/msgbyte/tianji/commit/94c824f6f86892ed1e38904de9f410b27d279e13)) + +### Others + +* update model price ([441770e](https://github.com/msgbyte/tianji/commit/441770eb60854dcd8b3cbc5cbb3e89955a725f25)) +* update translation ([1966254](https://github.com/msgbyte/tianji/commit/1966254b471a829655d3d1434919643ba2d1a1ef)) + +## [1.30.10](https://github.com/msgbyte/tianji/compare/v1.30.9...v1.30.10) (2025-11-11) + +### Features + +* add days visit charts for shortlink ([a3bcf49](https://github.com/msgbyte/tianji/commit/a3bcf4964bd08200afa228377269ea265a5897b9)) +* TimeEventChart add new props hideLegend ([e3f7df9](https://github.com/msgbyte/tianji/commit/e3f7df9cb593fca6dce0eeb46943ffd28264781e)) + +### Bug Fixes + +* improve legend display in time event chart when too much data ([8e615fd](https://github.com/msgbyte/tianji/commit/8e615fd102d0ac53db4619f740726bb336f1d4f9)) + +## [1.30.9](https://github.com/msgbyte/tianji/compare/v1.30.8...v1.30.9) (2025-11-10) + +### Others + +* add new implementation method for typescript support ([654abec](https://github.com/msgbyte/tianji/commit/654abec67bc9cf1f743ac2f87c7570505886f199)) + +## [1.30.8](https://github.com/msgbyte/tianji/compare/v1.30.7...v1.30.8) (2025-11-10) + +### Features + +* enhance MonitorRunner with caching for retried number and current status ([766d229](https://github.com/msgbyte/tianji/commit/766d2295c1431a18630ebf0d5333e56f9230b6c5)) + +### Bug Fixes + +* rollback typescript support which maybe have memory leak ([c5f4a57](https://github.com/msgbyte/tianji/commit/c5f4a57a9588b0ff5540ac46230263f075f75950)) + +### Others + +* improve JSON display performance with large payload ([2f093d6](https://github.com/msgbyte/tianji/commit/2f093d67621e8a72d424480c418c0713fcbbb83b)) + +## [1.30.7](https://github.com/msgbyte/tianji/compare/v1.30.6...v1.30.7) (2025-11-09) + +### Features + +* add test feature without deploy ([402e236](https://github.com/msgbyte/tianji/commit/402e23634b1ef2e1eb38bbd10d6debd2f0dde3d8)) +* add worker typescript support ([c723f62](https://github.com/msgbyte/tianji/commit/c723f626ca292f71c2948054bb8510e0a804e7f5)) + +### Others + +* add benchmarking for ts support and sandbox module demo ([474433f](https://github.com/msgbyte/tianji/commit/474433fe6a0e6739c718e0658f9c13a5aa0a7e25)) +* add editor tip for worker edit form ([e4a2bc2](https://github.com/msgbyte/tianji/commit/e4a2bc2cd1c4cfba3abe2c9cf2f0b100067c9222)) +* remove unused code ([c8a9814](https://github.com/msgbyte/tianji/commit/c8a981452258388b570cc6fb68f53b4488ffc906)) + +## [1.30.6](https://github.com/msgbyte/tianji/compare/v1.30.5...v1.30.6) (2025-11-04) + +### Features + +* add tianji cli which should more easy to manage worker ([c9ce2ab](https://github.com/msgbyte/tianji/commit/c9ce2ab18842a3e85f051e4d3fd2c81355133256)) +* add tianji worker pull command ([c658101](https://github.com/msgbyte/tianji/commit/c658101a8b7889b6b683527d890f2016872d6580)) +* add worker execution replay feature ([eee5f84](https://github.com/msgbyte/tianji/commit/eee5f846dc84a4da24d187a1fc18905130de0a45)) + +### Others + +* dump sdk version to latest openapi ([e4b61b2](https://github.com/msgbyte/tianji/commit/e4b61b2466e87d92c94d1c36687a4cd3333c57da)) +* update translations and increment SDK version to 1.30.5 ([8fa574c](https://github.com/msgbyte/tianji/commit/8fa574c99670cc9f8393d2f832b87f06dd03e8a1)) + +## [1.30.5](https://github.com/msgbyte/tianji/compare/v1.30.4...v1.30.5) (2025-11-03) + +### Features + +* add failed worker record usage and log support ([094291a](https://github.com/msgbyte/tianji/commit/094291a88206496b209893ad63a7df1bd73feebf)) +* add keyboard shortcut to execute SQL statements in editor ([2b77df7](https://github.com/msgbyte/tianji/commit/2b77df7cdaeab7b90986bd5074e21e1ad7ac9984)) +* add sql editor componemt ([b4e41ec](https://github.com/msgbyte/tianji/commit/b4e41ece65db171f1cb1c970237605b833210488)) +* add SQL editor with run button feature ([720db51](https://github.com/msgbyte/tianji/commit/720db51ccd724c458336a61f468f6a19e1229f03)) +* add sql formatter for sql editor ([3c34919](https://github.com/msgbyte/tianji/commit/3c3491901b84be4e54a313305dea46b5dcaa3373)) +* add warehouse sql query feature ([a2ce0a5](https://github.com/msgbyte/tianji/commit/a2ce0a53e7a7342d5ef109a4f9098fa2c7f79e88)) +* improve sql editor and query page display ([9594a14](https://github.com/msgbyte/tianji/commit/9594a14356bd2699975290bb24dee5f75826aacd)) + +### Bug Fixes + +* fix a bug which warehouse can not correct exec insight query issue with workspace level ([5fa8650](https://github.com/msgbyte/tianji/commit/5fa865057788c9481bfd0dfe1d865627a15cc0ee)) + +### Document + +* update tracker script document ([67a3023](https://github.com/msgbyte/tianji/commit/67a3023a5bb6ad3360b077ed1975d060ac87fdd7)) + +### Others + +* add query limit to avoid query too many data ([f4fe136](https://github.com/msgbyte/tianji/commit/f4fe136988bb6c7a41f5f74f82623a12b3d0c1cb)) +* add worker openapi ([e22eb2d](https://github.com/msgbyte/tianji/commit/e22eb2d3dcdada292cf36b71fe51103c1c54f636)) +* change sql formatter library to reduce bundle size ([f7eddb8](https://github.com/msgbyte/tianji/commit/f7eddb8fe67582479447a661dc2fd75533f0889f)) +* extract logic for ai generate ([97ee03a](https://github.com/msgbyte/tianji/commit/97ee03aa3b7604d9dbf955c443fe993121cd62be)) +* resolve duplicated register issue ([802b645](https://github.com/msgbyte/tianji/commit/802b645a7dcea94c0e70f048d6d8e3c83aceae2a)) +* streamline SQL CodeLens management for editor instances ([1b9bc27](https://github.com/msgbyte/tianji/commit/1b9bc2719e0ee9dd072319675dd4142285aa5775)) +* update default worker template ([339f861](https://github.com/msgbyte/tianji/commit/339f8612327103441bb39f7ae4854029548e0dce)) +* update storybook for more code editor ([c941638](https://github.com/msgbyte/tianji/commit/c94163869c6a5e5d6df31ec3ae5bed5ed3950771)) + +## [1.30.4](https://github.com/msgbyte/tianji/compare/v1.30.3...v1.30.4) (2025-10-26) + +### Features + +* implement distributed locking for worker cron execution to prevent concurrent runs ([34dceed](https://github.com/msgbyte/tianji/commit/34dceed6a5d0c5fd65a839f16a573b8ff71252b8)) + +## [1.30.3](https://github.com/msgbyte/tianji/compare/v1.30.2...v1.30.3) (2025-10-24) + +### Features + +* add survey insight query alias support ([ec7b8e0](https://github.com/msgbyte/tianji/commit/ec7b8e0743ec88520cd498491d886d2de6d13476)) +* add WorkerSparkline ([0f6e728](https://github.com/msgbyte/tianji/commit/0f6e728ad97fd98db35b193684697ad6a59c8b29)) + +### Document + +* add summaries to various API endpoints for improved documentation ([e835a00](https://github.com/msgbyte/tianji/commit/e835a00c25e361e64e2a5a34801e5cacaef7fa47)) +* update openapi.json ([586441e](https://github.com/msgbyte/tianji/commit/586441ef60bf24a5aa77dc2ccc83b38c050b4bd9)) + +## [1.30.2](https://github.com/msgbyte/tianji/compare/v1.30.1...v1.30.2) (2025-10-24) + +### Bug Fixes + +* fix a bug which monitor not have lock and will call many times in multi-node case ([70d8059](https://github.com/msgbyte/tianji/commit/70d80592b041131ce8b9876706811e2508b31800)) +* fix insights fetch same column name not work in clickhouse and improve display for insight metric ([900224b](https://github.com/msgbyte/tianji/commit/900224b5944b69bff716b17f871ab79c6b61a148)) + +### Others + +* update worker editor default template ([d7e0905](https://github.com/msgbyte/tianji/commit/d7e0905bc6a71f84b33ed93de438378ae031d891)) + +## [1.30.1](https://github.com/msgbyte/tianji/compare/v1.30.0...v1.30.1) (2025-10-23) + +### Bug Fixes + +* fix a issue which oauth not have email handle ([99a009c](https://github.com/msgbyte/tianji/commit/99a009c67bb309c13178b025cd66d8f9b44a0329)) + +### Others + +* skip if tianji already init ([e209a84](https://github.com/msgbyte/tianji/commit/e209a84fae780eb260a6ad991a8245593df410aa)) + +## [1.30.0](https://github.com/msgbyte/tianji/compare/v1.29.2...v1.30.0) (2025-10-21) + +### Features + +* add NavigationBlocker component to prevent navigation on unsaved changes ([ad28a51](https://github.com/msgbyte/tianji/commit/ad28a51ef174f86b9684716c78b02eb4468b0fb7)) +* add public page for survey ([2243915](https://github.com/msgbyte/tianji/commit/2243915b24dc0523bcd5ca3cea74da3c0d48f11d)) +* add ShortLinkType enum and update short link creation to support type ([2e02a98](https://github.com/msgbyte/tianji/commit/2e02a98cd2ef0b4ea7978d012bd2ca01e545a0f5)) + +### Bug Fixes + +* fix a bug which telemetry not work in some case ([f55a07c](https://github.com/msgbyte/tianji/commit/f55a07cc32f30bff73e38901d9fea71ba39f2e04)) + +### Others + +* refactor default color theme usage ([91e11fe](https://github.com/msgbyte/tianji/commit/91e11fe99d09a6731fb57f63c00e27c0e1334a0c)) +* update translation ([0c8ad6e](https://github.com/msgbyte/tianji/commit/0c8ad6e1d5a4f1ca0f46a9564b6bde055789262f)) + +## [1.29.2](https://github.com/msgbyte/tianji/compare/v1.29.1...v1.29.2) (2025-10-20) + +### Bug Fixes + +* fix version issue for zod version incorrect ([7f3a41b](https://github.com/msgbyte/tianji/commit/7f3a41b37a66ad72ea401f8350699467eecce94b)) + +### Document + +* add blog ([c9815b0](https://github.com/msgbyte/tianji/commit/c9815b03f4c778b5fd2564111ef2e0b5cf5f4048)) + +### Others + +* add clickhouse migrate on db migrate script ([d03167a](https://github.com/msgbyte/tianji/commit/d03167abfd88dab9ddcbe31627c88d1c92c78f94)) + +## [1.29.1](https://github.com/msgbyte/tianji/compare/v1.29.0...v1.29.1) (2025-10-18) + +### Bug Fixes + +* [#230](https://github.com/msgbyte/tianji/issues/230) fix a issue which monitor bar not disable after health bar refactor ([123ae28](https://github.com/msgbyte/tianji/commit/123ae289a7e97569fabb8c9b40eed9779d1c8c82)) +* fix a bug which monitor can not unselect when update website info ([f424a32](https://github.com/msgbyte/tianji/commit/f424a3285ff2cc7249f0a7c1580d4b4314230b45)) + +### Others + +* update clickhouse migration sql for utm ([8ba5acf](https://github.com/msgbyte/tianji/commit/8ba5acfa98a221c1625373dc08dcfe7947635831)) + +## [1.29.0](https://github.com/msgbyte/tianji/compare/v1.28.0...v1.29.0) (2025-10-17) + +### Features + +* add public url environment ([928f756](https://github.com/msgbyte/tianji/commit/928f7565d2c5221214d84f79e15558d42eb7ee26)) +* add utm support ([3c2190a](https://github.com/msgbyte/tianji/commit/3c2190af63301d1c45ef354c15497b9cf3178f21)) + +### Bug Fixes + +* fix date message display issue ([81f3ba1](https://github.com/msgbyte/tianji/commit/81f3ba139bfaba95dcf6b6ead0e42eee93ee9a0a)) + +### Others + +* add hour level disable for long date range ([c8579e9](https://github.com/msgbyte/tianji/commit/c8579e9a5d582321de7df9380504812392f2baca)) +* optimize date lookup performance by using a Map for O(1) access, reduces time complexity from O(n×m) to O(n+m) ([511050e](https://github.com/msgbyte/tianji/commit/511050edc19db4097036b5f2b59ee166192198ed)) + +## [1.28.0](https://github.com/msgbyte/tianji/compare/v1.27.12...v1.28.0) (2025-10-15) + +### Features + +* add datakey parse support which can improve display for share query params ([dee32ae](https://github.com/msgbyte/tianji/commit/dee32aef32d9fd776a8219f48197c2000f2e2c3c)) +* add insight share feature ([e371612](https://github.com/msgbyte/tianji/commit/e37161205377d264e0a2d7654d932ee2ed47ba85)) + +### Document + +* add more info for survey openapi endpoint ([f8fe355](https://github.com/msgbyte/tianji/commit/f8fe35577e64a2a32453157a6c78dc1a42597e7c)) + +### Others + +* improve query performance for worker stats ([1510218](https://github.com/msgbyte/tianji/commit/1510218a96d478cbbb5c8117e179a6d2b9a0f252)) +* upgrade zod to v4 and trpc to v11 ([955fd53](https://github.com/msgbyte/tianji/commit/955fd53a962a9f174081e7fc775dd4d3d82eba11)) + +## [1.27.12](https://github.com/msgbyte/tianji/compare/v1.27.11...v1.27.12) (2025-10-14) + +### Features + +* add shortlink feature ([2fa9ea8](https://github.com/msgbyte/tianji/commit/2fa9ea858ce8154861f6c6c213e9330e2f88b37e)) + +### Bug Fixes + +* fix crash for insights context ([e854ec6](https://github.com/msgbyte/tianji/commit/e854ec6bbea7a153c2e4307767bf08ab52ea2c25)) + +### Document + +* update openapi overview description ([ca6077e](https://github.com/msgbyte/tianji/commit/ca6077efc23a7f6aef366f2e2a27e31ecb36612d)) + +### Others + +* update translation ([5c78c38](https://github.com/msgbyte/tianji/commit/5c78c38e42d27c3572a3a7f7aecff6135b3386d5)) + +## [1.27.11](https://github.com/msgbyte/tianji/compare/v1.27.10...v1.27.11) (2025-10-14) + +### Bug Fixes + +* fix ci issue ([bdb457d](https://github.com/msgbyte/tianji/commit/bdb457dd1aae83fe2548d827ef8e5fc95dc360c9)) + +## [1.27.10](https://github.com/msgbyte/tianji/compare/v1.27.9...v1.27.10) (2025-10-14) + +### Features + +* add full screen worker editor ([97db785](https://github.com/msgbyte/tianji/commit/97db7851e61f695cb331944f16ab131b60102df2)) +* add get result endpoint which can get survey result by result id ([016bb26](https://github.com/msgbyte/tianji/commit/016bb2644fae77dd0d872de45eedcc7cae70675a)) +* add worker code editor entry ([54b119d](https://github.com/msgbyte/tianji/commit/54b119dff0e503d2b9f24701bcac5edf54dc7ee9)) +* add worker visibility ([5129825](https://github.com/msgbyte/tianji/commit/51298250c4767e206740f520678f14f171cf6754)) + +### Others + +* add backup script ([714f088](https://github.com/msgbyte/tianji/commit/714f0883e683829b011c42fc1e59667a26167ac4)) +* add context scope for insight feature ([18c8b66](https://github.com/msgbyte/tianji/commit/18c8b66d3e1437080ff0be93ad4f4073a096d75d)) +* improve performance of work exec ([122a13e](https://github.com/msgbyte/tianji/commit/122a13e56795f18017490ee505bc04da9dfa45cc)) +* update translation ([ea33bf7](https://github.com/msgbyte/tianji/commit/ea33bf7ae124638ba8b590008f2fb2fdd75cb2f6)) + +## [1.27.9](https://github.com/msgbyte/tianji/compare/v1.27.8...v1.27.9) (2025-10-10) + +### Features + +* add cron preview feature to WorkerEditForm ([76f18ff](https://github.com/msgbyte/tianji/commit/76f18ffe277711363d718fc7c0efb6b9a427e8d5)) +* improve description display for function worker ([d7d4776](https://github.com/msgbyte/tianji/commit/d7d477685287d5d707bc1ab1a21076da129237d6)) + +### Bug Fixes + +* fix worker runner issue problem ([c10917c](https://github.com/msgbyte/tianji/commit/c10917c66910e180f9939a03e315648d58f60cba)) + +### Document + +* add badge on turbo0 ([9719ea9](https://github.com/msgbyte/tianji/commit/9719ea938cb9955d2bebfe4dce798da79afd8dac)) + +### Others + +* change endpoint place ([af168af](https://github.com/msgbyte/tianji/commit/af168af7bc3d2a785fc5ad40381fb7474afcb4a6)) + +## [1.27.8](https://github.com/msgbyte/tianji/compare/v1.27.7...v1.27.8) (2025-10-09) + +### Features + +* add worker runtime context ([7527fdc](https://github.com/msgbyte/tianji/commit/7527fdcfec4ee330d9bd858f62216493d7c4c2df)) + +### Others + +* upgrade isolated-vm version ([eb542b5](https://github.com/msgbyte/tianji/commit/eb542b538eba1900bdb40c892b02a82dee577fa4)) + +## [1.27.7](https://github.com/msgbyte/tianji/compare/v1.27.6...v1.27.7) (2025-10-08) + +### Features + +* add id language support ([d95435a](https://github.com/msgbyte/tianji/commit/d95435a4aa99d6af281eb21e6659a930968ef386)) + +### Document + +* add id language support ([1003668](https://github.com/msgbyte/tianji/commit/10036687a5d7f271810eddadb6c9412d31b3c79c)) + +### Others + +* update patched dependency version ([0587fb0](https://github.com/msgbyte/tianji/commit/0587fb04c1de9fc8f46b56c2beeb7d83283a1c5b)) + +## [1.27.6](https://github.com/msgbyte/tianji/compare/v1.27.5...v1.27.6) (2025-10-05) + +### Features + +* add preview collapse button in worker page ([67ae4f0](https://github.com/msgbyte/tianji/commit/67ae4f092933a73310ec49f6b224e5e9a1d7ee41)) +* add tooltip for website share feature when feed is disabled ([6f18466](https://github.com/msgbyte/tianji/commit/6f184662a2fab0bf7e0793993891ccdacb2b4551)) + +### Document + +* add blog ([e103dc5](https://github.com/msgbyte/tianji/commit/e103dc596c9833d459619eab21ccb85f33cd6338)) + +### Others + +* update translation ([ba11c97](https://github.com/msgbyte/tianji/commit/ba11c97c350dc0b97a29dcf01c630882404dd3f7)) +* upgrade pnpm version to 10.17.1 across the project ([0803aae](https://github.com/msgbyte/tianji/commit/0803aae4a0095021d669d067b8d65a439ea36141)) + +## [1.27.5](https://github.com/msgbyte/tianji/compare/v1.27.4...v1.27.5) (2025-10-04) + +### Others + +* add glib which can resolve docker scout issue ([687bb7c](https://github.com/msgbyte/tianji/commit/687bb7c9348e0173b849d237d2e9875f1fee3b6d)) +* upgrade report docker lib version ([e21a436](https://github.com/msgbyte/tianji/commit/e21a43680eedb3d7c61fbb294870e1b58a359189)) +* upgrade zeromq version ([df62b53](https://github.com/msgbyte/tianji/commit/df62b533476f5ec3a36116ee6e621791c7939048)) + +## [1.27.4](https://github.com/msgbyte/tianji/compare/v1.27.3...v1.27.4) (2025-10-03) + +### Others + +* fix ci problem ([033c425](https://github.com/msgbyte/tianji/commit/033c4257130dc35ccb2b90d35c1b677b74ba4e85)) + +## [1.27.3](https://github.com/msgbyte/tianji/compare/v1.27.2...v1.27.3) (2025-10-03) + +### Features + +* add pageview tracker for tianji self ([9103ba5](https://github.com/msgbyte/tianji/commit/9103ba51b6aff6946156f593431b8654645fb4bc)) +* add public share feature for feed channels ([47c890e](https://github.com/msgbyte/tianji/commit/47c890eb2f63911ae2698ed4e7e779d72cb1a6c2)) +* add website share feature which can share info to public ([36b521e](https://github.com/msgbyte/tianji/commit/36b521eb8c8c4fcb8a5d56812351474f9cc5f8d1)) +* sdk add trackPageView function ([5b30e8d](https://github.com/msgbyte/tianji/commit/5b30e8dffedda78ed28af607ed5b00bd01c87a76)) + +### Others + +* update translation ([74f575b](https://github.com/msgbyte/tianji/commit/74f575b4f832ea5d781ecdfd36379e114bf849b4)) + +## [1.27.2](https://github.com/msgbyte/tianji/compare/v1.27.1...v1.27.2) (2025-09-29) + +### Features + +* add multi console log item support ([e1b599d](https://github.com/msgbyte/tianji/commit/e1b599d9bc13ded28bf9516f6e570857d0b9a606)) +* worker add request payload ([eb77559](https://github.com/msgbyte/tianji/commit/eb77559f8b0145241ff7dc69f1f44c5799471a76)) + +### Bug Fixes + +* fix type error for request function ([bd3f72e](https://github.com/msgbyte/tianji/commit/bd3f72e70f93e1ec60e45212e86ff29868076f15)) + +### Others + +* change order of worker ([50621e3](https://github.com/msgbyte/tianji/commit/50621e3814d8c31806067ac2c430acbe007e8da6)) + +## [1.27.1](https://github.com/msgbyte/tianji/compare/v1.27.0...v1.27.1) (2025-09-28) + +### Features + +* add function worker revision ([bf2a800](https://github.com/msgbyte/tianji/commit/bf2a800f3a68a7a27f879d12715fe1bddcf7f32a)) +* add tabs for subscription and credit recharge in billing settings ([1953b09](https://github.com/msgbyte/tianji/commit/1953b09ae46653f9926b70caaeefd81378283574)) + +## [1.27.0](https://github.com/msgbyte/tianji/compare/v1.26.4...v1.27.0) (2025-09-28) + +## [1.26.4](https://github.com/msgbyte/tianji/compare/v1.26.3...v1.26.4) (2025-09-28) + +### Features + +* add credit balance and recharge feature for billing ([7f7d2a7](https://github.com/msgbyte/tianji/commit/7f7d2a728f2ae7a2e86bf1200c322c6970dfda14)) +* add credit history page ([d75e791](https://github.com/msgbyte/tianji/commit/d75e791d325b55d49710ae467e51c7e5622fe0d5)) +* add WebsiteEventAnalysis component to display event metrics ([b89ec22](https://github.com/msgbyte/tianji/commit/b89ec2284b96a727189b669ee9bc234f07334b65)) + +### Others + +* improve display for credit balance ([54967e5](https://github.com/msgbyte/tianji/commit/54967e5526c727e08803410117ea3b07f5ac8b5c)) +* update translation ([fe98ea6](https://github.com/msgbyte/tianji/commit/fe98ea6900d72b33cd9a0397169eaf558a76fd4d)) + +## [1.26.3](https://github.com/msgbyte/tianji/compare/v1.26.2...v1.26.3) (2025-09-24) + +### Features + +* add observability configuration and event tracker ([768a145](https://github.com/msgbyte/tianji/commit/768a14584dbd2593b7c3a9efa737bb2054bea826)) + +### Bug Fixes + +* fix a issue when open function page will crash total page ([de58aca](https://github.com/msgbyte/tianji/commit/de58acaec48e5731842eaa4c419bf1fe10523ae1)) +* replace ScrollArea with div in WorkerExecutionDetail for better layout ([755dfac](https://github.com/msgbyte/tianji/commit/755dface0eaf3b3bcb8a7824298b6ec4eb7d3ba5)) + +### Others + +* add more event tracking ([872611e](https://github.com/msgbyte/tianji/commit/872611ebce53f8e3ff771ccaf62a0aea05f713a9)) +* adjust header title styling for better layout ([f622569](https://github.com/msgbyte/tianji/commit/f62256900b45b23cf3bbadf4cd078c626b9190de)) +* fix broken link ([06bda72](https://github.com/msgbyte/tianji/commit/06bda72a0969a2679ede1e4db889b4a59009bbd0)) + +## [1.26.2](https://github.com/msgbyte/tianji/compare/v1.26.1...v1.26.2) (2025-09-23) + +### Features + +* add code validator for function worker ([d1bb53b](https://github.com/msgbyte/tianji/commit/d1bb53b991d096835df79b7b0557c8bd4da5f7a1)) +* add function worker entry and service count ([0c0da21](https://github.com/msgbyte/tianji/commit/0c0da219bcbb4a5f30ebe07d8270d1daf746097a)) + +### Bug Fixes + +* ensure worker deletion handles null worker case and invalidate cache ([4a410c8](https://github.com/msgbyte/tianji/commit/4a410c83ba4a918416fbb5a2f8621d79fb9dda3c)) +* fix crash when open worker detail for some case ([27f7068](https://github.com/msgbyte/tianji/commit/27f70684099d5ed9e018378fe24677bc2de7869f)) + +### Document + +* update environment document ([afc93e9](https://github.com/msgbyte/tianji/commit/afc93e973cb9d952349bfb89fb9dbf855c720fd0)) + +### Others + +* update translations ([7307e8a](https://github.com/msgbyte/tianji/commit/7307e8a3504d3f5430f7d65f48defa10b113d641)) + +## [1.26.1](https://github.com/msgbyte/tianji/compare/v1.26.0...v1.26.1) (2025-09-22) + +### Bug Fixes + +* fix field error issue for warehouse long table ([47016b0](https://github.com/msgbyte/tianji/commit/47016b022302403a1ebe262f4054f3b3f1f4cedc)) + +### Others + +* update default desktop panel layout size ([d9c1688](https://github.com/msgbyte/tianji/commit/d9c1688fe926c70375f1d2f63e3d69838ef78927)) + +## [1.26.0](https://github.com/msgbyte/tianji/compare/v1.25.14...v1.26.0) (2025-09-21) + +### Features + +* add custom auth icon support ([303372e](https://github.com/msgbyte/tianji/commit/303372ee20d7a2b76636cbf42ae1d5cd90426749)) +* add debug option for ClickHouse sync cronjob ([3e64174](https://github.com/msgbyte/tianji/commit/3e6417480863b3596ed0e7b9a8549a4ecf5b53cb)) +* add DotPatternBackground component and theme support in register route ([edc1a6d](https://github.com/msgbyte/tianji/commit/edc1a6d54087c3446021bd3278e0ae6f23ce8542)) +* add redis adaptar for socket.io ([2202931](https://github.com/msgbyte/tianji/commit/2202931b9193d5a5a7cb6c88ef0bab3db543e478)) +* add REGISTER_AUTO_JOIN_WORKSPACE_ID env for auto join workspace with new user ([9ad13fc](https://github.com/msgbyte/tianji/commit/9ad13fc5a7c8461ec6bfe09b0d98dbe42d6a1496)) +* enhance ChartRender component with metrics validation and improved query handling ([7422450](https://github.com/msgbyte/tianji/commit/742245042e658254c450ee4c067d5b2287330a05)) + +### Bug Fixes + +* fix style issue for ai gateway sparkline ([8d97872](https://github.com/msgbyte/tianji/commit/8d97872f15660a63fffc405b450b98c46924137e)) + +### Others + +* fix ci issue ([c634185](https://github.com/msgbyte/tianji/commit/c6341851f0c6167a0d9f1c1f266b27290c673cd6)) +* update tianji favicon icon ([ac1628a](https://github.com/msgbyte/tianji/commit/ac1628a9ef652bef8132848425884c32b986a207)) +* update translation ([a149b02](https://github.com/msgbyte/tianji/commit/a149b02ced177c5ff2b55f7c75660f048e14ba79)) + +## [1.25.14](https://github.com/msgbyte/tianji/compare/v1.25.13...v1.25.14) (2025-09-17) + +### Others + +* add openrouter ranker for tianji ([98e239b](https://github.com/msgbyte/tianji/commit/98e239b06277c940cb9dcdba201ba437f88f60cf)) + +## [1.25.13](https://github.com/msgbyte/tianji/compare/v1.25.12...v1.25.13) (2025-09-17) + +### Features + +* enhance AIGateway analytics with detailed metrics and improved naming conventions ([2878777](https://github.com/msgbyte/tianji/commit/2878777bacbc7b80a3796544f28ffc57e239a602)) + +## [1.25.12](https://github.com/msgbyte/tianji/compare/v1.25.11...v1.25.12) (2025-09-17) + +### Features + +* add ai gateway analytics ([78559d7](https://github.com/msgbyte/tianji/commit/78559d7c536753243e1f354d2a0c65eb8bdabf45)) + +### Bug Fixes + +* fix timezone config in monitor feature not work issue ([1cd0245](https://github.com/msgbyte/tianji/commit/1cd024530943610f9a8a8af4b65c67de8cc1db0b)) + +### Others + +* changed correct timezone display in settings ([9a62f86](https://github.com/msgbyte/tianji/commit/9a62f86f20465d70e5aa4b0620ae230cc0a76502)) +* improve calc token usage and improve quota alert formatting ([ef9984c](https://github.com/msgbyte/tianji/commit/ef9984c9ddd5885e1160b8b648ab2c301800f0fc)) +* quota alert support correct time display for timezone with workspace config ([842f83a](https://github.com/msgbyte/tianji/commit/842f83a468d0012454dad5acd77783efaa6089e0)) +* update translation ([9cca1a7](https://github.com/msgbyte/tianji/commit/9cca1a70ada680b372a04bfb515098fb26e015ad)) + +## [1.25.11](https://github.com/msgbyte/tianji/compare/v1.25.10...v1.25.11) (2025-09-16) + +### Others + +* add openrouter price support, improve calc price more precision ([f5881ae](https://github.com/msgbyte/tianji/commit/f5881aebc1586b28d76ec8db6fc30bee07d21c39)) + +## [1.25.10](https://github.com/msgbyte/tianji/compare/v1.25.9...v1.25.10) (2025-09-16) + +### Others + +* fix ci problem ([ae59609](https://github.com/msgbyte/tianji/commit/ae596093b09d8b130b76f5a25688e70388028913)) + +## [1.25.9](https://github.com/msgbyte/tianji/compare/v1.25.8...v1.25.9) (2025-09-15) + +### Features + +* add json editor and warehouse config ([d841396](https://github.com/msgbyte/tianji/commit/d8413963c250586a734407732ed3f3e9aeeee0c8)) +* add warehouse query support with workspace level config ([ef5a852](https://github.com/msgbyte/tianji/commit/ef5a852331560444204654b8c4eddd43ab95c312)) +* add workspace config ([f64c984](https://github.com/msgbyte/tianji/commit/f64c984759bda51338133cb4e2bc1c9e2f5b1a7e)) +* refactor workspace config handling and expose schemas for insights applications ([eba3bf5](https://github.com/msgbyte/tianji/commit/eba3bf5732875fcd1c28f1b20e607de199c4c277)) + +### Others + +* upgrade @modelcontextprotocol/inspector version to resolve CVE issue [#222](https://github.com/msgbyte/tianji/issues/222) ([664b0c0](https://github.com/msgbyte/tianji/commit/664b0c0bd80f2a27004458214cec48d316840779)) + +## [1.25.8](https://github.com/msgbyte/tianji/compare/v1.25.7...v1.25.8) (2025-09-14) + +### Features + +* add refresh feature in aigateway overview which can easy update info without web refresh ([2062adc](https://github.com/msgbyte/tianji/commit/2062adc35ef875e32024790c07c3ba8372c86f30)) + +### Others + +* add health check and improve health endpoint ([7b049c9](https://github.com/msgbyte/tianji/commit/7b049c94b015e68eaa9e5c6205cb234609249c33)) + +## [1.25.7](https://github.com/msgbyte/tianji/compare/v1.25.6...v1.25.7) (2025-09-14) + +### Features + +* add lock to reduce notification ([49c6e77](https://github.com/msgbyte/tianji/commit/49c6e774b6fc6e9e09fed01b7cdd1927353f4e50)) + +## [1.25.6](https://github.com/msgbyte/tianji/compare/v1.25.5...v1.25.6) (2025-09-14) + +### Features + +* add distributed lock implementation for managing concurrent tasks ([764cfaa](https://github.com/msgbyte/tianji/commit/764cfaa88ac1599fa6c1a4ab8eb0c947a1ecb387)) +* enhance PostgreSQL to ClickHouse sync with distributed locking mechanism ([b3f3373](https://github.com/msgbyte/tianji/commit/b3f33732ca1b4d8497061ddccccfce5c4903adbd)) + +## [1.25.5](https://github.com/msgbyte/tianji/compare/v1.25.4...v1.25.5) (2025-09-12) + +### Bug Fixes + +* resolve ai gateway log infinite fetch issue ([810789e](https://github.com/msgbyte/tianji/commit/810789e1169c4bd42a04bde029cb660200d088ac)) + +### Others + +* improve refresh function which improve performance for infinite list ([ad38950](https://github.com/msgbyte/tianji/commit/ad38950caa226e40b62c87ea732467acc59b05d2)) + +## [1.25.4](https://github.com/msgbyte/tianji/compare/v1.25.3...v1.25.4) (2025-09-12) + +### Features + +* improve algorithm of calc prompt token and prefer to use response token result rather than calc by self ([988caed](https://github.com/msgbyte/tianji/commit/988caed8733cbb2227a30d37de6b481edb84c823)) + +## [1.25.3](https://github.com/msgbyte/tianji/compare/v1.25.2...v1.25.3) (2025-09-12) + +### Others + +* fix ci issue ([6243ae5](https://github.com/msgbyte/tianji/commit/6243ae52eeff25ffc1fdee56a86c215810ef2d9d)) + +## [1.25.2](https://github.com/msgbyte/tianji/compare/v1.25.1...v1.25.2) (2025-09-12) + +### Others + +* fix ci issue ([8f8a573](https://github.com/msgbyte/tianji/commit/8f8a5736246e4359c286c953b5fd6f8cf5582131)) + +## [1.25.1](https://github.com/msgbyte/tianji/compare/v1.25.0...v1.25.1) (2025-09-12) + +### Features + +* add keyv cache manager support which can make sure cache can be use in multi-node ([4b20d32](https://github.com/msgbyte/tianji/commit/4b20d32e7161f1dbba406d46bd183f6d6dd44b3f)) +* add update function to cache management for quota alerts ([53e32f4](https://github.com/msgbyte/tianji/commit/53e32f487ab9162bd6bda76ebc7627bb58c77fc4)) +* implement token calculation queue to optimize performance and prevent high CPU usage ([0df31eb](https://github.com/msgbyte/tianji/commit/0df31ebad6a983d0441cc351d2bc6e24a7e89a03)) + +### Others + +* update cost calc timezone ([46fb8d2](https://github.com/msgbyte/tianji/commit/46fb8d28b67b4a268155643aaef961d77b4869b2)) +* update translation ([bbcc720](https://github.com/msgbyte/tianji/commit/bbcc7202ebd996b5cfdc273fc4459e4019fffd5c)) + +## [1.25.0](https://github.com/msgbyte/tianji/compare/v1.24.29...v1.25.0) (2025-09-11) + +### Features + +* add AIGateway quota alert ([1948b42](https://github.com/msgbyte/tianji/commit/1948b42b4c70ba6f45fafff88a1c39d7a4e565ac)) +* add NotificationPickerV2 and ColorTagV2 ([1a4de44](https://github.com/msgbyte/tianji/commit/1a4de44ba0ef443393e7788172687ac3a917c717)) + +### Others + +* improve fetch logic to avoid block ([fe77fad](https://github.com/msgbyte/tianji/commit/fe77fad6028d7e9e7a85c2bb42ea1c2e177c0229)) +* remove unused cronjob ([8150656](https://github.com/msgbyte/tianji/commit/815065677bae971c5980546d788177c6c6d0637e)) +* replace Spin component with LoadingView for better loading state management ([f1892dd](https://github.com/msgbyte/tianji/commit/f1892dd4102ade6f28e803e8398d83ef596ae195)) + +## [1.24.29](https://github.com/msgbyte/tianji/compare/v1.24.28...v1.24.29) (2025-09-11) + +### Bug Fixes + +* fix image problem ([df8b8cf](https://github.com/msgbyte/tianji/commit/df8b8cf7f6c55d9c937332f193e23d2c05c44dd7)) + +## [1.24.28](https://github.com/msgbyte/tianji/compare/v1.24.27...v1.24.28) (2025-09-10) + +### Features + +* add AIGatewayPricingBtn and AIGatewayPricingModal components for model pricing display ([c5cb8ba](https://github.com/msgbyte/tianji/commit/c5cb8ba6f698038bbaeb008060e99576a113fa42)) +* add AIGatewaySparkline component in aigateway list ([426fa82](https://github.com/msgbyte/tianji/commit/426fa822c0784080e54976971aebacaa99f68327)) +* add sparkline component ([a5f41db](https://github.com/msgbyte/tianji/commit/a5f41db20f7e6aead9323e1a0aa02c7c204f3f52)) +* add v2 llm model price list which can improve price usage with more models ([2c0b498](https://github.com/msgbyte/tianji/commit/2c0b498c583d76fcacf27bcac3d31778d57241dd)) + +### Others + +* add storybook support ([7273155](https://github.com/msgbyte/tianji/commit/727315567408ce98ce71fd2a3deb2cdd6dffdf7b)) +* translation and change ai price button position ([dea798a](https://github.com/msgbyte/tianji/commit/dea798a353061f60f408792889887615cf63c90e)) +* update model prices ([74bd53b](https://github.com/msgbyte/tianji/commit/74bd53bccba9ac627966b4cb628e3707eb645b9a)) + +## [1.24.27](https://github.com/msgbyte/tianji/compare/v1.24.26...v1.24.27) (2025-09-08) + +### Features + +* add aiTranslation field for survey insight ([b2b7ee7](https://github.com/msgbyte/tianji/commit/b2b7ee7bec61c18dd13f6a015d8cd3ce059c5382)) +* add duplicate feature in survey ([c00ff7a](https://github.com/msgbyte/tianji/commit/c00ff7a0ea43857f3f222d7912cd2cdbe4e5856d)) + +### Bug Fixes + +* fix a bug which in list and not list in insight not work ([09f5aea](https://github.com/msgbyte/tianji/commit/09f5aeaee14539ef57d8fcc992f723f4a1650f41)) + +### Document + +* add blog post on privacy-first website analytics with Tianji ([7ccab50](https://github.com/msgbyte/tianji/commit/7ccab50913449fca7655e3f016d5459030bfc679)) + +### Others + +* add blog ([ad115f3](https://github.com/msgbyte/tianji/commit/ad115f30dbbcadc1a8266bcd05f944f193712377)) +* update clickhouse sync cronjob ([bbf865a](https://github.com/msgbyte/tianji/commit/bbf865a59c90357f6e42e4ff50b055c2f4d0b203)) + +## [1.24.26](https://github.com/msgbyte/tianji/compare/v1.24.25...v1.24.26) (2025-08-29) + +### Features + +* website session info support clickhouse query ([0cd5714](https://github.com/msgbyte/tianji/commit/0cd5714fefe9a9420502c86aae625580aef7c76d)) + +## [1.24.25](https://github.com/msgbyte/tianji/compare/v1.24.24...v1.24.25) (2025-08-29) + +### Features + +* add alias support for insight ([79f156d](https://github.com/msgbyte/tianji/commit/79f156dbf798dcf9f791510be80f7932732056fa)) + +### Document + +* add new blog ([1918605](https://github.com/msgbyte/tianji/commit/1918605e25f53be94201aeb489c9daf2c8fc85f3)) +* improve seo info ([2d21a87](https://github.com/msgbyte/tianji/commit/2d21a87420b319872ebabde195fa9f0014ac7883)) + +### Others + +* remove unused footer ([6c3baf8](https://github.com/msgbyte/tianji/commit/6c3baf8b326b2a99c5f741bb65ef0b79e7c5abf2)) +* update query structure in WebsiteOverview component for insights ([ea72bb1](https://github.com/msgbyte/tianji/commit/ea72bb11f3b88aebec58fb580db8bee17089e206)) +* update SQL query handling in InsightsSqlBuilder and WebsiteInsightsSqlBuilder ([7dc952a](https://github.com/msgbyte/tianji/commit/7dc952a295db3d47aa24cbc7fa79a8cd8a0ded15)) + +## [1.24.24](https://github.com/msgbyte/tianji/compare/v1.24.23...v1.24.24) (2025-08-28) + +### Features + +* enhance warehouse AI tools with database connection handling ([2fba864](https://github.com/msgbyte/tianji/commit/2fba8645e2ba7cea614dfe69fa46615c73d0f444)) + +### Others + +* add ui testing ([4f8672c](https://github.com/msgbyte/tianji/commit/4f8672c82dc86af116f53b49bd1f422aa6237e33)) +* allow non-dev mode can use insight chat endpoint ([f3ec5a0](https://github.com/msgbyte/tianji/commit/f3ec5a00c5965ca93dda0bf399d434623eb7d4e7)) +* improve database status handling in insights warehouse page ([3a56d15](https://github.com/msgbyte/tianji/commit/3a56d150e7e955104d9dd234e6a4e0c21940d1e7)) +* update model price ([5fcd1fe](https://github.com/msgbyte/tianji/commit/5fcd1fed758d2c4ec68ff2a065a88074aa15867d)) + +## [1.24.23](https://github.com/msgbyte/tianji/compare/v1.24.22...v1.24.23) (2025-08-26) + +### Features + +* add insights warehouse scope select feature ([0a8ab92](https://github.com/msgbyte/tianji/commit/0a8ab927a64b9b77f84edddda6ec435713c05c34)) +* add Turkish (tr) localization ([#218](https://github.com/msgbyte/tianji/issues/218)) ([1c011cb](https://github.com/msgbyte/tianji/commit/1c011cbf2ee6fcadc3741b5551d1c8fa9a294d8b)) +* add WarehouseDatabase and WarehouseDatabaseTable models ([a1f353e](https://github.com/msgbyte/tianji/commit/a1f353ee1f3768218c214eb5f825c9b8bffd72d4)) +* enhance warehouse insights functionality with new connection management and table synchronization features ([2d1178f](https://github.com/msgbyte/tianji/commit/2d1178fd110cd79f06411b63738da65d2e54b892)) +* implement insights warehouse connection management and table editing features ([7034348](https://github.com/msgbyte/tianji/commit/70343487819571440279b8f1eceb7c29f3f7fc09)) + +### Others + +* remove unused code ([039ccbe](https://github.com/msgbyte/tianji/commit/039ccbe5bfd07f18b73a49bed13fc4f0c35fba53)) +* translation ([ece4e9f](https://github.com/msgbyte/tianji/commit/ece4e9f843548bfbc5174e70d9647e3d87f7b550)) +* update prompt and suggestions ([38ac73a](https://github.com/msgbyte/tianji/commit/38ac73a73cbe326efb1e1639bbf31bf5701cdc15)) +* update prompt of warehouse ai insight ([bc90a75](https://github.com/msgbyte/tianji/commit/bc90a75524e41b0aaac7883fca0b1f8302fcc3e9)) +* update translation ([e419c76](https://github.com/msgbyte/tianji/commit/e419c76794d58116e77bcab88fb4731e71cac1c8)) + +## [1.24.22](https://github.com/msgbyte/tianji/compare/v1.24.21...v1.24.22) (2025-08-24) + +### Others + +* update default tianji container reporter mode to silent mode ([ca76a4f](https://github.com/msgbyte/tianji/commit/ca76a4f30f873c62aefa8ef49d505981568061ee)) + +## [1.24.21](https://github.com/msgbyte/tianji/compare/v1.24.20...v1.24.21) (2025-08-24) + +### Features + +* **reporter:** add silent mode to suppress logs for cleaner output ([1d15ce4](https://github.com/msgbyte/tianji/commit/1d15ce4db78d2f0260054d77536308d306366f3b)) + +### Others + +* add send reasoning ([b4cc15e](https://github.com/msgbyte/tianji/commit/b4cc15eee80ef888843f5301be8f95ee12077bc3)) +* update cronjob sync batch size ([b5877c0](https://github.com/msgbyte/tianji/commit/b5877c076a3585906100625ca493382fae3c322d)) + +## [1.24.20](https://github.com/msgbyte/tianji/compare/v1.24.19...v1.24.20) (2025-08-22) + +### Features + +* add Carousel and HoverCard components for enhanced UI interactions ([38db0cb](https://github.com/msgbyte/tianji/commit/38db0cbff0888bca2b40b0d85120d83457df44f4)) + +## [1.24.19](https://github.com/msgbyte/tianji/compare/v1.24.18...v1.24.19) (2025-08-22) + +### Features + +* add ai-elements ui components ([ff183c0](https://github.com/msgbyte/tianji/commit/ff183c0e2e86f75e49eb151afb74c8b9b80a0471)) +* add basic ai charts analyze and add rich display support for render ([a309bc9](https://github.com/msgbyte/tianji/commit/a309bc931878c529cf89afe39f43dc40ba980a21)) +* add empty state for insight warehouse ([20b5c24](https://github.com/msgbyte/tianji/commit/20b5c24b6afe3468a6a22b9b36929172621f6739)) +* add WarehouseChartBlock component for visualizing warehouse data and integrate it into the insights page ([e06e6f9](https://github.com/msgbyte/tianji/commit/e06e6f9f65cdbc0e53ad62b2a20473e78edd2a55)) +* update API route from /api/ai/ to /api/insights/ and enhance error response for development mode ([198dd1c](https://github.com/msgbyte/tianji/commit/198dd1caf1562965498457b47234c0d0d9e8e3fc)) +* update API routes for insights and add authentication middleware ([57baa63](https://github.com/msgbyte/tianji/commit/57baa6300ca08df1d24ae7ad79ab9bbe3395a096)) + +### Bug Fixes + +* resolve some clickhouse instance not support transaction issue ([5dd56cf](https://github.com/msgbyte/tianji/commit/5dd56cf87e799bfcd89bbac8d4414eb53db3f1da)) + +### Others + +* add sync controller ([419761c](https://github.com/msgbyte/tianji/commit/419761ce63c196c54700ea0a1f378a3ff6637198)) +* add translation ([15ff07f](https://github.com/msgbyte/tianji/commit/15ff07f4f323286876075ee02e2893160ce84c05)) +* clean up imports and improve class order in message components; add sleep utility function ([38d48a1](https://github.com/msgbyte/tianji/commit/38d48a19ad944ea064a19115ded65967abce5ca1)) +* improve display for result ([169f031](https://github.com/msgbyte/tianji/commit/169f0317fe7e30936db62ef65b7eaee6a033daae)) +* improve table context ([27de587](https://github.com/msgbyte/tianji/commit/27de587810613cd6d100bdd32c5439cb7e642a77)) + +## [1.24.18](https://github.com/msgbyte/tianji/compare/v1.24.17...v1.24.18) (2025-08-19) + +### Features + +* add optional session id for long table ([5ba38da](https://github.com/msgbyte/tianji/commit/5ba38da21c588dcf3b02bbb2e6ce9295b5736308)) +* add title for survey download button which can improve survey usage ([15515a8](https://github.com/msgbyte/tianji/commit/15515a8282cb646b7b6950c0a755dd47cc9623f1)) + +### Document + +* update robots.txt content ([eeb241c](https://github.com/msgbyte/tianji/commit/eeb241c29d0538aa205e7ddcf2e4631ebd34d690)) + +### Others + +* add fallback operator to support custom field ([5238b36](https://github.com/msgbyte/tianji/commit/5238b3642c12971dccb79d0f88d75ae7cf9d4976)) +* improve type handling and simplify conditionals in insights components ([badf59d](https://github.com/msgbyte/tianji/commit/badf59d58977ba6f315ba40306e62537dc09fa4a)) +* reset insight params after change insight target ([9e01059](https://github.com/msgbyte/tianji/commit/9e01059d33c7bdfc0ef0c613dd65d1b818a631be)) +* upgrade version of dev dependency to resolve vulnerabilities ([9d022fb](https://github.com/msgbyte/tianji/commit/9d022fb896c6f72d60f958c6abbcf9484a56e733)) + +## [1.24.17](https://github.com/msgbyte/tianji/compare/v1.24.16...v1.24.17) (2025-08-18) + +### Features + +* add download buttion for insights table ([abebd62](https://github.com/msgbyte/tianji/commit/abebd6206382e11069b9a19c69dfb2a3362a9b13)) + +### Document + +* update llms file generator ([ac07c8c](https://github.com/msgbyte/tianji/commit/ac07c8c773e27a1aa6b5a2c23a3b364a52731b02)) + +## [1.24.16](https://github.com/msgbyte/tianji/compare/v1.24.15...v1.24.16) (2025-08-18) + +### Features + +* add warehouse sql page ([1b250f5](https://github.com/msgbyte/tianji/commit/1b250f5023e73979149249d1834b3b85f0146934)) + +### Bug Fixes + +* fix a bug which will cause loop fetch for survey ([cb9688c](https://github.com/msgbyte/tianji/commit/cb9688c1b5ae7ada9a7a423f2992824b22cf06b7)) +* update fallback in FilterParamsOperator to return string operators ([35b3411](https://github.com/msgbyte/tianji/commit/35b3411ad618d466470b16ee985a1ba1e46ffbbd)) + +### Document + +* add new blog post ([108b38d](https://github.com/msgbyte/tianji/commit/108b38d99f4e7bd882e9cbf8bf51b69e06a5d55a)) + +## [1.24.15](https://github.com/msgbyte/tianji/compare/v1.24.14...v1.24.15) (2025-08-16) + +### Features + +* add cohorts workspace id support for warehouse and some design ([3c60859](https://github.com/msgbyte/tianji/commit/3c608592fec188edc0c5c458a6054b326d922597)) +* add simple retention query ([3309c9b](https://github.com/msgbyte/tianji/commit/3309c9b0077397866d8948d51bb3cad22101f743)) +* allow user modify up / down message template ([f3678b0](https://github.com/msgbyte/tianji/commit/f3678b0b4c3e5a8fab23f7e57a21317e03a3619b)) + +### Bug Fixes + +* fix color issue of simple world map ([df1340c](https://github.com/msgbyte/tianji/commit/df1340cf869d6c088ce3dfc8b3e9767d4b4665a8)) + +### Document + +* [#216](https://github.com/msgbyte/tianji/issues/216) fix website openapi doc issue ([b5a59ee](https://github.com/msgbyte/tianji/commit/b5a59ee400d03a081b6d0111debb052a2bce8d42)) +* add feed documents ([e9a3796](https://github.com/msgbyte/tianji/commit/e9a3796470b96cdd5d2b27840f29f49e9048ada4)) +* add lastUpdatedAt support for route ([b38b500](https://github.com/msgbyte/tianji/commit/b38b500c0a51293edba7b18560bcb09742a7beff)) +* add new blog post on cost-aware observability to optimize cloud spending while maintaining SLOs ([a5c84f2](https://github.com/msgbyte/tianji/commit/a5c84f27dd50dd75b731cb286f617dd9d8520abd)) +* add new blog post on runbook automation connecting detection, diagnosis, and repair into a closed loop ([19352a2](https://github.com/msgbyte/tianji/commit/19352a287442530db47a52a29edf9c298f32e4e6)) +* add some blog for SEO ([7949a21](https://github.com/msgbyte/tianji/commit/7949a2156c5433012cbe8ba603433f69f1274a14)) +* try to resolve vercel build issue for sitemap ([acc45a1](https://github.com/msgbyte/tianji/commit/acc45a145e6545faee85f2b130185e1d038f54a3)) +* update i18n support for docs ([c8916bc](https://github.com/msgbyte/tianji/commit/c8916bca8e1c1f8d48d3e6afe2c596ebe5173d4d)) +* update robot config which allow /api route ([c3c13e1](https://github.com/msgbyte/tianji/commit/c3c13e14d2c837831748178e021825af6f764e07)) + +## [1.24.14](https://github.com/msgbyte/tianji/compare/v1.24.13...v1.24.14) (2025-08-11) + +## [1.24.13](https://github.com/msgbyte/tianji/compare/v1.24.12...v1.24.13) (2025-08-11) + +### Features + +* add 3-day date range selection option in DateRangeSelection component ([b5f7c12](https://github.com/msgbyte/tianji/commit/b5f7c12c2a9f56561646624d1dfc7d427959f809)) +* add event name field and filter support in WarehouseInsightsSqlBuilder ([88940e2](https://github.com/msgbyte/tianji/commit/88940e248707806221afd666c0f4d554c1d629fd)) +* add event names fetcher ([8737a34](https://github.com/msgbyte/tianji/commit/8737a34db82da6364e11e4dcdc5d475995fc7d66)) +* add insights event handling with warehouse wide table application ([13e3fb8](https://github.com/msgbyte/tianji/commit/13e3fb8c296768e18ff6b2fdd9cc0953f0282030)) +* add insightsWarehouseFilterParams function to fetch distinct event parameters ([47f3506](https://github.com/msgbyte/tianji/commit/47f3506f7d508fa29168dff37a5f8cb2ca383bf6)) +* add long table query events logic and improve display of event list page ([9a81ec9](https://github.com/msgbyte/tianji/commit/9a81ec921f2898a45b1900a1ce41718d4a1158b3)) +* add skipBatch context to TRPC queries in insights components ([c2df195](https://github.com/msgbyte/tianji/commit/c2df19512b8347b00e4e059aab3e29fd60f60b9f)) +* add specify feature for events ([0d3c841](https://github.com/msgbyte/tianji/commit/0d3c8414864724cdf4aaf1076a2169a5e4125146)) +* add warehousecohorts db model ([6f2f15d](https://github.com/msgbyte/tianji/commit/6f2f15d41907e116dfc68a61167d9b8cb9eb57c2)) +* add wide table query support ([b8fa898](https://github.com/msgbyte/tianji/commit/b8fa8988344b3b59a31eabe691fa7646456c277f)) + +### Others + +* reduce intro of raw and sql from prisma ([37b0640](https://github.com/msgbyte/tianji/commit/37b064098023fdb49cdbf06e4e4700629711fd65)) +* rename insight file path ([11bbf01](https://github.com/msgbyte/tianji/commit/11bbf0164ca09a59bfbbb1e43f15d36cf4d85f1a)) +* rename warehouse to warehouse long table and extract some logic ([476553b](https://github.com/msgbyte/tianji/commit/476553b67c5e9fb3f5f94b771c1d848fbc71e08c)) + +## [1.24.12](https://github.com/msgbyte/tianji/compare/v1.24.11...v1.24.12) (2025-08-06) + +## [1.24.11](https://github.com/msgbyte/tianji/compare/v1.24.10...v1.24.11) (2025-08-06) + +## [1.24.10](https://github.com/msgbyte/tianji/compare/v1.24.9...v1.24.10) (2025-08-05) + +### Features + +* add basic warehouse insight query ([d107f76](https://github.com/msgbyte/tianji/commit/d107f767072652867d5e662d841309c137ed3ef7)) +* add logId filter support for AIGatewayLogTable ([d59675a](https://github.com/msgbyte/tianji/commit/d59675a9d236ea834e77e67ec5117d227c02e793)) +* add optimized date based query support ([5bfd8dd](https://github.com/msgbyte/tianji/commit/5bfd8ddabe60a47769c11a568ea6a87efd668bdf)) +* add query time tracking for insights feature ([465569f](https://github.com/msgbyte/tianji/commit/465569f2c7a0d5fbf482d4fc1d163fbeb9f48507)) +* add warehouse view ([3abe951](https://github.com/msgbyte/tianji/commit/3abe95102ace6eb2eeba0440b5da976acc96a53e)) + +### Others + +* add LoadingView component and integrate it into VirtualizedInfiniteDataTable for improved loading state handling ([48ee26b](https://github.com/msgbyte/tianji/commit/48ee26bc9ba22fdd5bc423da9b396137af930707)) +* extract SearchInput component ([dfd79c6](https://github.com/msgbyte/tianji/commit/dfd79c6cc78585630d490597841f13a6bb217d78)) +* integrate LoadingView component into website detail view ([c6d386a](https://github.com/msgbyte/tianji/commit/c6d386a53fa41f4f97502fb9102cab8e89ad33dd)) +* translation ([68e00c2](https://github.com/msgbyte/tianji/commit/68e00c2869dc7b2a7cb8d8929758263be840c8c6)) +* update translation ([6ef8b96](https://github.com/msgbyte/tianji/commit/6ef8b96f0f9c25a1fb1c05d5e3bd27b1651d9b5c)) + +## [1.24.9](https://github.com/msgbyte/tianji/compare/v1.24.8...v1.24.9) (2025-08-02) + +### Features + +* add cron worker support ([c794d42](https://github.com/msgbyte/tianji/commit/c794d4222b5769d7f6bf868beb7761cb921a8ba5)) +* add react simple map for website detail ([ab8a6b8](https://github.com/msgbyte/tianji/commit/ab8a6b8d7e1f2e197c211b53430dc62f21d1b50a)) + +### Bug Fixes + +* fix a bug which survey and aigateway insight query not work ([d3c10ee](https://github.com/msgbyte/tianji/commit/d3c10ee2ea5d06a0f8c163ed4f757d33653c3b4e)) +* fix layout issue about filter section in vertical mode ([c429a12](https://github.com/msgbyte/tianji/commit/c429a125dd5323160ec52d72b107721698fee100)) + +### Others + +* add aigateway alias which maybe more better for gateway using api ([3c214a9](https://github.com/msgbyte/tianji/commit/3c214a973a7eec449429cf14aa445e25c5910b79)) +* add translation ([6781f6f](https://github.com/msgbyte/tianji/commit/6781f6f5c22edc3b7fb016a8d4c3105e66a678a4)) +* extract buildOpenAIHandler to model folder ([648fa2e](https://github.com/msgbyte/tianji/commit/648fa2ea472f84448b8831449d6ff6efcec27129)) +* optimize ChartTypeSelection component by consolidating chart type definitions and improving icon retrieval ([6cf85f7](https://github.com/msgbyte/tianji/commit/6cf85f74d96fcbf98bb5608096297b807b8aeb01)) + +## [1.24.8](https://github.com/msgbyte/tianji/compare/v1.24.7...v1.24.8) (2025-07-29) + +### Bug Fixes + +* fix type check [#213](https://github.com/msgbyte/tianji/issues/213) ([06ab175](https://github.com/msgbyte/tianji/commit/06ab17595012a0ea1f73b22652d72491a1f56f0e)) + +## [1.24.7](https://github.com/msgbyte/tianji/compare/v1.24.6...v1.24.7) (2025-07-27) + +### Features + +* add code testing functionality in worker edit form ([849d51b](https://github.com/msgbyte/tianji/commit/849d51bf530748ee2ad85cd2b3553c2f9a41d201)) +* add country and ip for server ([374a302](https://github.com/msgbyte/tianji/commit/374a302f25d36db336db1a658b256565d15e903f)) +* add env switch for worker which can control permission and risk ([c926c5e](https://github.com/msgbyte/tianji/commit/c926c5e89326993fdaec0fa024fd44b2f17b5f7d)) +* add fullscreen modal for code editor in worker edit form ([5a7a2ed](https://github.com/msgbyte/tianji/commit/5a7a2ed7490e7c527b2b8772046ea5ab60011112)) +* add UrlParamsInput component for handling URL parameters ([9a29c48](https://github.com/msgbyte/tianji/commit/9a29c488aaf96d325d4b9cedb654f95ea281523d)) +* add verbose logging option to display full payload content or its length ([a62aa22](https://github.com/msgbyte/tianji/commit/a62aa2242397b50569bfad2242f8cdf6ee23ce2c)) + +### Document + +* update openapi schema ([bf1c684](https://github.com/msgbyte/tianji/commit/bf1c6841afc9c7ab65c93fccb069e81015cd532b)) + +### Others + +* update background of worker preview ([38ef5ae](https://github.com/msgbyte/tianji/commit/38ef5aeb550533b5a5489fce5cb05cc1c6c0eadc)) +* update model prices and context window configurations in JSON files ([8a8855c](https://github.com/msgbyte/tianji/commit/8a8855cecc9abbda3eee39dde5b06bf865a155ec)) +* update UrlParamsInput component to improve layout and add overflow handling ([1a085a6](https://github.com/msgbyte/tianji/commit/1a085a6d43b20d0c59c3f4c6d92ef34c901d6ea8)) + +## [1.24.6](https://github.com/msgbyte/tianji/compare/v1.24.5...v1.24.6) (2025-07-22) + +### Others + +* fix ci problem ([054f831](https://github.com/msgbyte/tianji/commit/054f831405245de5472c4f2203671def394d0bb8)) + +## [1.24.5](https://github.com/msgbyte/tianji/compare/v1.24.4...v1.24.5) (2025-07-21) + +### Features + +* add function worker feature ([58f8eca](https://github.com/msgbyte/tianji/commit/58f8eca551933f58186d42a6c39ecd351dc925de)) +* add request payload handle for worker ([24e4fdc](https://github.com/msgbyte/tianji/commit/24e4fdc6f54f488761a0ea60da37d889afbb2add)) +* add worker API endpoint ([6f34f37](https://github.com/msgbyte/tianji/commit/6f34f37bd4bb7b71acb25e4bf5ee9eab45079cbe)) +* implement pagination for worker executions and enhance preview functionality ([d256e5c](https://github.com/msgbyte/tianji/commit/d256e5c706a95024f237d960417e5d2fd856124b)) +* refactor worker routes and add edit functionality with new components ([334484d](https://github.com/msgbyte/tianji/commit/334484d3d863973390f910a21c760773b27e85ef)) + +### Others + +* add pagination component ([558e7cf](https://github.com/msgbyte/tianji/commit/558e7cfd0fe2755917b11ec7bfb5218abda8d3b4)) +* update CPU time display from milliseconds to microseconds for better precision ([6e6a10e](https://github.com/msgbyte/tianji/commit/6e6a10e3e390d9e117313aa1b7e32f536c616f61)) + +## [1.24.4](https://github.com/msgbyte/tianji/compare/v1.24.3...v1.24.4) (2025-07-20) + +### Others + +* clean logs ([c3fe3a8](https://github.com/msgbyte/tianji/commit/c3fe3a8d2d4dd3dbb9688b4ea24e9740466c794b)) +* update chart type labels for better clarity and add missing translations ([fc31946](https://github.com/msgbyte/tianji/commit/fc319462358ad2a122b6ec3b6140c0096d6c65aa)) + +## [1.24.3](https://github.com/msgbyte/tianji/compare/v1.24.2...v1.24.3) (2025-07-19) + +### Others + +* add amd64 support for alpine release ([8df7071](https://github.com/msgbyte/tianji/commit/8df707160276771c76e0e16c5c3e3c365fee3178)) + +## [1.24.2](https://github.com/msgbyte/tianji/compare/v1.24.1...v1.24.2) (2025-07-19) + +### Features + +* add monitor manual trigger ([4db5d04](https://github.com/msgbyte/tianji/commit/4db5d041128bd6b8897646ded3b5ab3dac7ae670)) +* reporter build add alpine support ([07af814](https://github.com/msgbyte/tianji/commit/07af814b7f8e0b05be899d20896f447004148e34)) + +## [1.24.1](https://github.com/msgbyte/tianji/compare/v1.24.0...v1.24.1) (2025-07-18) + +### Features + +* add batch endpoint for website events ([44d45b2](https://github.com/msgbyte/tianji/commit/44d45b2d59558d7f6871b928813b69aaaf22f131)) +* add pure website tracking functions and update existing tracker to utilize them ([0662362](https://github.com/msgbyte/tianji/commit/0662362f29d2172e8b481590e094e778bbd65f48)) +* add WebsiteVisitorMap route and enhance map components with full-screen support ([fc6266f](https://github.com/msgbyte/tianji/commit/fc6266fffd3c776c2b43a9692adffd96786b916f)) +* implement batch request handling for website events and enhance tracking options ([38bd5e9](https://github.com/msgbyte/tianji/commit/38bd5e9c26f9b7bbd6671e356cdacee1f606a5a8)) + +### Bug Fixes + +* fix a bug which insight can not good handle timezone problem when in different timezone ([a20cb7a](https://github.com/msgbyte/tianji/commit/a20cb7ae5f6231d944319924e382317568fd52ac)) + +### Document + +* update changelog page ([e06d1b5](https://github.com/msgbyte/tianji/commit/e06d1b506c7b9a5457ac34e554dabdb03488709e)) + +### Others + +* add TypeScript tracker implementation and update build entry point ([fcc7f94](https://github.com/msgbyte/tianji/commit/fcc7f94b883ee257e91611946607c3f0ee4bc51d)) +* redesign changelog page ([658c045](https://github.com/msgbyte/tianji/commit/658c045ed1828a4e9067e3b1b7e22947043d6317)) +* simplify insightsAIGateway function by utilizing processGroupedTimeSeriesData ([58369d4](https://github.com/msgbyte/tianji/commit/58369d4d6a2d17a198cf8651cfa5bd64b3684f11)) +* update model prices ([46ba80d](https://github.com/msgbyte/tianji/commit/46ba80db81d478c6be1940ae62391c11c052c2ed)) +* update tracking functions to use new website event reporting methods ([2494227](https://github.com/msgbyte/tianji/commit/2494227933b1fe477d8d34755a3fab1d2ebad07f)) + +## [1.24.0](https://github.com/msgbyte/tianji/compare/v1.23.5...v1.24.0) (2025-07-15) + +### Features + +* add ClickHouse health check manager and integrate it into insights query handling ([b755b63](https://github.com/msgbyte/tianji/commit/b755b632424f258e1b0994161bcfd2cf7727c194)) +* add clickhouse infrastructure ([23ea9c1](https://github.com/msgbyte/tianji/commit/23ea9c1da2965ba40f686343bb5769812b71c50b)) +* add clickhouse insights support ([4fdfacd](https://github.com/msgbyte/tianji/commit/4fdfacdef9fe0241ae90e04e033e331b7dd23b63)) +* implement transaction handling in migration process and improve SQL query parameterization ([bd1a866](https://github.com/msgbyte/tianji/commit/bd1a866eb8d5ca0ec302a759e15b48e31953c052)) + +### Bug Fixes + +* fix a bug which last_sync_timestamp incorrect ([6ac7250](https://github.com/msgbyte/tianji/commit/6ac7250f0f17b831c92f2760a859cd66e387939c)) + +### Others + +* add CLICKHOUSE_DISABLE_SYNC environment ([8de9899](https://github.com/msgbyte/tianji/commit/8de9899d1c4b18be46f2f6abe09195bd6a79f9a7)) +* add sdk environment check before run browser only logic ([407ab74](https://github.com/msgbyte/tianji/commit/407ab74e1721042b38d98aa6ca06f6c139c158c3)) +* change client sdk tracker path ([83c50bb](https://github.com/msgbyte/tianji/commit/83c50bbbd45578b1157670511c86ca484ee4efb9)) +* **example app:** add Tianji tracking functionality with page view simulation and user session management ([0eeb672](https://github.com/msgbyte/tianji/commit/0eeb6722919ab7411b9e6446807c1d604a6331e4)) +* simplify SQL query construction for ClickHouse and PostgreSQL ([1ade1fb](https://github.com/msgbyte/tianji/commit/1ade1fb637a432e3b580ee2cf42561f0564c66fe)) +* update ClickHouse env examples ([f0ff7cf](https://github.com/msgbyte/tianji/commit/f0ff7cf34f1cfeeeac732555f4bc52c0c66082de)) +* update dependencies and bump docker version ([7d007b5](https://github.com/msgbyte/tianji/commit/7d007b5cbbcc866cfca4c1600c4bd8cd3bd3e039)) + +## [1.23.5](https://github.com/msgbyte/tianji/compare/v1.23.4...v1.23.5) (2025-07-13) + +### Features + +* add direction prop to FilterParamsBlock and update FilterSection for horizontal layout ([ed17bbb](https://github.com/msgbyte/tianji/commit/ed17bbb3c501ab97c373762f62218224ba1bf686)) +* enhance event display with object serialization and custom collapse icon ([a7e5efa](https://github.com/msgbyte/tianji/commit/a7e5efa88ed01bb0e9cffb6f16bfeb3468580b55)) + +### Bug Fixes + +* fix insights event feature session data incorrect problem ([2c2b570](https://github.com/msgbyte/tianji/commit/2c2b5704c5dd10a85e0f21e6c4756e44a0d6fe53)) + +### Others + +* fix filter not work issue in fetch event ([107de00](https://github.com/msgbyte/tianji/commit/107de0006a7e6101be2d6590dc61fa1844ebeb35)) + +## [1.23.4](https://github.com/msgbyte/tianji/compare/v1.23.3...v1.23.4) (2025-07-10) + +### Features + +* upgrade go runtime and upgrade gopsutil version and add process command support ([a17a4b4](https://github.com/msgbyte/tianji/commit/a17a4b44e956ae8f0cd43d0c940ec7f38f28f6e3)) + +## [1.23.3](https://github.com/msgbyte/tianji/compare/v1.23.2...v1.23.3) (2025-07-09) + +### Features + +* add bar chart support ([a6839c3](https://github.com/msgbyte/tianji/commit/a6839c346ddc6166aea26e98b0c91cbdb4588331)) +* add default and max result limits for insights queries to avoid performance issues ([47eb27e](https://github.com/msgbyte/tianji/commit/47eb27e4cdf812f21380fea73ee916adef208d14)) +* add pie chart view type ([f9e7c26](https://github.com/msgbyte/tianji/commit/f9e7c2614c553ec7708fecb9507eb683871ce9af)) +* add server timezone options in global config ([ec3f5ac](https://github.com/msgbyte/tianji/commit/ec3f5ac8d4b88a17cffecc8eb48ff477cc9ac541)) +* add timezone support for insights components and add presist date setting ([0bf04a2](https://github.com/msgbyte/tianji/commit/0bf04a2f07bc0519d82918403eee0ba618eadbbf)) + +### Document + +* update yandex verify html ([8cf94d9](https://github.com/msgbyte/tianji/commit/8cf94d9d59f80977fea0dec7f4514e610863e043)) + +### Others + +* improve display for table view ([e6bd1e3](https://github.com/msgbyte/tianji/commit/e6bd1e30932ef5ed204f4d39a9b92f4bcdc1a92a)) +* remove ai category which should not filter or group ([000253f](https://github.com/msgbyte/tianji/commit/000253fd20bc6b38efbbbc6d71cd837f69caa7f0)) + +## [1.23.2](https://github.com/msgbyte/tianji/compare/v1.23.1...v1.23.2) (2025-07-07) + +### Features + +* add more group support for insightsSurveyBuiltinFields ([eac0c65](https://github.com/msgbyte/tianji/commit/eac0c65d905ff5aede93c64c915cd27e5992b433)) + +## [1.23.1](https://github.com/msgbyte/tianji/compare/v1.23.0...v1.23.1) (2025-07-07) + +### Features + +* add in list filter support in string operator ([e68cc15](https://github.com/msgbyte/tianji/commit/e68cc150d35d254f0655d10c19e6e23906b29a79)) + +### Bug Fixes + +* fix a problem which can not support insight input and save ([9ce3a8d](https://github.com/msgbyte/tianji/commit/9ce3a8d4ff4a02d09eb968776511381c55025935)) + +### Others + +* translation ([b98c630](https://github.com/msgbyte/tianji/commit/b98c6302706613c897aac89ff89bf9ec763e858d)) + +## [1.23.0](https://github.com/msgbyte/tianji/compare/v1.22.6...v1.23.0) (2025-07-06) + +### Features + +* add server status option for status pages ([f281d1d](https://github.com/msgbyte/tianji/commit/f281d1d8b30b0427c6447784edb49f92446b5a55)) +* add tipicon which tell user how to use api key ([174d1df](https://github.com/msgbyte/tianji/commit/174d1df781e8d4f8ad5a674cc609c8d7135671e8)) +* add tooltip of user invitation which SMTP maybe not config [#180](https://github.com/msgbyte/tianji/issues/180) ([c721595](https://github.com/msgbyte/tianji/commit/c721595a876e1c42044678a2f4f946c943924a3b)) + +### Bug Fixes + +* fix tooltip can only display part issue ([d9678f3](https://github.com/msgbyte/tianji/commit/d9678f3481d3d12b814e40c29810784ed8d86d64)) + +### Others + +* improve display for server status page ([4513026](https://github.com/msgbyte/tianji/commit/4513026a63fcc143d5adf14ef9d3449cf818ad47)) + +## [1.22.6](https://github.com/msgbyte/tianji/compare/v1.22.5...v1.22.6) (2025-07-05) + +### Features + +* add multiselectpopover logic which support filter reference value ([4b11135](https://github.com/msgbyte/tianji/commit/4b111352202e29a4a7c79498750a644698ee4e4e)) +* **insights:** add filterParamValues query for website and survey insights ([7d7e65e](https://github.com/msgbyte/tianji/commit/7d7e65e8e493e402b9825000529c5342c3bb385a)) +* **status-page:** toggle detail display ([5a3a336](https://github.com/msgbyte/tianji/commit/5a3a336ec5a79e89a3cca64dce4c57ea158b5bac)) + +### Bug Fixes + +* fix survey create page can not scroll if form is too large ([96d9c79](https://github.com/msgbyte/tianji/commit/96d9c79f31023ff94539ca1f7b2dbdf7cda5f4ec)) + +### Document + +* add kubernetes reporter daemonset ([80d5b7f](https://github.com/msgbyte/tianji/commit/80d5b7fae6569c6ffcef17e112f38c1d12b5ae0f)) +* add translation file note ([2e4da45](https://github.com/msgbyte/tianji/commit/2e4da453c420f1ccab13c820bf1958618954b565)) + +### Others + +* update valueFormatter to format numbers with locale ([a76890d](https://github.com/msgbyte/tianji/commit/a76890d9494df1443cfa093f9c0a097a64bf591b)) + +## [1.22.5](https://github.com/msgbyte/tianji/compare/v1.22.4...v1.22.5) (2025-06-28) + +### Features + +* add yAxisDomain to time event chart and improve history display ([235920a](https://github.com/msgbyte/tianji/commit/235920aee10ec10fc4b1c9ef664552304cff9c13)) +* enhance database configuration with debug and transaction options ([812bfe5](https://github.com/msgbyte/tianji/commit/812bfe571674f5ecf70044ea88d625faaf7f64b7)) +* **server-status:** implement caching for server status and history, update related functions to support async operations ([ca7cb73](https://github.com/msgbyte/tianji/commit/ca7cb738fbb83d963903cd277f8a658348c79676)) + +### Document + +* add document for traefik plugin ([1493d4a](https://github.com/msgbyte/tianji/commit/1493d4a1a386db3fa4267029344db667ae1889cb)) +* change website domain to tianji.dev ([30834aa](https://github.com/msgbyte/tianji/commit/30834aad73179bd5f642833428dee4a68e4f3f11)) +* move traefik with plugin document position ([422fbde](https://github.com/msgbyte/tianji/commit/422fbdec40b93a11eca463e5921fe6710cdc9160)) + +### Others + +* change docker start script, redirect tianji-reporter output to /dev/null for cleaner logs ([0034253](https://github.com/msgbyte/tianji/commit/0034253944015446f25d388b1e898a093c3b0763)) +* migrate traefik tianji plugin to independent repo ([0718edb](https://github.com/msgbyte/tianji/commit/0718edb39d9813eb3d1848efcacaf3369cae3a01)) +* update translation ([d7e94fc](https://github.com/msgbyte/tianji/commit/d7e94fca3ef27aa46bd413b4b5955ec3b1193168)) + +## [1.22.4](https://github.com/msgbyte/tianji/compare/v1.22.3...v1.22.4) (2025-06-25) + +### Features + +* add process stats support ([6079501](https://github.com/msgbyte/tianji/commit/607950178ce2d7f8beaeff5090a0e3485a3400e7)) +* add ServerCard and ServerCardView components for improved server monitoring display ([79a026e](https://github.com/msgbyte/tianji/commit/79a026e441d19d6366eb0c7540d577299a677904)) +* **server-status:** add history caching and chart ([4c9c938](https://github.com/msgbyte/tianji/commit/4c9c93852dab03a6996f72c0fe53f03d7a627896)) + +### Document + +* fix typos and env variable ([7ca6fff](https://github.com/msgbyte/tianji/commit/7ca6fff64a8da4601e78f6e31d4b5a9b99f8260d)) + +### Others + +* add translation ([9331c6a](https://github.com/msgbyte/tianji/commit/9331c6a349b45d708a6fbf73fa8076e492c34525)) +* fix ci issue ([257ecad](https://github.com/msgbyte/tianji/commit/257ecad0fe679087ca7c3350719030f6c6b62f75)) +* fix ci type issue ([d6328b4](https://github.com/msgbyte/tianji/commit/d6328b4435ece16186996cc08e551e3714886189)) +* scale timeout time to 10x which added more tolerance ([f6ba75e](https://github.com/msgbyte/tianji/commit/f6ba75ecef841193b869d82fbaf05ebec5c5498a)) + +## [1.22.3](https://github.com/msgbyte/tianji/compare/v1.22.2...v1.22.3) (2025-06-22) + +### Others + +* update server list header name to improve display ([76236e9](https://github.com/msgbyte/tianji/commit/76236e9bf0a8bac28800a72d836730bcde713c4d)) +* upgrade reporter version from 1.21.1 to 1.22.5 ([49f90af](https://github.com/msgbyte/tianji/commit/49f90afac845a9504bfacc6534d83d40f5051432)) + +## [1.22.2](https://github.com/msgbyte/tianji/compare/v1.22.1...v1.22.2) (2025-06-22) + +### Features + +* add start tianji container shell and which make tianji report can build in report self ([d420443](https://github.com/msgbyte/tianji/commit/d420443735a2fa0fb823ffff395d771123e2667e)) + +### Document + +* add document about docker monitoring ([c2354f6](https://github.com/msgbyte/tianji/commit/c2354f69ff317e822d5412ab82513e78f4becf95)) + +### Others + +* change run strategy of ai classify ([8923df3](https://github.com/msgbyte/tianji/commit/8923df34fadfcf5d47db73f5b01eabd2d8105150)) +* improve example app style ([60af300](https://github.com/msgbyte/tianji/commit/60af3005b6c17d9f989408c85288769153d7936b)) +* upgrade wrangler version ([ff3c4a5](https://github.com/msgbyte/tianji/commit/ff3c4a569d093737c07c07c27a4a5dfd861f0e0d)) + +## [1.22.1](https://github.com/msgbyte/tianji/compare/v1.22.0...v1.22.1) (2025-06-20) + +### Features + +* add recent suggestion category feature to Survey model and remove auto input suggestion category ([f2a8184](https://github.com/msgbyte/tianji/commit/f2a81847b0b515fa7f042429883f3a382ce087b3)) +* add Tianji plugin for Traefik [#142](https://github.com/msgbyte/tianji/issues/142) ([b1d97e9](https://github.com/msgbyte/tianji/commit/b1d97e9d27b5064330c9072c1e7ce5f9fd3327c4)) +* improve daily ai trigger logic which can use recentSuggestionCategory ([b302104](https://github.com/msgbyte/tianji/commit/b3021044c5e6e89cb2d0ac03ef66ef674c2fa16f)) +* intro new visitor map layer style which fit on black and light theme ([32e3fa1](https://github.com/msgbyte/tianji/commit/32e3fa15fa31c26daa65b1aceff8f3d8d6aa86b6)) + +### Bug Fixes + +* fix openapi swagger can not call self problem ([35f41de](https://github.com/msgbyte/tianji/commit/35f41de72c48ef78632b55a113c7478c2643d6b7)) + +### Others + +* improve openapi ui page which maybe more prettier ([213f366](https://github.com/msgbyte/tianji/commit/213f366afcb50860e194f3a09daf133118ee4500)) + +## [1.22.0](https://github.com/msgbyte/tianji/compare/v1.21.17...v1.22.0) (2025-06-18) + +### Bug Fixes + +* change env and fix a bug which OPENAI_ will exposing to other openai instance. ([2ff7f16](https://github.com/msgbyte/tianji/commit/2ff7f16ab1e7aef03f3049018bc3e5bf9bf9bc5f)) + +## [1.21.17](https://github.com/msgbyte/tianji/compare/v1.21.16...v1.21.17) (2025-06-18) + +### Bug Fixes + +* fix a bug which website event throw error if can not create session together ([0592355](https://github.com/msgbyte/tianji/commit/0592355841c41dec762fbfce6a673bd05aec83c7)) + +### Others + +* improve display for common list and ai gateway example ([afa4391](https://github.com/msgbyte/tianji/commit/afa439168cc27391343e25f29faccf2cd355bc6c)) + +## [1.21.16](https://github.com/msgbyte/tianji/compare/v1.21.15...v1.21.16) (2025-06-17) + +### Others + +* update lock file ([c8f5fb3](https://github.com/msgbyte/tianji/commit/c8f5fb3d2f15ac55a3b1bf8240f506ea1cb078d1)) + +## [1.21.15](https://github.com/msgbyte/tianji/compare/v1.21.14...v1.21.15) (2025-06-17) + +### Bug Fixes + +* fix update problem when switch page in admin which maybe not update data ([042cc8d](https://github.com/msgbyte/tianji/commit/042cc8ddf48a20efeb1404d659fa7a13a548c06a)) + +### Others + +* add more logs ([f39e7fb](https://github.com/msgbyte/tianji/commit/f39e7fb8b64c653a8a3904d5b177a8389a9d153e)) +* remove unused time markers in MonitorHTTPTiming ([2e1ebb8](https://github.com/msgbyte/tianji/commit/2e1ebb8730ad2e093b86cd2804c56287c887240e)) +* update translation ([4a6ea01](https://github.com/msgbyte/tianji/commit/4a6ea01559d005a6105026efa6ab61378e4feea3)) +* upgrade dependency version ([79467e2](https://github.com/msgbyte/tianji/commit/79467e2b807f873757e429cf6c56b0c3a739831f)) + +## [1.21.14](https://github.com/msgbyte/tianji/compare/v1.21.13...v1.21.14) (2025-06-16) + +### Features + +* add AIGatewaySummaryStats component and integrate it into AIGatewayOverview ([1a04d0a](https://github.com/msgbyte/tianji/commit/1a04d0a54920d0b05779ff38c05ae193e2406abe)) +* add FOCUS_CATEGORY support ([075e08d](https://github.com/msgbyte/tianji/commit/075e08d51523c2fa25384c11969e64a8d0181ba1)) +* add MonitorHTTPTiming component to display detailed HTTP request timing metrics in MonitorInfo ([a89ddbe](https://github.com/msgbyte/tianji/commit/a89ddbeb3dfd094b3158246a188c45c7afe461dd)) +* add timedFetch utility for HTTP requests with detailed timing metrics and integrate it into the monitor provider ([205cee6](https://github.com/msgbyte/tianji/commit/205cee69b03f1537f6ce26ded6e21fb92fae8945)) +* add tooltips to HTTP timing phases for better user guidance ([ad8c9fa](https://github.com/msgbyte/tianji/commit/ad8c9facdb2d015ee9c1794b0dc255850228c95a)) + +### Others + +* improve animation of timing component ([fa3cbce](https://github.com/msgbyte/tianji/commit/fa3cbceb73e24c9d7275bb48f2765c68683f29aa)) +* simplify MonitorHTTPTiming component structure and improve timing metrics display ([a01fb40](https://github.com/msgbyte/tianji/commit/a01fb40ecf3d76901e5be549d219cd509cfc3345)) +* update translation ([4f7e7de](https://github.com/msgbyte/tianji/commit/4f7e7de695c96c73fa037228581244119424ec78)) + +## [1.21.13](https://github.com/msgbyte/tianji/compare/v1.21.12...v1.21.13) (2025-06-13) + +### Features + +* add template support for ai translation worker which easy to translate mul-field ([7c7dbc3](https://github.com/msgbyte/tianji/commit/7c7dbc39d4ad71e83371f19e34ad50b00f773429)) + +### Others + +* update app store and Google Play reviews fetching to use async/await for better error handling ([81bf407](https://github.com/msgbyte/tianji/commit/81bf4071d943fe71330c8a144be8480d6cb8067b)) + +## [1.21.12](https://github.com/msgbyte/tianji/compare/v1.21.11...v1.21.12) (2025-06-11) + +### Bug Fixes + +* fix a bug which normal user can not see example ([6d607a9](https://github.com/msgbyte/tianji/commit/6d607a969011e3596b3b0017ee95062448a78960)) + +### Document + +* add installation link for Tianji MCP server ([250a1e7](https://github.com/msgbyte/tianji/commit/250a1e7a62d4f618c8a34ab2aa25633e7fca79f9)) +* remove unused section ([b39faf2](https://github.com/msgbyte/tianji/commit/b39faf20de2c062c11f94aaceacc5755b4711f75)) + +### Others + +* ai gateway add anthropic api ([79d403a](https://github.com/msgbyte/tianji/commit/79d403a938ab08a9614db4de127c6c1eceb026c9)) +* update email template style ([0a63cf3](https://github.com/msgbyte/tianji/commit/0a63cf3a8319947126a9cda1944718f668454f7e)) +* update model price and context window config ([a769d71](https://github.com/msgbyte/tianji/commit/a769d71b577a95bd68ae1a231c69fa14e1ce9d18)) + +## [1.21.11](https://github.com/msgbyte/tianji/compare/v1.21.10...v1.21.11) (2025-06-08) + +### Features + +* add application batch endpoint ([d1ff4e6](https://github.com/msgbyte/tianji/commit/d1ff4e6f7b78bf904e9e65d4bd1b1ba4c31057c1)) +* add custom mode setting feature ([9227ddb](https://github.com/msgbyte/tianji/commit/9227ddba714bc7efeffa48e8dbe4a02b8ff2ef70)) +* add manual refresh button to ai gateway logs ([b3eba5f](https://github.com/msgbyte/tianji/commit/b3eba5f2aeb9840d32ffa66f6746ae9db4db40d0)) +* add render function for nullable values in AIGateway log columns ([b7dd1d9](https://github.com/msgbyte/tianji/commit/b7dd1d92b4e03d168295032e2a0aeca62bc5bbc8)) + +### Others + +* improve style and improve code handle for calc cost ([bb14b5a](https://github.com/msgbyte/tianji/commit/bb14b5ae5b678c0f5477d6beecd163d0271aab12)) +* translation ([cac7519](https://github.com/msgbyte/tianji/commit/cac7519932234ddf09f00deb09981237947a05cb)) + +## [1.21.10](https://github.com/msgbyte/tianji/compare/v1.21.9...v1.21.10) (2025-06-05) + +### Document + +* add troubleshooting for websocket connection issues ([0db2013](https://github.com/msgbyte/tianji/commit/0db2013fc67de24a7f648698895dcb859d03c33f)) + +### Others + +* add AUTH_USE_SECURE_COOKIES env var to try to resolve ws and https mix problem ([605443f](https://github.com/msgbyte/tianji/commit/605443f907265c1f3301f8d450e0478dd41e62c5)) +* improve secure cookie handling in getAuthSession function ([00b6e2d](https://github.com/msgbyte/tianji/commit/00b6e2d6e53cd1bdda12be1e7e706ac3352b7e4a)) + +## [1.21.9](https://github.com/msgbyte/tianji/compare/v1.21.8...v1.21.9) (2025-06-04) + +### Others + +* add more context for debug ([f347440](https://github.com/msgbyte/tianji/commit/f3474406dd2f6d89995be022eed1607c6e9b1e54)) + +## [1.21.8](https://github.com/msgbyte/tianji/compare/v1.21.7...v1.21.8) (2025-06-04) + +### Others + +* add more logs ([00286ef](https://github.com/msgbyte/tianji/commit/00286efa68e11bbe0ca2115eda9205f4bc8ec43d)) + +## [1.21.7](https://github.com/msgbyte/tianji/compare/v1.21.6...v1.21.7) (2025-06-04) + +### Bug Fixes + +* handle case when CPU information is unavailable, returning 0.0 as fallback [#193](https://github.com/msgbyte/tianji/issues/193) ([80d1be4](https://github.com/msgbyte/tianji/commit/80d1be40d3f752675df9631677566f7775cd9733)) + +### Document + +* update env document ([9c0838e](https://github.com/msgbyte/tianji/commit/9c0838e39315caa5713f43605f6bc6058ffb4df1)) + +### Others + +* add more log for get auth session which maybe can help to debug ([5eb57f4](https://github.com/msgbyte/tianji/commit/5eb57f438785f167fe6b76d6e3ec81c67e28ae32)) + +## [1.21.6](https://github.com/msgbyte/tianji/compare/v1.21.5...v1.21.6) (2025-06-02) + +### Bug Fixes + +* improve JSON parsing and handling of quotes in user feedback ([b6368ba](https://github.com/msgbyte/tianji/commit/b6368baf0f470213c327d5d768e55c84955045a1)) + +## [1.21.5](https://github.com/msgbyte/tianji/compare/v1.21.4...v1.21.5) (2025-06-02) + +### Bug Fixes + +* try to fix quote bad case in sonnet which maybe more fit to real world case. ([08a8c5f](https://github.com/msgbyte/tianji/commit/08a8c5fc5382c937705f628dfcc71a13e24d3395)) + +## [1.21.4](https://github.com/msgbyte/tianji/compare/v1.21.3...v1.21.4) (2025-06-02) + +### Bug Fixes + +* try to handle sonnet problem in translation prompt ([e0587ac](https://github.com/msgbyte/tianji/commit/e0587acc4b04cb8827bc2aade9d2802b33840a11)) + +## [1.21.3](https://github.com/msgbyte/tianji/compare/v1.21.2...v1.21.3) (2025-06-01) + +### Features + +* add modelApiKey which can use same api key in one team ([43b1bbe](https://github.com/msgbyte/tianji/commit/43b1bbe9d2ee0868f66dbf9d7d61115c73e894e1)) +* add partial to save to database because translation task have more bad case to parse json format ([6a125d5](https://github.com/msgbyte/tianji/commit/6a125d54af8d78d56ce2c65321f96f610f69b62d)) + +### Document + +* add authentication and getting started documents in multiple languages ([917306a](https://github.com/msgbyte/tianji/commit/917306a78d17fd91955287fc5af8473417a405b9)) + +## [1.21.2](https://github.com/msgbyte/tianji/compare/v1.21.1...v1.21.2) (2025-05-30) + +### Bug Fixes + +* fix sonnet mode will output invalid json problem ([94ce294](https://github.com/msgbyte/tianji/commit/94ce294a8666811b5704f25bea328d54b69d0287)) + +### Document + +* add api related documents ([850b642](https://github.com/msgbyte/tianji/commit/850b642cead45beacaefe4e4f1dc54a7a447c580)) + +## [1.21.1](https://github.com/msgbyte/tianji/compare/v1.21.0...v1.21.1) (2025-05-30) + +### Features + +* add a function which make sure we can get json response from LLM output even mode not support json mode ([411c285](https://github.com/msgbyte/tianji/commit/411c285b536a20e2c14d906356962c73c8fa8a67)) +* add new routes and CommonPageEmpty component for various sections ([b5df979](https://github.com/msgbyte/tianji/commit/b5df979a92e8489bf15733b9c3882cce4c721dca)) +* wrap Empty component in DelayRender for improved loading experience in CommonPageEmpty ([fedb7f9](https://github.com/msgbyte/tianji/commit/fedb7f922bf91e56d85130f4badbbdefb1d323c4)) + +## [1.21.0](https://github.com/msgbyte/tianji/compare/v1.20.10...v1.21.0) (2025-05-25) + +### Features + +* add api manage page allow description editing ([48f14b6](https://github.com/msgbyte/tianji/commit/48f14b6b8c970a8d3dd273d6f71528a983e53c69)) +* add cron cron support to push monitor ([c23dff5](https://github.com/msgbyte/tianji/commit/c23dff5c699e92cfc5b74ccda5c7bd3816f83075)) +* add MonitorPushStatus component to display push monitor status ([211a7fa](https://github.com/msgbyte/tianji/commit/211a7fab1a28a5f9e3fe33eb52086657e4265ffa)) +* add more language example for push feature ([7a6fcbb](https://github.com/msgbyte/tianji/commit/7a6fcbbf057b8cee209176acda3cf1fd26ae322b)) +* add Prometheus counters for cron jobs and message queue operations ([0afcb3d](https://github.com/msgbyte/tianji/commit/0afcb3d987b915b6a9e494ec8fc3464dc6ddc77f)) +* add push monitor [#36](https://github.com/msgbyte/tianji/issues/36) ([2ca6487](https://github.com/msgbyte/tianji/commit/2ca6487f05e267308e8d34fb664765f9a99ea121)) +* add push token regeneration feature to MonitorPush component ([3b07de2](https://github.com/msgbyte/tianji/commit/3b07de24821cb90ded72d4f09cac0be603adad14)) +* integrate Stagewise toolbar for development environment ([303b799](https://github.com/msgbyte/tianji/commit/303b79941bdc1e6ea1e9c8263e55239464df975e)) + +### Document + +* add document about push monitor ([8210f74](https://github.com/msgbyte/tianji/commit/8210f74e0127b08e9890ffe91a1fec4d86ea35e9)) + +### Others + +* add badge SVG proxy in dev mode ([da0428c](https://github.com/msgbyte/tianji/commit/da0428c66b623863fd89acd64afd702e6eda919d)) +* add global rules and project structure documentation for Tianji ([e5c427c](https://github.com/msgbyte/tianji/commit/e5c427c78f815e790544693861118a58daaeb23d)) +* add more translation ([b21cd12](https://github.com/msgbyte/tianji/commit/b21cd123ccad10b52dee67acd7e78476e29ec1de)) +* update translation ([69f1a19](https://github.com/msgbyte/tianji/commit/69f1a197adbe4e48942cf4cf0ef2008c201d5575)) + +## [1.20.10](https://github.com/msgbyte/tianji/compare/v1.20.9...v1.20.10) (2025-05-21) + +### Features + +* add AIGatewayCodeExampleBtn component and update ai proxy proxy configuration ([a921e24](https://github.com/msgbyte/tianji/commit/a921e24ad7b6d95cee3a79ff0dd36aea603d227b)) +* add FeedState model and related functionality for managing feed states ([7347833](https://github.com/msgbyte/tianji/commit/734783310a61d2bc695dbd494ebe148e85575daf)) +* add FeedStateList component to display and manage feed states in the feed view ([645e3b1](https://github.com/msgbyte/tianji/commit/645e3b1c88e5d6b515302071ac7cbb153be7d2b5)) +* add insights events route ([51d949b](https://github.com/msgbyte/tianji/commit/51d949b4e31a4443fec7bfb85c3ecdabab78d4b8)) +* add queryEvents function to handle website event queries ([db27f51](https://github.com/msgbyte/tianji/commit/db27f515042117293d1edc576875600835d68367)) +* add state guide for feed api ([3050cfa](https://github.com/msgbyte/tianji/commit/3050cfa166a39b6ac9674a6949faaee0dc795622)) +* update queryEvents to support cursor pagination and refactor related functions ([e698c61](https://github.com/msgbyte/tianji/commit/e698c616d4586385d526af9ddd19a9f51af665f2)) + +### Others + +* move insight route ([78b21a4](https://github.com/msgbyte/tianji/commit/78b21a4d401e31310086bf828d1112f5b3f51d2c)) +* rename insights.events => insights.eventNames ([0a5346d](https://github.com/msgbyte/tianji/commit/0a5346df3c5b083d7dcd6cb1582302c12ea0efa1)) +* update dependencies and remove deprecated API files ([afad5f0](https://github.com/msgbyte/tianji/commit/afad5f08fc9086fff728567c66f2856a1ba6abe8)) + +## [1.20.9](https://github.com/msgbyte/tianji/compare/v1.20.8...v1.20.9) (2025-05-05) + +### Bug Fixes + +* fix website not support group filter problem ([4cc7fd0](https://github.com/msgbyte/tianji/commit/4cc7fd01309ed1079a7d9811f7e7cc3336b871f3)) + +### Others + +* prevent access logs in test environment ([5fe4205](https://github.com/msgbyte/tianji/commit/5fe4205a44bce2e6c5fb17c27c911407c00ba246)) + +## [1.20.8](https://github.com/msgbyte/tianji/compare/v1.20.7...v1.20.8) (2025-05-02) + +### Features + +* add responsive testimonial grid and tweet card styles ([80cbf2b](https://github.com/msgbyte/tianji/commit/80cbf2bb4c26a3b223502bb33a2671d6ec7ffc8f)) + +### Document + +* add robots.txt and LLM documentation files ([6da44ab](https://github.com/msgbyte/tianji/commit/6da44ab29cf29ae21fa9eb758fd3a48652761c9f)) +* update documents ([4dc3d6d](https://github.com/msgbyte/tianji/commit/4dc3d6df1e5bf72bd39d11763ae5a20f89b872ba)) + +### Others + +* improve insight debug logic ([31a9a33](https://github.com/msgbyte/tianji/commit/31a9a339cbb2030e44dc2d78f6fa1c8a3637a8b0)) +* update version to 1.3.1 and improve event tracking error handling ([755dc8f](https://github.com/msgbyte/tianji/commit/755dc8fd78a4116f39fd673e1ec9e12437f95ae5)) + +## [1.20.7](https://github.com/msgbyte/tianji/compare/v1.20.6...v1.20.7) (2025-04-19) + +### Features + +* add real-time update feature for AI Gateway logs ([080123f](https://github.com/msgbyte/tianji/commit/080123f039fb43cdcb086195b5c32acad84d04df)) + +### Bug Fixes + +* fix ai gateway chart label not correct issue ([a83ee66](https://github.com/msgbyte/tianji/commit/a83ee66874e67b7780ca8ed4fcbe61edd5b4564a)) + +## [1.20.6](https://github.com/msgbyte/tianji/compare/v1.20.5...v1.20.6) (2025-04-18) + +### Bug Fixes + +* restore timezone parameter in insights functions to improve date handling ([d3ee48e](https://github.com/msgbyte/tianji/commit/d3ee48e4c56171221f427f03003ebe1c0322f831)) + +## [1.20.5](https://github.com/msgbyte/tianji/compare/v1.20.4...v1.20.5) (2025-04-18) + +### Bug Fixes + +* fix a issue which maybe cause service crash ([08f3fe5](https://github.com/msgbyte/tianji/commit/08f3fe5705f9117d2907818cd6e798d36b140a47)) +* make timezone parameter optional in date-related functions ([e05a289](https://github.com/msgbyte/tianji/commit/e05a289b37ebc86b532ebd13074ef93f7662d938)) + +### Others + +* remove sitemap ([357c11c](https://github.com/msgbyte/tianji/commit/357c11c7af9ce76b909f7ac4cccdd55493324e27)) +* update docusaurus dependencies to version 3.7.0 and enable sitemap configuration ([681d50c](https://github.com/msgbyte/tianji/commit/681d50ca86a54fc2a8b6ea718745502eb0774d27)) + +## [1.20.4](https://github.com/msgbyte/tianji/compare/v1.20.3...v1.20.4) (2025-04-17) + +### Bug Fixes + +* remove timezone parameter from insights functions as return date is already in user timezone ([7cf38f3](https://github.com/msgbyte/tianji/commit/7cf38f3613bb4afc6cd9b6580b9a2d054a26bfe3)) + +## [1.20.3](https://github.com/msgbyte/tianji/compare/v1.20.2...v1.20.3) (2025-04-17) + +### Features + +* enhance aiGateway to support additional model prefixes for deepseek variants ([5f2db80](https://github.com/msgbyte/tianji/commit/5f2db80c2e91f1d13994b1a364acf9d1d7152dd3)) + +## [1.20.2](https://github.com/msgbyte/tianji/compare/v1.20.1...v1.20.2) (2025-04-17) + +### Bug Fixes + +* fix some problem with user timezone handling in AIGateway overview and enhance SQL date formatting validation ([734f716](https://github.com/msgbyte/tianji/commit/734f7162a618a1ed18f3f14a3e463aa558b54fc1)) + +## [1.20.1](https://github.com/msgbyte/tianji/compare/v1.20.0...v1.20.1) (2025-04-17) + +### Features + +* enhance AI Gateway with model price name handling and support for new models in context window ([3771375](https://github.com/msgbyte/tianji/commit/3771375d6a8d5a80f461a17396aa957ef414bab5)) + +### Bug Fixes + +* update email template and SMTP HTML to use 'Docs' instead of 'Documentation' ([041004c](https://github.com/msgbyte/tianji/commit/041004c369938483d0ed5a7be69649d2f42fb305)) + +### Document + +* Add ClawCloud Run Button ([797c519](https://github.com/msgbyte/tianji/commit/797c519991cec001c8976f86183eadea5d114e38)) + +## [1.20.0](https://github.com/msgbyte/tianji/compare/v1.19.7...v1.20.0) (2025-04-14) + +### Features + +* add AI Gateway feature with CRUD operations and logs ([69d7878](https://github.com/msgbyte/tianji/commit/69d78787b237a873134a6f639a391222fa5417c7)) +* add AI Gateway models, routes, and logging ([fe68b23](https://github.com/msgbyte/tianji/commit/fe68b2399a66f3a75377f0cbcf0705512ccde705)) +* add AIGateway support to insights query schema ([e1eacea](https://github.com/msgbyte/tianji/commit/e1eaceadbf35edcd2e5cc1f09d17246de24242a3)) +* add AIGateway-related translations and update chart colors ([b90d8ac](https://github.com/msgbyte/tianji/commit/b90d8ac24c11cbf63beed76edddcb14eaff9e78f)) +* add blog section and two new blog posts ([88c689f](https://github.com/msgbyte/tianji/commit/88c689fe7ced5ee618c991202f50dd4a11bad0b9)) +* add overview tab and insights support for AI Gateway ([ccf7bc7](https://github.com/msgbyte/tianji/commit/ccf7bc74176ae08e5b01a829862e67faca032601)) +* add price tracking for AI Gateway logs ([96b8996](https://github.com/msgbyte/tianji/commit/96b89963d219f451d6ed968ac1452b209c2c945e)) +* ai gateway add deepseek and openrouter support ([3a7dbc7](https://github.com/msgbyte/tianji/commit/3a7dbc71fdee11fe977919745a77f5e6b5864f29)) + +### Others + +* consolidate SQL query building logic into base class ([77f046a](https://github.com/msgbyte/tianji/commit/77f046a8e3c5c8ac59030959f2c8afdabfeabee8)) +* fix ci issue ([127f59a](https://github.com/msgbyte/tianji/commit/127f59aef7601803232af1159bf889b8e385b90f)) +* improve token calculation with model fallback ([748be17](https://github.com/msgbyte/tianji/commit/748be176f12cab08f1186eb5e06e25258f22222e)) +* replace dropdown with select component for date range ([55c303d](https://github.com/msgbyte/tianji/commit/55c303df10f4cb33306d1b1784e046bb8d6a8bc4)) +* update translation ([d1dfbbb](https://github.com/msgbyte/tianji/commit/d1dfbbb6d4db7faa337b7846bb581f99184993c9)) + +## [1.19.7](https://github.com/msgbyte/tianji/compare/v1.19.6...v1.19.7) (2025-04-06) + +### Features + +* add workspace invitation which allow user invite non-register user [#180](https://github.com/msgbyte/tianji/issues/180) ([32d52ae](https://github.com/msgbyte/tianji/commit/32d52aef5b23af3460e2e4d8f42b9b994669c02f)) + +### Document + +* add MCP integration and environment variable documentation ([aefb090](https://github.com/msgbyte/tianji/commit/aefb0900703053920877fe266004425fd22fe584)) +* add social icons ([a9bee2a](https://github.com/msgbyte/tianji/commit/a9bee2a3e9399775be3f74a472c96da7ff1893e7)) + +### Others + +* enhance email template and consolidate SMTP logic ([b2a384c](https://github.com/msgbyte/tianji/commit/b2a384cf0d8c23743e56c5c2ee75d56c81e4d35f)) + +## [1.19.6](https://github.com/msgbyte/tianji/compare/v1.19.5...v1.19.6) (2025-04-01) + +### Features + +* add value formatter and filesize for application compare ([17ebf2e](https://github.com/msgbyte/tianji/commit/17ebf2e6bf402fc39d89a4ee5feb2c5975242791)) +* create tianji mcp server ([36aaabc](https://github.com/msgbyte/tianji/commit/36aaabc91e1dbfe5ee6da1cb465a2647e99d19be)) +* insight add line chart type ([bf0022d](https://github.com/msgbyte/tianji/commit/bf0022d7438646ec913c69dbdf65839df47f8f7b)) + +### Bug Fixes + +* fix user api key not increment problem ([cf2b454](https://github.com/msgbyte/tianji/commit/cf2b454146651a5683306e12c50d350d923fdb78)) + +### Document + +* add dark-brand ([a2c5c98](https://github.com/msgbyte/tianji/commit/a2c5c98d093f15fcf1d7a4ed606547678fc0ffa1)) +* fix typo ([34d863e](https://github.com/msgbyte/tianji/commit/34d863efa8814ee0919d0f24fa33f4cf26ba18e8)) + +### Others + +* remove inappropriate translations ([0ffb3be](https://github.com/msgbyte/tianji/commit/0ffb3beb5ef3787b2a2f4a7f069d0676fb8f79cd)) + +## [1.19.5](https://github.com/msgbyte/tianji/compare/v1.19.4...v1.19.5) (2025-03-28) + +### Others + +* rollback pnpm version to 9.x because of isolated_vm [#181](https://github.com/msgbyte/tianji/issues/181) ([15d575d](https://github.com/msgbyte/tianji/commit/15d575d954627fbd4ea1b626c837cdff8962ab60)) + +## [1.19.4](https://github.com/msgbyte/tianji/compare/v1.19.3...v1.19.4) (2025-03-27) + +### Features + +* add line chart type support and adjust fill opacity ([0724d8c](https://github.com/msgbyte/tianji/commit/0724d8c6a4ef5d3946400758e083ee4887d7ab7e)) +* add react native sdk initApplication function and handle session version ([fdf4a05](https://github.com/msgbyte/tianji/commit/fdf4a05ff6ef432a2e9988626167e1e3a057cf08)) +* refactor insights SQL builders and update related tests ([ccf93cb](https://github.com/msgbyte/tianji/commit/ccf93cb6c6ee53d5ec9e0979310bef0f479b12f8)) + +### Others + +* rerun all translation with 4o ([e60136e](https://github.com/msgbyte/tianji/commit/e60136e0a0aee3f16e9d05306d178ef775e8e020)) +* update pnpm version to 10.6.5 ([176f4ed](https://github.com/msgbyte/tianji/commit/176f4edb05ae40f015d1d91db2eda9bb90a8ca24)) + +## [1.19.3](https://github.com/msgbyte/tianji/compare/v1.19.2...v1.19.3) (2025-03-25) + +### Features + +* add diff view in application compare chart ([c49152a](https://github.com/msgbyte/tianji/commit/c49152a1bc5bb895ccd76a4bd4db1231a75bc19e)) +* add http status code field which can treat other status code as normal. [#173](https://github.com/msgbyte/tianji/issues/173) ([1cacdeb](https://github.com/msgbyte/tianji/commit/1cacdeb7ec2b2d69f4b9f0fe69737c2a6e1451aa)) + +### Document + +* add environment document ([db654d3](https://github.com/msgbyte/tianji/commit/db654d32bb5f594d4ba15caaa24e73d49d669825)) + +### Others + +* update translation ([6d8b12e](https://github.com/msgbyte/tianji/commit/6d8b12e35e0426575be3f1057d9cbc403bad7028)) + +## [1.19.2](https://github.com/msgbyte/tianji/compare/v1.19.1...v1.19.2) (2025-03-23) + +### Features + +* add app store search and picker ([331153d](https://github.com/msgbyte/tianji/commit/331153df25889802975d6836f8bf67e28e10a400)) +* add application compare feature ([0f8a672](https://github.com/msgbyte/tianji/commit/0f8a6729048e2d93bb8cee076387609c929d48e2)) +* add store info history ([0473ba5](https://github.com/msgbyte/tianji/commit/0473ba5f2178af9a1c7a6752810e6dc288f869b8)) +* store detail tab add history chart ([63e9588](https://github.com/msgbyte/tianji/commit/63e9588aa5e29a2541ba45b6a3c07ba2afa2201d)) + +### Others + +* improve style ([8484e25](https://github.com/msgbyte/tianji/commit/8484e25e39509ed665200305c35763467b3ebe5b)) +* update translation ([6a21965](https://github.com/msgbyte/tianji/commit/6a21965f3d5bec5e66dab0e32716ee59809d9f09)) +* upgrade node version to 22.14.0, [#132](https://github.com/msgbyte/tianji/issues/132) ([48280c5](https://github.com/msgbyte/tianji/commit/48280c596d8dc9c6b1fbb22820e9a1075a7e7830)) + +## [1.19.1](https://github.com/msgbyte/tianji/compare/v1.19.0...v1.19.1) (2025-03-22) + +### Bug Fixes + +* fix a bug which will clause run custom code error ([9a132d9](https://github.com/msgbyte/tianji/commit/9a132d93a4584db2e441a2bd68b64c5b86de1b75)) + +### Document + +* add more language document for application tracking ([4ed139f](https://github.com/msgbyte/tianji/commit/4ed139fb9315a47288417914aa752842eba629b3)) +* update translation modify files in README.md ([3310a1c](https://github.com/msgbyte/tianji/commit/3310a1c00264067a7e62c7c67cc177c8e41f3a0c)) + +### Others + +* add more docker ignore claim to reduce docker image size ([aa29d18](https://github.com/msgbyte/tianji/commit/aa29d18d910a3a45ecfddde71838db771dd700f6)) + +## [1.19.0](https://github.com/msgbyte/tianji/compare/v1.18.22...v1.19.0) (2025-03-22) + +### Features + +* add application entry ([b9d6e4a](https://github.com/msgbyte/tianji/commit/b9d6e4a7745abcb059f9d6e9c1701d0855a86f8b)) +* add application event router ([da83fa9](https://github.com/msgbyte/tianji/commit/da83fa9d28d4d6d9c4ccd80b71568feed7b6e24b)) +* add application models ([90c72a9](https://github.com/msgbyte/tianji/commit/90c72a9d321c0df9c057b5da529bd598150df76d)) +* add application page add/edit and appinfo scraper logic ([b114aaf](https://github.com/msgbyte/tianji/commit/b114aafbf93a6fbb7531fb3391da9114e11edcbf)) +* add application screen name support ([5e7c499](https://github.com/msgbyte/tianji/commit/5e7c4990f85fe2c644f0e3fef47fd9deb7c27bc2)) +* add auto jump for application route ([01521e7](https://github.com/msgbyte/tianji/commit/01521e73097918e5a84757fc7ac9ed01bc86d0ef)) +* add avgEventsPerSession and avgScreensPerSession ([87e5e19](https://github.com/msgbyte/tianji/commit/87e5e191f7582305998b73e140e39aff8c098da9)) +* add charts for app stats ([a767aca](https://github.com/msgbyte/tianji/commit/a767acacab0d77797739020147289f444ca447e6)) +* add daily cronjob dailyUpdateApplicationStoreInfo ([0fbf456](https://github.com/msgbyte/tianji/commit/0fbf456be5de70c3b716d6afc94198db8d755527)) +* add delete feature in application page ([824dedd](https://github.com/msgbyte/tianji/commit/824deddc4102129be42d97a1efb2cf1dfe45ad3a)) +* add read more component and apply in application description ([10fb73a](https://github.com/msgbyte/tianji/commit/10fb73a304c16c1af9822633cb2a88f36f1dd766)) +* add screen view event reporting and update version ([83cb111](https://github.com/msgbyte/tianji/commit/83cb111617382ea8ad6458239c7b320f10ce2da6)) +* add StatCard component and integrate avg time metric ([f99a6e0](https://github.com/msgbyte/tianji/commit/f99a6e0dcc4287d2f5f7ea94420f0f74729a8f3c)) +* add vm2 sandbox support as vm runtime backup ([6928103](https://github.com/msgbyte/tianji/commit/69281039972301ad4c1b7f5a0916a8dab9c636b9)) +* allow change role in member table if people is workspace owner [#170](https://github.com/msgbyte/tianji/issues/170) ([d94a9bd](https://github.com/msgbyte/tianji/commit/d94a9bd5a0c120e8dc8675db98ea3c8df1622f2d)) + +### Bug Fixes + +* [#172](https://github.com/msgbyte/tianji/issues/172) fix import issue ([be1e5fb](https://github.com/msgbyte/tianji/commit/be1e5fbf61a58f17859a1030a19bbf11be4645cc)) + +### Document + +* add new documentation for application tracking SDK ([7374542](https://github.com/msgbyte/tianji/commit/7374542a44b0caaeff593b9a84b9a5d557674c89)) +* add yandex verify ([fa0d2da](https://github.com/msgbyte/tianji/commit/fa0d2dabf7fea18118c83d0fc54f141447e162d9)) + +### Others + +* add app card component ([d34da88](https://github.com/msgbyte/tianji/commit/d34da88cb6c27714d05c09c1a64054f64809f9cc)) +* add application tracking things ([9ff4dc6](https://github.com/msgbyte/tianji/commit/9ff4dc663da112c0f13d5ce44242af9229043561)) +* add count sum in worker ([4672795](https://github.com/msgbyte/tianji/commit/4672795b9708ca4b494c425bdd720ebf4bb03661)) +* add member role translation ([512cbbf](https://github.com/msgbyte/tianji/commit/512cbbf0e8694a91d06cd8135c6c2ecf846313ee)) +* add no store info tip and update translation ([3a50ce5](https://github.com/msgbyte/tianji/commit/3a50ce56daee31dc676f69b117e9af0d74132976)) +* drop expo example support ([2bcec9e](https://github.com/msgbyte/tianji/commit/2bcec9ee2fca35fd8386bbf7127bfce690c335dc)) +* fix ci problem ([5a09aff](https://github.com/msgbyte/tianji/commit/5a09affb4752b0009ba6047f9341eccbd04e5a63)) +* improve style and make those things more better ([604d53c](https://github.com/msgbyte/tianji/commit/604d53ceb9a60177c97c4fb7da2cdfea311a0cc1)) +* init expo repo which as example for react native sdk ([0e42057](https://github.com/msgbyte/tianji/commit/0e42057aeda1cca54766ce21d9325af3494b43a0)) +* integrate nativewind for styling and update configs ([2d0a225](https://github.com/msgbyte/tianji/commit/2d0a2252b06925534dd8774a5d3c5c28e5dc7aa9)) +* move example to example/web ([45250ed](https://github.com/msgbyte/tianji/commit/45250ed748880c58478cfb28767019efc6ab951e)) +* refactor cronjob tasks and move to independent file ([9be0e28](https://github.com/msgbyte/tianji/commit/9be0e28085cea9a1dfa45e4ec83b626f6aba6ec3)) +* reorder components and update styles ([bd4e737](https://github.com/msgbyte/tianji/commit/bd4e73775d4f09ad69335e74bb4b5fce4a174272)) +* reorganize overview tab and detail card layout ([7cef811](https://github.com/msgbyte/tianji/commit/7cef81194337e8183caad633c55230917dc98d32)) +* update translation ([0d409be](https://github.com/msgbyte/tianji/commit/0d409be7b7c26656366f149bfbd5c01d177b33eb)) + +## [1.18.22](https://github.com/msgbyte/tianji/compare/v1.18.21...v1.18.22) (2025-03-15) + +### Features + +* add stack chart type ([5a5579a](https://github.com/msgbyte/tianji/commit/5a5579a036cef1733d992b542c7ae728fcf42f42)) +* add survey break down filter render chart and table view ([e13256e](https://github.com/msgbyte/tianji/commit/e13256e6cdcde455e992905011de2693483fcca9)) +* insight survey add groups support ([0288735](https://github.com/msgbyte/tianji/commit/028873523f487e21215d5d7e6388f8339f473bc8)) +* insight website add groups support ([54d2678](https://github.com/msgbyte/tianji/commit/54d267887f06993d8cefa1bfdc96f6f7eab52402)) + +### Others + +* add more chart color ([ca0601d](https://github.com/msgbyte/tianji/commit/ca0601d1e49c7602a23f310be0b32c98feb8a054)) +* fix ci problem ([78a2a79](https://github.com/msgbyte/tianji/commit/78a2a793ee43fce7fbd6f45f2d8b13a0930d18a5)) +* insight add types and survey params ([446b19a](https://github.com/msgbyte/tianji/commit/446b19a8ca9d37c28c7ad97e4f20ba2956be9600)) +* refactor survey sql place ([099214c](https://github.com/msgbyte/tianji/commit/099214ca6f0b41cfe126ab89ac8ababccb1f391c)) +* release insight route for non-dev environment ([8bdff74](https://github.com/msgbyte/tianji/commit/8bdff74ba6312461c4b3b57da10e5145c0b2fcab)) +* rollback react package version ([d69b484](https://github.com/msgbyte/tianji/commit/d69b484fae322523c2183fd319aaef23cc85930c)) +* update minute logic, will auto switch date unit after switch ([965e502](https://github.com/msgbyte/tianji/commit/965e50229a58ec37860483c8ed87b7d5dbf8f9ea)) + +## [1.18.21](https://github.com/msgbyte/tianji/compare/v1.18.20...v1.18.21) (2025-03-13) + +### Others + +* improve write logic, update database when openai return response ([0d31c07](https://github.com/msgbyte/tianji/commit/0d31c074fa29d42de3f63d28656dac5de7988a5c)) +* update end at time for ai task to let has more range ([2849048](https://github.com/msgbyte/tianji/commit/2849048ec4d31b94b082609f2232f91a71c55a3d)) + +## [1.18.20](https://github.com/msgbyte/tianji/compare/v1.18.19...v1.18.20) (2025-03-12) + +### Others + +* change daily work cronjob time ([a288245](https://github.com/msgbyte/tianji/commit/a288245cf306fb5fd523b659b611cb0b5b1499a8)) +* implement common filter query operator for insights model ([d972924](https://github.com/msgbyte/tianji/commit/d9729241e080e42e7c922ae15408ba3713b225b4)) +* update dependencies and package versions ([3762281](https://github.com/msgbyte/tianji/commit/3762281f601ab5517d3411da81b6238a98d7a4c8)) + +## [1.18.19](https://github.com/msgbyte/tianji/compare/v1.18.18...v1.18.19) (2025-03-11) + +### Others + +* update default mode because of different performance in data analysis ([2b23ec9](https://github.com/msgbyte/tianji/commit/2b23ec95dd43b01e4d461548dc0685d797e7db20)) + +## [1.18.18](https://github.com/msgbyte/tianji/compare/v1.18.17...v1.18.18) (2025-03-11) + +### Others + +* update top p and temperature ([1f850b6](https://github.com/msgbyte/tianji/commit/1f850b6a5b22307ff89ba70e6f1236c241d28fa5)) + +## [1.18.17](https://github.com/msgbyte/tianji/compare/v1.18.16...v1.18.17) (2025-03-11) + +### Others + +* fix ci problem ([6fd40b0](https://github.com/msgbyte/tianji/commit/6fd40b0255ef727a06cc9f3fd22462892c10193c)) + +## [1.18.16](https://github.com/msgbyte/tianji/compare/v1.18.15...v1.18.16) (2025-03-11) + +### Others + +* update lock file ([d3b1adc](https://github.com/msgbyte/tianji/commit/d3b1adcde0eb2f9a37f61c416f4bbdc19365c408)) + +## [1.18.15](https://github.com/msgbyte/tianji/compare/v1.18.14...v1.18.15) (2025-03-11) + +### Others + +* rebuild ai classify prompt which will have more better performance ([c9f2a8e](https://github.com/msgbyte/tianji/commit/c9f2a8ebdf8d13b487a140d22bb63da5fafdcee7)) + +## [1.18.14](https://github.com/msgbyte/tianji/compare/v1.18.13...v1.18.14) (2025-03-11) + +### Features + +* add country translation ([4a183a6](https://github.com/msgbyte/tianji/commit/4a183a685bdf443e07635f4f228f5d809f5f49a0)) +* add custom date picker in insight feature ([c0aa47b](https://github.com/msgbyte/tianji/commit/c0aa47b671758765456d68dad01f3cc8f83bb885)) +* add openapi for ai route and update openai schema ([da1cb8c](https://github.com/msgbyte/tianji/commit/da1cb8ce1ebfbf8de1be341582ec05205f204ca0)) +* add SearchLoadingView which improve insight display ([30fd3bf](https://github.com/msgbyte/tianji/commit/30fd3bfe4335170eb13cd71df83d2474a88dc48b)) +* add worker which can trigger ai task in survey daily ([44f8507](https://github.com/msgbyte/tianji/commit/44f8507ab42516dbcfa8732393cd1a2d7ae25dfb)) +* insight add survey support ([8542d48](https://github.com/msgbyte/tianji/commit/8542d48e8139435b0b71321f5aa147a768587bfe)) + +### Bug Fixes + +* fix filter operator in date type ([13f96a8](https://github.com/msgbyte/tianji/commit/13f96a8ffb0095957ece18d7e1e4dd561eef1474)) + +### Document + +* update openapi schema ([9642142](https://github.com/msgbyte/tianji/commit/96421423c564d17a967c933d1d7152360394ac13)) + +### Others + +* add insight type support in code logic ([1f2af9b](https://github.com/msgbyte/tianji/commit/1f2af9b044645639dc23b3aabed180c8e0a9f14a)) +* create daily-ai-trigger project ([f20e149](https://github.com/msgbyte/tianji/commit/f20e14988912b0087aceb3e853aa29d79630b652)) +* improve display of count ([e26de44](https://github.com/msgbyte/tianji/commit/e26de446d787d2bdd4c64f74b3a71ab354d02d05)) +* improve insight chart render main block display ([62ef469](https://github.com/msgbyte/tianji/commit/62ef469812749030218a4dfd3e4e0211d3eaee5f)) +* rebuild insight related logic place folder ([4951410](https://github.com/msgbyte/tianji/commit/49514104c31ac766a91bdeb41723a1038e627356)) +* update monitor clear day, make sure its can always large than 1 month ([e048157](https://github.com/msgbyte/tianji/commit/e04815798849ddd59f547e6ab276a344105ca860)) + +## [1.18.13](https://github.com/msgbyte/tianji/compare/v1.18.12...v1.18.13) (2025-03-09) + +### Features + +* add run task record ([48383db](https://github.com/msgbyte/tianji/commit/48383db76db4ad1b45aff6d0ddf41b1dd7677c99)) + +### Others + +* change survey preview date range from 7 days to 14 days ([99a9f3b](https://github.com/msgbyte/tianji/commit/99a9f3b4765216a02e3c5406d0bba6feb53b0d4c)) + +## [1.18.12](https://github.com/msgbyte/tianji/compare/v1.18.11...v1.18.12) (2025-03-07) + +### Features + +* add survey ai translation ([476259c](https://github.com/msgbyte/tianji/commit/476259ce5ce071717170ebe02ca4f9c2758703bf)) +* add workspace task model ([94a4943](https://github.com/msgbyte/tianji/commit/94a49436e3a194a29f3a073397651c4347a4ee57)) + +### Bug Fixes + +* add scrollview in status page ([1f5280e](https://github.com/msgbyte/tianji/commit/1f5280e39d59f091a14ea5db1bacd8d595b53c4a)) + +### Document + +* add Troubleshooting section in server status page ([9b2b881](https://github.com/msgbyte/tianji/commit/9b2b8812726f53d7cdb98dfbe5fb88b3a42a76f4)) + +### Others + +* add ai classify worker batch log ([668705c](https://github.com/msgbyte/tianji/commit/668705c9de52d5803eb36887c0f1ce7b3a009c8c)) +* add key to force refresh table component ([ae095fb](https://github.com/msgbyte/tianji/commit/ae095fbf6768806f9acb018bb321e383d8a4bf01)) +* change default language strategy to user ([7b2b202](https://github.com/msgbyte/tianji/commit/7b2b2028fe6225866ac820951061752ba71a2b30)) +* fix ci problem ([3f8e17d](https://github.com/msgbyte/tianji/commit/3f8e17dcde44ce27caf145916cb90bdd739453e3)) +* move insight button into dev stage ([96faa81](https://github.com/msgbyte/tianji/commit/96faa81a3daf9d944e7d12b90c53c07c308c7678)) +* refacyor survey ai button modal ([9a24eca](https://github.com/msgbyte/tianji/commit/9a24ecacb18e70f39775f693f7e15469eed84218)) + +## [1.18.11](https://github.com/msgbyte/tianji/compare/v1.18.10...v1.18.11) (2025-02-19) + +### Others + +* improve tokenizer calc, replace with number rather than calc because of high cpu usage ([3ba7ca2](https://github.com/msgbyte/tianji/commit/3ba7ca2b537a7eef5b01b9553897fc0e1410ca76)) + +## [1.18.10](https://github.com/msgbyte/tianji/compare/v1.18.7...v1.18.10) (2025-02-18) + +### Features + +* migrate ai survey task from promise to MQ ([e79ad8f](https://github.com/msgbyte/tianji/commit/e79ad8fe4b966d69956fea01c3474fe6b347ba52)) + +### Bug Fixes + +* fix a not accept language problem(some ts issue before) ([b38310e](https://github.com/msgbyte/tianji/commit/b38310eb1c2b9811c93567989aa15fcc7acb9afd)) +* hotfix some header not have language header issue ([8959ddc](https://github.com/msgbyte/tianji/commit/8959ddcc4bb70bb8d921bc27a5db3810d2daeecc)) + +### Others + +* release v1.18.8 ([b838c4d](https://github.com/msgbyte/tianji/commit/b838c4dc055cd2109ee08476f1afec5bc94d38e3)) +* release v1.18.9 ([ab90204](https://github.com/msgbyte/tianji/commit/ab90204344f4336c14669e856c37267a94164bed)) +* update survey date picker logic ([a235289](https://github.com/msgbyte/tianji/commit/a2352896dc1806ce6e208b27ca150a3f3758dcf7)) +* use or to filter suggestion category ([c0995bc](https://github.com/msgbyte/tianji/commit/c0995bcb313d72429bc5fe1a7eeea6fde6ef5764)) + +## [1.18.9](https://github.com/msgbyte/tianji/compare/v1.18.8...v1.18.9) (2025-02-18) + +### Bug Fixes + +* fix a not accept language problem(some ts issue before) ([5f53736](https://github.com/msgbyte/tianji/commit/5f53736fa1e0716ef04e50c95900087b9da78c9a)) + +## [1.18.8](https://github.com/msgbyte/tianji/compare/v1.18.7...v1.18.8) (2025-02-18) + +### Bug Fixes + +* hotfix some header not have language header issue ([da24e65](https://github.com/msgbyte/tianji/commit/da24e653aeb7edef907a7dfa9b997e7b198fa83a)) + +## [1.18.7](https://github.com/msgbyte/tianji/compare/v1.18.6...v1.18.7) (2025-02-17) + +### Features + +* add group analyze and ai debug ([971427f](https://github.com/msgbyte/tianji/commit/971427f22f2ee85cec8007a0f4bc3539df1aef11)) +* add user language support in ai category classify ([ab4342b](https://github.com/msgbyte/tianji/commit/ab4342b2a2af83581f0a910671df3725db0b3315)) + +## [1.18.6](https://github.com/msgbyte/tianji/compare/v1.18.5...v1.18.6) (2025-02-16) + +### Features + +* add click to view ai category feature ([b72643a](https://github.com/msgbyte/tianji/commit/b72643a5372f29ba8aa0c9964efc6d54379c7c10)) +* add env DISABLE_ACCESS_LOGS and DEBUG_AI_FEATURE ([4795b3d](https://github.com/msgbyte/tianji/commit/4795b3dc3583b671ddf0e24f9ca7afb76ac04c5b)) +* add more run strategy and improve ui style ([fd1e5cb](https://github.com/msgbyte/tianji/commit/fd1e5cb46e39c114fb66db410f403ec15c36d79d)) + +### Bug Fixes + +* [#158](https://github.com/msgbyte/tianji/issues/158) fix typo ([8819af4](https://github.com/msgbyte/tianji/commit/8819af4db1cc01727ddfe4cf41a48390b1634889)) + +### Document + +* add telemetry report claim ([fc67dba](https://github.com/msgbyte/tianji/commit/fc67dbaef82121a58a02c14f1787cc7f3f1b9cd5)) +* improve display for website carousel, which will let image can be selected ([99b4140](https://github.com/msgbyte/tianji/commit/99b41408ce1b41f4229ee689def60d99189aa8e9)) + +### Others + +* add filter query in survey result ([fff7346](https://github.com/msgbyte/tianji/commit/fff7346d3cd5da284691e89cc145863ec4d8d482)) +* update columns logic ([d6af45e](https://github.com/msgbyte/tianji/commit/d6af45e2b200a9cb2b188bb92843a8751a2ff626)) +* update translation ([dc41557](https://github.com/msgbyte/tianji/commit/dc415578ebdc363a7825a4262bf93ae2479b4d31)) +* update translation ([f627b31](https://github.com/msgbyte/tianji/commit/f627b31a71149eae27f8fda17d608a86fe3f1709)) + +## [1.18.5](https://github.com/msgbyte/tianji/compare/v1.18.4...v1.18.5) (2025-02-11) + +### Others + +* add more timezone related things ([ca37ea1](https://github.com/msgbyte/tianji/commit/ca37ea16eaa5e98e29e3727859e9409fcbaacca6)) +* clear result text after re-run ([a63240a](https://github.com/msgbyte/tianji/commit/a63240accf6c5094bc6fd2f3e9f94697c4595c23)) +* merge category result after parsed ([8d6ab5c](https://github.com/msgbyte/tianji/commit/8d6ab5c44870ccf3aa19bfcd9989bb66e62d9275)) + +## [1.18.4](https://github.com/msgbyte/tianji/compare/v1.18.3...v1.18.4) (2025-02-10) + +### Bug Fixes + +* fxi import problem in server side in 1.18.3 ([d59fe7b](https://github.com/msgbyte/tianji/commit/d59fe7b03a41924f823418508f0385b6323c9338)) + +## [1.18.3](https://github.com/msgbyte/tianji/compare/v1.18.2...v1.18.3) (2025-02-10) + +### Bug Fixes + +* fix timezone issue in server side which in different server ([e3d6828](https://github.com/msgbyte/tianji/commit/e3d682889cb9011970687e6b6649eee33bee9bf8)) + +### Others + +* fix ci problem ([73687a6](https://github.com/msgbyte/tianji/commit/73687a6163a178f1249963ca3da88b4572b3e997)) + +## [1.18.2](https://github.com/msgbyte/tianji/compare/v1.18.1...v1.18.2) (2025-02-09) + +### Features + +* add checkbox ui ([9068935](https://github.com/msgbyte/tianji/commit/9068935e3bfab5fc375d8c75a1c66fa8ec3babf4)) +* add classify survey feature ([4c37eab](https://github.com/msgbyte/tianji/commit/4c37eab57ab43086c0c12ca5454469202b484cbd)) +* add credit cost and check in ai ask route ([6c8e49b](https://github.com/msgbyte/tianji/commit/6c8e49b43011991f6318ff2d979bfc13da0646ab)) +* add image url render ([b84b840](https://github.com/msgbyte/tianji/commit/b84b840a38fa331a4b86fb05883a6fe097aa9977)) +* add SurveyCategoryChart ([ba281ac](https://github.com/msgbyte/tianji/commit/ba281ac0f66ff756ad72c0f8d8e9c211f85cf2f5)) + +### Bug Fixes + +* fix app review pool not enough problem ([cd29f86](https://github.com/msgbyte/tianji/commit/cd29f8631878023f7f363ff85e07bf65a6594a14)) +* fix survey stat timezone not correct problem ([a5c7998](https://github.com/msgbyte/tianji/commit/a5c7998cf1431086336d231b6aa2b5d9b40544d7)) + +### Others + +* add global config local storage cache ([cc532bb](https://github.com/msgbyte/tianji/commit/cc532bb381f5cc766616f2a12165f01a56d69f1b)) +* add translation ([170b406](https://github.com/msgbyte/tianji/commit/170b40647c0f53216ebea85f18d891eca9b6b9a3)) +* extract DatePicker component ([ead62eb](https://github.com/msgbyte/tianji/commit/ead62eb77f985bcf5f7fe90bfe2aee3783246d2b)) +* survey download btn should export all necessary field ([b936421](https://github.com/msgbyte/tianji/commit/b936421fa7138b437efa9cc5a835cf3a59ba6be5)) + +## [1.18.1](https://github.com/msgbyte/tianji/compare/v1.18.0...v1.18.1) (2025-02-02) + +### Others + +* upgrade isolated-vm version ([ec3e9a3](https://github.com/msgbyte/tianji/commit/ec3e9a3daeae4d7ed30742110b5424dce26d593a)) +* upgrade zeromq version from 6.0.4 -> 6.3.0 ([f5e5e5c](https://github.com/msgbyte/tianji/commit/f5e5e5c46b46a3bc219ef72db4ef8ef2d318cd75)) + +## [1.18.0](https://github.com/msgbyte/tianji/compare/v1.17.6...v1.18.0) (2025-02-02) + +### Features + +* add ai tool getSurveyByDateRange ([a27ef8f](https://github.com/msgbyte/tianji/commit/a27ef8f38c3518748569befe655a4d88eadca775)) +* add aiToolsSelection which can easy to pick tools ([eec23ec](https://github.com/msgbyte/tianji/commit/eec23ec506dc5cb1498a91c81090f4ebcc2dfb18)) +* add busy check ([fea436a](https://github.com/msgbyte/tianji/commit/fea436ac07e61e92c3369af04a8d499e4c7ddd2d)) +* add date picker ([c83c43d](https://github.com/msgbyte/tianji/commit/c83c43d73fd316cf1002324eb0e35561be31ab4d)) +* add date unit support ([453c8f2](https://github.com/msgbyte/tianji/commit/453c8f25e2e907b312d7818a15ed45e120c16314)) +* add event calc math method: by session ([8c6bb24](https://github.com/msgbyte/tianji/commit/8c6bb24143566fcc7c2ea222831fdeac8cd7813f)) +* add hover state of metrics block ([4d5b098](https://github.com/msgbyte/tianji/commit/4d5b098f9f41a5af5c14f9143f732a937e6814de)) +* add insights chart render and backend fetch ([343e2b3](https://github.com/msgbyte/tianji/commit/343e2b353e855843b58e70d1097c13b146f608ec)) +* add insights in chart ([1c777b7](https://github.com/msgbyte/tianji/commit/1c777b754bf4ef6b946ee1e30137d7b2389e6806)) +* add new entry for website insights ([82972f5](https://github.com/msgbyte/tianji/commit/82972f51c5472ccfd1092e7c8727eca991c7983a)) +* add resizable panel ([61af19b](https://github.com/msgbyte/tianji/commit/61af19bba5fbed4313e859598d73f3a99c95f051)) +* add survey detail sheet panel ([5752762](https://github.com/msgbyte/tianji/commit/57527625cbbfa2089648f0748327e6716813b1f1)) +* add survey prompt, credit calc logic and context ([a651f5d](https://github.com/msgbyte/tianji/commit/a651f5d977a7a87ecd642b8435f9aa2462cb0c32)) +* add table pinning ([572e551](https://github.com/msgbyte/tianji/commit/572e5512b55ab75f3391233a62fb2ecaab1b2234)) +* add table view for insights ([05a7def](https://github.com/msgbyte/tianji/commit/05a7def550b587b831235a2e95ca07a52dc8983e)) +* add tianji app reviewer ([5da38b4](https://github.com/msgbyte/tianji/commit/5da38b4c0f49f7619495f4cd12c917e232717f49)) +* add useAIStoreContext which allow change current page ai store context ([f70dde0](https://github.com/msgbyte/tianji/commit/f70dde03bcc3d12468c19ae3913c9b867a9b4980)) +* insights add filter logic ([c1b0812](https://github.com/msgbyte/tianji/commit/c1b081282cce8da41942089099e2941523042eb1)) +* metrics add delete item support ([9779885](https://github.com/msgbyte/tianji/commit/9779885720e479d0a077b95c00ef4bc8739d6315)) + +### Bug Fixes + +* [#146](https://github.com/msgbyte/tianji/issues/146) fix typo ([e1ed6e3](https://github.com/msgbyte/tianji/commit/e1ed6e35633cd5aef1705a11f0d6aeb918c316ef)) +* fix a issue which will make openai key is required(original should be optional) [#139](https://github.com/msgbyte/tianji/issues/139) ([f0fba13](https://github.com/msgbyte/tianji/commit/f0fba13179115aa68741ab8b66f7375f1cab2aac)) +* fix session data can not save problem ([9405884](https://github.com/msgbyte/tianji/commit/94058840df5626e94cf7a53a793c060ad664b160)) + +### Document + +* add event track documents ([d815adf](https://github.com/msgbyte/tianji/commit/d815adfa720c30d3340a5e790519b55c2ed80ac1)) +* add interactive hover button in get start button ([a40fe6e](https://github.com/msgbyte/tianji/commit/a40fe6e646d009506ae5674e09cd2393e0c1b88d)) +* add tweet card in website ([f6e443f](https://github.com/msgbyte/tianji/commit/f6e443fae504d1c34befffdc12648a59e374a300)) +* upgrade docusaurus-i18n and update website translation ([892e650](https://github.com/msgbyte/tianji/commit/892e650d3ce0bba2ac5f365e474d39b8176b3e80)) + +### Others + +* add empty state for chart render component ([208daf0](https://github.com/msgbyte/tianji/commit/208daf0259afcd1296adec23e19f32a43ebda5c1)) +* add identify in example repo ([a61d28f](https://github.com/msgbyte/tianji/commit/a61d28fda55941f6e178548d209dcd10e6128eb3)) +* add metrics block no content tip ([0d91bad](https://github.com/msgbyte/tianji/commit/0d91badc41830c7921d8314c5bbcbe1cece01ea5)) +* add more example playground events ([ce70092](https://github.com/msgbyte/tianji/commit/ce7009248a97287d8b567f15465607ae68dc5cae)) +* add null ip process ([59eac90](https://github.com/msgbyte/tianji/commit/59eac903c3e09f4b09d807f21bab471055d16f6c)) +* add openai tool choose support ([a0d170d](https://github.com/msgbyte/tianji/commit/a0d170de36eae12fec32faa4db444e575de9f737)) +* add openapi sdk options ([ea4b04c](https://github.com/msgbyte/tianji/commit/ea4b04cd6eab399a7ba4fe4264b94e8ef7338b5f)) +* add optional in channel ([ae4c2bb](https://github.com/msgbyte/tianji/commit/ae4c2bb89bae03389441fa07cfddb4943be3c16e)) +* add type support for ai store context ([b548341](https://github.com/msgbyte/tianji/commit/b548341dccf9c8ba431f0b7a4eea73d56729032b)) +* disable ai feature if not enable ai feature in server ([6a5753e](https://github.com/msgbyte/tianji/commit/6a5753e487547efc637e5e47ddaac4c0088b2756)) +* example repo add send tianji report support ([d8e642b](https://github.com/msgbyte/tianji/commit/d8e642be953d8cd02199d91d73d76308e49a5b9d)) +* fix ci problem ([2f70afd](https://github.com/msgbyte/tianji/commit/2f70afdeaa20b2ad9ea1be4ed9ce9774bf61f49e)) +* fix ci problem ([2e47cc4](https://github.com/msgbyte/tianji/commit/2e47cc44d3e94970d082613c3d5699d25c71daa5)) +* fix ci problem ([f9bda19](https://github.com/msgbyte/tianji/commit/f9bda19d828e517ca4be7503c686f1ac27745019)) +* fix datatable component array keys problem ([be90c3d](https://github.com/msgbyte/tianji/commit/be90c3d5e2f21e61674a8bacff2b3e7df11e580a)) +* fix trpc-to-openapi use incorrect middleware problem ([b91cb44](https://github.com/msgbyte/tianji/commit/b91cb4458f3e4ddccdb05ba981f8e263c7221071)) +* improve insights style ([ff5b777](https://github.com/msgbyte/tianji/commit/ff5b7773921d6382cc751cb5195629cc323e331e)) +* improve metric block display and create logic, improve user experience ([dae37b5](https://github.com/msgbyte/tianji/commit/dae37b5bb4c578913c19e10da288dfc1d146479d)) +* improve sdk package ([ebb69a4](https://github.com/msgbyte/tianji/commit/ebb69a421998376559d6fbec692972e62851ca8a)) +* improve some detail ([c131078](https://github.com/msgbyte/tianji/commit/c1310784dad5abf907fbc64c25b74f8229252e9e)) +* improve tip and style ([13ee7f5](https://github.com/msgbyte/tianji/commit/13ee7f5a0644d6d0effaef3d03e1a2a5bd08656a)) +* init example repo ([55fc93d](https://github.com/msgbyte/tianji/commit/55fc93da7ed6a20d01b1d7b5ae7548d6e26c45ee)) +* move insights to other router ([074aa56](https://github.com/msgbyte/tianji/commit/074aa56898b232d6c7e730e143f4ef3cc6107998)) +* refactor metric block and add reused part of dropdown ([e855fb6](https://github.com/msgbyte/tianji/commit/e855fb68d644200fe0be7f981426e4323d7dd696)) +* release v1.17.7 ([081f102](https://github.com/msgbyte/tianji/commit/081f1025ba1fa3a70421838cb086de41ccdb057e)) +* release v1.17.8 ([7dbfb6f](https://github.com/msgbyte/tianji/commit/7dbfb6f7db1738b4078a01eee81baaddfce20e15)) +* release v1.17.9 ([6cceb40](https://github.com/msgbyte/tianji/commit/6cceb407a8907acbb626bef84e9184f2ca40d4b7)) +* update react version in example repo ([313f909](https://github.com/msgbyte/tianji/commit/313f9093d8f4eb459ee6d941580a34f9d6379d47)) +* update translation ([7a714b5](https://github.com/msgbyte/tianji/commit/7a714b56abcb8b91d7336e9ecd8079f269f9bae2)) +* update translation ([74f62e2](https://github.com/msgbyte/tianji/commit/74f62e20b732620b03af498c67cccc83112e598c)) +* upgrade execa version ([1321986](https://github.com/msgbyte/tianji/commit/1321986930cda06b60e9c6c9a8b1ac6d48963f72)) +* upgrade react-icons version v4 -> v5 ([bc1a9d2](https://github.com/msgbyte/tianji/commit/bc1a9d28728daac4d315c80171d9f77a52fd4bab)) + +## [1.17.9](https://github.com/msgbyte/tianji/compare/v1.17.8...v1.17.9) (2025-01-21) + +### Others + +* fix trpc-to-openapi use incorrect middleware problem ([707d6fa](https://github.com/msgbyte/tianji/commit/707d6fa800dc917a8673927b973565995fdb398a)) + +## [1.17.8](https://github.com/msgbyte/tianji/compare/v1.17.7...v1.17.8) (2025-01-21) + +### Others + +* add null ip process ([2789ec7](https://github.com/msgbyte/tianji/commit/2789ec7d95b0658a6742ef3b98a77318e647e15b)) + +## [1.17.7](https://github.com/msgbyte/tianji/compare/v1.17.6...v1.17.7) (2025-01-19) + +### Bug Fixes + +* fix a issue which will make openai key is required(original should be optional) [#139](https://github.com/msgbyte/tianji/issues/139) ([1ed859b](https://github.com/msgbyte/tianji/commit/1ed859b56c0fb68b536ad83ad2c9b649cc0a853b)) + +## [1.17.6](https://github.com/msgbyte/tianji/compare/v1.17.5...v1.17.6) (2025-01-04) + +### Features + +* add auto refetch logic in status page and refactor status page header ([9ccae18](https://github.com/msgbyte/tianji/commit/9ccae18b2b00882a5634237078443e008dc607f4)) +* add openai endpoint ([cc41483](https://github.com/msgbyte/tianji/commit/cc41483d93294bde2dd0c81657c028b849ff0364)) +* add openai sse api ([4b1f6c2](https://github.com/msgbyte/tianji/commit/4b1f6c218219f6b83fb87b784d2ed5bc0d18ec70)) +* add timezone support in trpc ctx and monitor.publicSummary ([890bf18](https://github.com/msgbyte/tianji/commit/890bf182d71023b2ba4fa050e2fb49f34099e5a4)) +* add workspace bill model ([58a04aa](https://github.com/msgbyte/tianji/commit/58a04aa0eaff4391733cf77b7bb43e1d6e447d8a)) +* allow register command in page ([e7297b1](https://github.com/msgbyte/tianji/commit/e7297b1a5781349ed87d6cb8ea0a84975e978e50)) + +### Others + +* fix ci problem ([67c4fa3](https://github.com/msgbyte/tianji/commit/67c4fa305af8ffc60d31c6f598aad2434811de81)) +* migrate react query to v5 ([45c91f8](https://github.com/msgbyte/tianji/commit/45c91f805314d787e8d888c15720200c7afa7fef)) +* upgrade trpc version to next ([dfec38c](https://github.com/msgbyte/tianji/commit/dfec38cce440b77c7bc281cad8fdefb614c0cf09)) + +## [1.17.5](https://github.com/msgbyte/tianji/compare/v1.17.4...v1.17.5) (2024-12-25) + +### Features + +* add default header preset in http monitor ([60a1d84](https://github.com/msgbyte/tianji/commit/60a1d847497604be32a7f89e2e274c997cdaaa5f)) +* add insight route in dev ([04e93bf](https://github.com/msgbyte/tianji/commit/04e93bf542594e4bbfa85d0cd44dac0617f8faa3)) +* add new login background ([6f367c2](https://github.com/msgbyte/tianji/commit/6f367c2c48c9621e3070055bea42229f136093a6)) +* add paused tip ([d688a78](https://github.com/msgbyte/tianji/commit/d688a787427d1fd6d349a7576468021c8a795d13)) +* add survey stats chart ([536e3b1](https://github.com/msgbyte/tianji/commit/536e3b1c6212d59b438f75ed6795e8b9e3c7f5e2)) + +### Document + +* add aapanel install guide ([ba39fab](https://github.com/msgbyte/tianji/commit/ba39fab4dfa3ea95f114f2961a9eeae3dfda24cf)) +* add cloud link ([0ac3f41](https://github.com/msgbyte/tianji/commit/0ac3f414f9212eccfdaec0699eb3e5c7d7eab390)) +* update aapanel version and name of china ([1cf43cc](https://github.com/msgbyte/tianji/commit/1cf43cc2a5fe83a9f6b2a3aa0c76674cc06c87fd)) +* update changelog ([a0c54ba](https://github.com/msgbyte/tianji/commit/a0c54ba01bac691823c45f00781b73b7d72ded09)) +* update website and make cloud entry more strong ([4df9a5f](https://github.com/msgbyte/tianji/commit/4df9a5f6867e33a61ceacba074179f38d6aa6431)) + +### Others + +* add clickhouse config ([8daabe3](https://github.com/msgbyte/tianji/commit/8daabe322e761aea1257c39e9947a7c7d7196a7e)) +* add dev container for insights feature ([f8ba1dc](https://github.com/msgbyte/tianji/commit/f8ba1dc4d00db347e59d50612d9530643834555c)) +* fix typo ([6b34f10](https://github.com/msgbyte/tianji/commit/6b34f10bfd3d00a5bf8b0a5706422a8ee75455df)) +* move k8s folder to docker/k8s folder. ([08b37ce](https://github.com/msgbyte/tianji/commit/08b37ce16b1494731bf5140465a09659d2c186a4)) +* move TimeEventChart component ([e5cc2ee](https://github.com/msgbyte/tianji/commit/e5cc2eebf84eb7aaba6747939d0a14d42f337de0)) +* update monitor page style ([691bc4b](https://github.com/msgbyte/tianji/commit/691bc4bdb3fc4cc5d030616cb272ce2153b88e5f)) + +## [1.17.4](https://github.com/msgbyte/tianji/compare/v1.17.3...v1.17.4) (2024-11-29) + +### Document + +* update npm config ([ed591e2](https://github.com/msgbyte/tianji/commit/ed591e207d00d86b08989407d22ace1400bd0fd3)) +* update openapi document [#130](https://github.com/msgbyte/tianji/issues/130) ([0f3168e](https://github.com/msgbyte/tianji/commit/0f3168e340ab292cbf905b9d89dc666da809b96f)) +* update roadmap ([c7bd2e6](https://github.com/msgbyte/tianji/commit/c7bd2e6fb960996111cca091fa8dac1307457980)) + +### Others + +* change init state of global config ([e0da48f](https://github.com/msgbyte/tianji/commit/e0da48f4d438676ea094b39ea87e0d04248d83ca)) +* update code style ([99db3d0](https://github.com/msgbyte/tianji/commit/99db3d02e370b9d252919d216ad0e1b36540d199)) + +## [1.17.3](https://github.com/msgbyte/tianji/compare/v1.17.2...v1.17.3) (2024-11-27) + +### Features + +* add email login ([cc1cc95](https://github.com/msgbyte/tianji/commit/cc1cc9536e6626a232cdab4d31a5c6b7b8717061)) + +### Others + +* a not cool solution, refresh state again after 5s. ([5ed182b](https://github.com/msgbyte/tianji/commit/5ed182b7f8d7169d4336fd957f14a4f74ab77455)) +* update translations ([26d555f](https://github.com/msgbyte/tianji/commit/26d555f9fae8dc6f72cdaafb9c16b891025a17be)) +* upgrade package version to reduce vulnerabilities ([72458fe](https://github.com/msgbyte/tianji/commit/72458fe301267d5e9bcfd2b3903907439bd4d143)) +* upgrade release-it version ([947f364](https://github.com/msgbyte/tianji/commit/947f364a36ca035f797f210ae72a75ceff6d775e)) + +## [1.17.2](https://github.com/msgbyte/tianji/compare/v1.17.1...v1.17.2) (2024-11-25) + + +### Features + +* add free tier tip component ([1fc4d15](https://github.com/msgbyte/tianji/commit/1fc4d15c35e1c7b161aab968c4c8e595ef6c80b7)) +* add workspace usage check which can update when tier has updated ([bf0598d](https://github.com/msgbyte/tianji/commit/bf0598dd25b8e1b868a5f3ea3fad53a58d8c9dd4)) + + +### Bug Fixes + +* fix a bug that would accidentally pause the workspace ([420860f](https://github.com/msgbyte/tianji/commit/420860f1b6e25928d9444c9b64f7308dde83a472)) + + +### Others + +* update translation ([df12949](https://github.com/msgbyte/tianji/commit/df1294977fcfd1e1005919d8d5cf8b9c926b0473)) + +## [1.17.1](https://github.com/msgbyte/tianji/compare/v1.17.0...v1.17.1) (2024-11-21) + + +### Others + +* improve install script which make easy to install in non-root user ([53777b3](https://github.com/msgbyte/tianji/commit/53777b36c6387f7cd06833a28bf479d38ab00162)) +* update limit check logic ([927eac1](https://github.com/msgbyte/tianji/commit/927eac1da3c56be9bfabc1513b74ae26b3c6e01c)) +* update subscription redirect tip ([21ac087](https://github.com/msgbyte/tianji/commit/21ac0878f90194ca140044b69c07fc085c72cac3)) + +## [1.17.0](https://github.com/msgbyte/tianji/compare/v1.16.5...v1.17.0) (2024-11-19) + + +### Features + +* add component which can render usage data and progress ([a12fa3e](https://github.com/msgbyte/tianji/commit/a12fa3e6feedb3c7d83a097e187aa9945ff9bb10)) +* add api key and usage to command panel ([71f75c2](https://github.com/msgbyte/tianji/commit/71f75c27ddf83ec3ac3855a07b03db5d4182c514)) +* add api key fe and usage counter ([6a4bdd3](https://github.com/msgbyte/tianji/commit/6a4bdd324c2a2fd64219edf637dcfe2fa0959067)) +* add audit log clear feature ([3bf86b3](https://github.com/msgbyte/tianji/commit/3bf86b3e6e6c5e86cbeb3cd9b7aaa41a53dd5439)) +* add auto language detect for browser ([1629546](https://github.com/msgbyte/tianji/commit/162954606a36219c58cd5a36c4a32a325b9a80cb)) +* add create feed and website limit which in max tier limit ([c8d4063](https://github.com/msgbyte/tianji/commit/c8d4063dafe24c8bec0501459bc77af71a848cd1)) +* add cronjob to check workspace limit which will pause workspace ([31ad64c](https://github.com/msgbyte/tianji/commit/31ad64cd955e5dd31167ae5fca6c0e49b7475977)) +* add lemonsqueezy subscription ([74d391a](https://github.com/msgbyte/tianji/commit/74d391afc15a251a7098e4a5c8716bad3b756bd0)) +* add monitor error message ([e23258a](https://github.com/msgbyte/tianji/commit/e23258ac484de777a3c4a952df7b7307153a0b4a)) +* add more usage stats ([b71bf65](https://github.com/msgbyte/tianji/commit/b71bf6542e7312ec6ae0fdcc5008a197c1dc23d2)) +* add subscription selection page ([843a581](https://github.com/msgbyte/tianji/commit/843a581d429b11cb6959e6aee2d50eb9e144f3e5)) +* add user api key backend support ([f7b1d33](https://github.com/msgbyte/tianji/commit/f7b1d33c5d5bcd6d3ff8215412a4635495b35b7c)) +* add workspace pause tips ([b4bee32](https://github.com/msgbyte/tianji/commit/b4bee321ae512d49768522419996b430d569a3f3)) +* add workspace paused check ([77e14d3](https://github.com/msgbyte/tianji/commit/77e14d315f731204f1a38c2294d26d7cbe204ab8)) +* add workspace subscription ([e4b98b1](https://github.com/msgbyte/tianji/commit/e4b98b1c36f5362b88b73ff759378cc02185a836)) +* subscription switch and cancel handler() ([f2f8267](https://github.com/msgbyte/tianji/commit/f2f8267fef98accfd145f56c1c3a6ebab91d45cb)) + + +### Bug Fixes + +* fix ci probelm ([c7ef57b](https://github.com/msgbyte/tianji/commit/c7ef57b4c6bdcd79f95c499a02127d16916b3370)) +* fix isUser middleware will call twice problem ([c70e698](https://github.com/msgbyte/tianji/commit/c70e69879fe5ddc7f0dffcd546c79e79b50f034b)) +* fix url too long problem [#125](https://github.com/msgbyte/tianji/issues/125) ([c33d5bb](https://github.com/msgbyte/tianji/commit/c33d5bbedeb7890c06fbada71892c22639766a91)) + + +### Others + +* add alert ([ea75ed7](https://github.com/msgbyte/tianji/commit/ea75ed7f88f8ffc2290c85096c332b25f98305d9)) +* add apikey check before setup ([f0ddf6c](https://github.com/msgbyte/tianji/commit/f0ddf6c5ddbba871496eb77a02bf515e732809fa)) +* add usage limit and update card style ([34f9fe6](https://github.com/msgbyte/tianji/commit/34f9fe6957a53b46397f47210ec27dabcfc5f482)) +* change check workspace pause logic ([aed707a](https://github.com/msgbyte/tianji/commit/aed707a76146126a89e2edcf3a84e8307ba52806)) +* move billing mode inside folder ([fa1ff3b](https://github.com/msgbyte/tianji/commit/fa1ff3b5f6d93cce615b64bfeac0006f57291820)) +* move monitor action to hooks, reduce file size. ([ae33b52](https://github.com/msgbyte/tianji/commit/ae33b52d45f60d3abfc912b8c8d789a67c06e073)) +* remove passport package ([ae5f5a9](https://github.com/msgbyte/tianji/commit/ae5f5a97d99406df68ef92b2c5b958ec5ac2099c)) +* remove unused script ([fffc989](https://github.com/msgbyte/tianji/commit/fffc989336e8733d2f166d6e620fa379e2eb1d59)) +* update translation ([33b2ea5](https://github.com/msgbyte/tianji/commit/33b2ea581b2cbd52541ac983f8d22c5fa81239e6)) +* upgrade package version ([a03c182](https://github.com/msgbyte/tianji/commit/a03c1824f80f7807d8a3fb59cc47b8ebc4ed5ef3)) + +## [1.16.5](https://github.com/msgbyte/tianji/compare/v1.16.4...v1.16.5) (2024-11-02) + + +### Features + +* add webhookSignature in feed channel ([6b3631e](https://github.com/msgbyte/tianji/commit/6b3631eae186b9cacf64d0ddcfbb66378e041281)) + + +### Bug Fixes + +* add key to Fragment in map for monitor items ([9949b97](https://github.com/msgbyte/tianji/commit/9949b973bd63b4ad6b5e71f7b819442f505c09a6)) +* retrieve date as string ([a8a47ed](https://github.com/msgbyte/tianji/commit/a8a47ed94dda87c3fe4cdecc0acb9a31f53f00a5)) + + +### Others + +* fix ci problem ([59b8746](https://github.com/msgbyte/tianji/commit/59b874644fd3427bc86cd2a7e948054e827de080)) +* refactor status header and add typescript and translation support ([f637ade](https://github.com/msgbyte/tianji/commit/f637ade70f230fbf472bdee84105c9b284d6b8d4)) +* update amount in stripe ([2725056](https://github.com/msgbyte/tianji/commit/272505669e450d882930cbf594dac39a879b2072)) +* update webhooks signature api guide ([266b08f](https://github.com/msgbyte/tianji/commit/266b08f2da16d0457a5a44b4a7a251d28502abc9)) + +## [1.16.4](https://github.com/msgbyte/tianji/compare/v1.16.3...v1.16.4) (2024-10-27) + + +### Features + +* add stripe feed integration ([09d0f02](https://github.com/msgbyte/tianji/commit/09d0f02d844159565e97bb64f076e0bbe218ce98)) + + +### Others + +* update currency symbols in feed ([98298c4](https://github.com/msgbyte/tianji/commit/98298c43670326b4e2300a6bbdeee3daa53f0eb3)) + +## [1.16.3](https://github.com/msgbyte/tianji/compare/v1.16.2...v1.16.3) (2024-10-24) + + +### Others + +* fix ci problem and upgrade version ([1c5737e](https://github.com/msgbyte/tianji/commit/1c5737e588d19e0657be6437792cf4484b6fdddb)) + +## [1.16.2](https://github.com/msgbyte/tianji/compare/v1.16.1...v1.16.2) (2024-10-23) + + +### Features + +* add prometheus report support ([fcb8f22](https://github.com/msgbyte/tianji/commit/fcb8f221168281ab710d3d3f12064a99d17b39e7)) + + +### Bug Fixes + +* fix a bug which will match incorrect path [#115](https://github.com/msgbyte/tianji/issues/115) ([79667a9](https://github.com/msgbyte/tianji/commit/79667a9644b78451400acb6a6bbf07b6ca61e6e0)) + + +### Document + +* update README ([1df32dc](https://github.com/msgbyte/tianji/commit/1df32dc2579f32649afd6c008512c1190a45fd9e)) + + +### Others + +* fix ci problem ([554f902](https://github.com/msgbyte/tianji/commit/554f9025847defe0b05492cf07a5dc8acc6c3685)) +* update openapi document ([e402ee1](https://github.com/msgbyte/tianji/commit/e402ee1688bb77d83463ce70c5e730c97f68a695)) + +## [1.16.1](https://github.com/msgbyte/tianji/compare/v1.16.0...v1.16.1) (2024-10-20) + + +### Features + +* add test notify ([4e3fd9d](https://github.com/msgbyte/tianji/commit/4e3fd9db64629f7721e6092b86b06144c47f521d)) +* add timezone support [#114](https://github.com/msgbyte/tianji/issues/114) ([c7e20df](https://github.com/msgbyte/tianji/commit/c7e20df516bf3a991ce46c937223948bcdb6b8f0)) +* add workspace settings manage ([3dca8fc](https://github.com/msgbyte/tianji/commit/3dca8fc27c82bd96dbab423b111e4de57f3b4bd8)) + + +### Others + +* update cronjob clear time ([83850f2](https://github.com/msgbyte/tianji/commit/83850f2981ded0b6624556ee3430f684752b8ea3)) + +## [1.16.0](https://github.com/msgbyte/tianji/compare/v1.15.8...v1.16.0) (2024-10-19) + + +### Features + +* add click event for status page item which allow hide/show chart ([279e616](https://github.com/msgbyte/tianji/commit/279e616bee510ee5b0c5a3c9a3705a79efd5d3cb)) +* add daily monitor data display for public ([dcff57f](https://github.com/msgbyte/tianji/commit/dcff57fe69273c7f9b3dd9c28e8acc9cb6e430a9)) +* add monitor summary function ([bbb8d88](https://github.com/msgbyte/tianji/commit/bbb8d881168df695ccc70743f46320b39c1d7718)) +* add MonitorLatestResponse and up status summary ([316b954](https://github.com/msgbyte/tianji/commit/316b95467d49b3ebe93d03006d4b90f9ca482262)) + + +### Bug Fixes + +* fix reporter memory leak problem [#103](https://github.com/msgbyte/tianji/issues/103) ([7f70557](https://github.com/msgbyte/tianji/commit/7f70557c776c35e4e01a5533d2c05cecc711e113)) + + +### Others + +* add border radius in smtp template ([f553f15](https://github.com/msgbyte/tianji/commit/f553f157dd9708d553c9d6cfca4d119a62d849c3)) +* change public summary display logic ([e5e77db](https://github.com/msgbyte/tianji/commit/e5e77dbdeeeecb773237b84e3c671dd16e61d458)) +* fix ci problem ([820b25b](https://github.com/msgbyte/tianji/commit/820b25baedc6fec02010ca19b43e4da99bf4b820)) +* ignore unknown sentry log ([527f734](https://github.com/msgbyte/tianji/commit/527f734bc442458018d86df9a7e750a8e8de4495)) +* let version text more prominent ([61980b3](https://github.com/msgbyte/tianji/commit/61980b37d3cecce32fa87b2b9810f4c715990a71)) +* rename old tsconfig paths ([2a503ca](https://github.com/msgbyte/tianji/commit/2a503ca2501e705430c0c35cb7c8279927c1d4d5)) + +## [1.15.8](https://github.com/msgbyte/tianji/compare/v1.15.7...v1.15.8) (2024-10-13) + + +### Features + +* add payload for feed event integration and send function ([572d96b](https://github.com/msgbyte/tianji/commit/572d96babb348858911105659bfe304e869915e4)) +* add ping animation in website realtime visitor ([6da0e6f](https://github.com/msgbyte/tianji/commit/6da0e6f415e863448cd36246eb16e1f09dcd8a79)) +* add plausible tracking(for testing) ([6474cef](https://github.com/msgbyte/tianji/commit/6474cefd896b36b872460786b11005b8deaf4436)) +* add realtime datarange which can visit data more easy ([f3d8f55](https://github.com/msgbyte/tianji/commit/f3d8f5543d4e277fe34940ce98cd552dee45f2a8)) +* add survey curl example code ([5d54ca1](https://github.com/msgbyte/tianji/commit/5d54ca1cbc01b69b9bd03d167edd0db242df350e)) +* add survey webhook ([de57242](https://github.com/msgbyte/tianji/commit/de572426ebf99e99ff97ce49caf0b8ac13b68154)) +* sdk add send feed function export ([f5933ec](https://github.com/msgbyte/tianji/commit/f5933ec0548fb4ac327152b6d7afe7ca2978bade)) +* survey add webhook url field which can send webhook when receive any survey ([f00163b](https://github.com/msgbyte/tianji/commit/f00163b2f107bcf08d4ff398fa5dbd92ac36fda8)) +* time event chart legend add some interaction ([4b78771](https://github.com/msgbyte/tianji/commit/4b7877155fd54416fb9231a02aca0e868aec97d2)) + + +### Document + +* add shacdn to website ([763810e](https://github.com/msgbyte/tianji/commit/763810e8b7e6cd41fdc0d83d28262dc7d747e4bf)) +* add sitemap to improve SEO ([384224c](https://github.com/msgbyte/tianji/commit/384224cb624030522057df22527312957665d8e9)) +* add website more language: de, fr, ja, zh-Hans ([7bda542](https://github.com/msgbyte/tianji/commit/7bda5420c5f22f6205d2dbd5086dd0bdbbc7f558)) +* change code command line style ([8c5c417](https://github.com/msgbyte/tianji/commit/8c5c417a197531c187452fc4937d034d3bdc05a7)) +* remove used blog directory ([3d9d032](https://github.com/msgbyte/tianji/commit/3d9d03296e9fb26eb363b2dba2f830f2eb9d58f3)) +* resolve build problem with update source document content ([9e6e031](https://github.com/msgbyte/tianji/commit/9e6e03117cc3bd37058563e31817034876d35450)) +* update depenpendency to resolve issue of docusaurus build ([de38363](https://github.com/msgbyte/tianji/commit/de38363315275ecbc7384c935c313940fca1d4fc)) +* upgrade openapi ([1e57905](https://github.com/msgbyte/tianji/commit/1e57905f3239cc4fb3a949b469161dc9c8d2b40c)) + + +### Others + +* add CodeExample component ([29f184c](https://github.com/msgbyte/tianji/commit/29f184c15d36e42af750261f81ad67b49fe58c0b)) +* comment sitemap to make sure its can build safe ([9b9799e](https://github.com/msgbyte/tianji/commit/9b9799ec6f846eb40762f5ca8cc0aa6c27fb7e02)) +* fix ci problem ([a32f3d9](https://github.com/msgbyte/tianji/commit/a32f3d9824a09a2d8c9e97ba211ee5d44c8e2763)) +* fix isolated-vm version ([43b4c9f](https://github.com/msgbyte/tianji/commit/43b4c9fe3763673dc54b61b01e50bc5b3d24a371)) +* fix version of postman-code-generators ([eaffe3a](https://github.com/msgbyte/tianji/commit/eaffe3ab21022215a76c3441ef0b9b2c37386227)) +* improve display of visitor map if data is too much ([9bc8c63](https://github.com/msgbyte/tianji/commit/9bc8c63fe2ea2ab080e7db3cf7d0c7636fabf8d1)) +* migrate monitor data chart to recharts and remove @ant-design/charts ([c0e2ef0](https://github.com/msgbyte/tianji/commit/c0e2ef0fe8f5520a7b935eeeb44f6be9224e56a4)) +* update ci run trigger path ([7322ad7](https://github.com/msgbyte/tianji/commit/7322ad741dcfc5c033b5057e1862e91d27244f7f)) +* update pnpm lock file to resolve some magic problem ([064dbe9](https://github.com/msgbyte/tianji/commit/064dbe9985767b32492de4264d08c26206318cd4)) +* update survey edit form ([a218c22](https://github.com/msgbyte/tianji/commit/a218c2239725deb5bcdee2e8d2de377e04dec941)) +* upgrade @radix-ui/react-scroll-area version ([9d559b9](https://github.com/msgbyte/tianji/commit/9d559b93d16c130cf58649e0f12edf9e795ba8a5)) +* upgrade @tianji/website docusaurus version ([e46f970](https://github.com/msgbyte/tianji/commit/e46f97097a593fe4bd5a8946237fd2f46fea69f6)) +* use prebuilt rather than deploy build ([e51a880](https://github.com/msgbyte/tianji/commit/e51a88044fcef7727b2e7f17c5dd9eff08329cdc)) + +## [1.15.7](https://github.com/msgbyte/tianji/compare/v1.15.6...v1.15.7) (2024-10-03) + + +### Bug Fixes + +* fix a problem which will make request list incorrect ([2d5a09c](https://github.com/msgbyte/tianji/commit/2d5a09c79cae48f62029b8767bae552376e68639)) + + +### Document + +* fix update code to new version ([1fe5009](https://github.com/msgbyte/tianji/commit/1fe50092bab5e0824c1b96b70d40d33f751f4135)) + + +### Others + +* split website from monorepo ([e09d7ee](https://github.com/msgbyte/tianji/commit/e09d7eef8788e27ac651f18dcf4d04de895a35ee)) +* update workspace config and remove unused lock file ([7301eeb](https://github.com/msgbyte/tianji/commit/7301eeb82a4fdc4ae6853dbe98c1f1d7b9b79bca)) + +## [1.15.6](https://github.com/msgbyte/tianji/compare/v1.15.5...v1.15.6) (2024-10-02) + + +### Others + +* add build dependency for build zeromq ([79b75f5](https://github.com/msgbyte/tianji/commit/79b75f55e39c057c330bc1fb0b3b15dc10e28a78)) +* improve install package time in docker build static stage ([1be03cc](https://github.com/msgbyte/tianji/commit/1be03ccf532a7dd6d23334a5666dec34e2e68d77)) +* update NODE_OPTIONS in static layer to make sure build can pass ([5eb7696](https://github.com/msgbyte/tianji/commit/5eb7696ead2da961d6ac5223a2badb48502142ba)) + +## [1.15.5](https://github.com/msgbyte/tianji/compare/v1.15.4...v1.15.5) (2024-10-01) + + +### Features + +* add error message for lighthouse ([bb0c574](https://github.com/msgbyte/tianji/commit/bb0c57489347242300c6153ed3908d1822bb692c)) +* add lighthouse score in database fields ([6c2a093](https://github.com/msgbyte/tianji/commit/6c2a0938423385d67309deefa67a3d971bf8d7c8)) +* add webhook playground ([33a0a60](https://github.com/msgbyte/tianji/commit/33a0a60eee53d1ac08cc9accc2e96f06e56ebb52)) +* add webhook playground entry ([92196e4](https://github.com/msgbyte/tianji/commit/92196e4e5bb9b183cfe85aad876115c0e17f824e)) +* add zeromq to make sure lighthouse can only run one at same time ([50a3573](https://github.com/msgbyte/tianji/commit/50a35732ff202f2452b344c2df17aba677426ec3)) + + +### Others + +* improve avatar display timing for non-avatar user ([04dc1e9](https://github.com/msgbyte/tianji/commit/04dc1e98dd448c0fd6661559722cf594ab2e751e)) +* refactor time event chart to recharts ([1337eaa](https://github.com/msgbyte/tianji/commit/1337eaa2c0ff55651a05878edd722ac1b46a5067)) +* update style of website page card ([b778f8c](https://github.com/msgbyte/tianji/commit/b778f8c982f7df8328c2fffe67783b15cde51c15)) +* upgrade shadcn cli and add recharts ([055f57e](https://github.com/msgbyte/tianji/commit/055f57e087f002b8f891053509e3cad865f1d52b)) + +## [1.15.4](https://github.com/msgbyte/tianji/compare/v1.15.3...v1.15.4) (2024-09-30) + + +### Features + +* allow rename workspace ([63e6bfe](https://github.com/msgbyte/tianji/commit/63e6bfe0d1a989479a6c4658d01ea9d84fc84b45)) + + +### Bug Fixes + +* fix login view split incorrect if not any extra login way ([b16a7c3](https://github.com/msgbyte/tianji/commit/b16a7c3c2c203394c94ccfee8e829bc7685a2457)) +* remove workspace name validation ([7c271dc](https://github.com/msgbyte/tianji/commit/7c271dc3c14fc6c751fb69b16adf6f08bfd5ac7b)) + + +### Others + +* add ignore in docker build ([ee72f74](https://github.com/msgbyte/tianji/commit/ee72f74e2c68c9baec500b78a3b994c8083abeed)) +* add logger for lighthouse ([9d3e9d8](https://github.com/msgbyte/tianji/commit/9d3e9d89db40aad4a78df8b64ad3d7bfccb94d2e)) +* add no sandbox args in puppeteer ([8b6a740](https://github.com/msgbyte/tianji/commit/8b6a74033c2838a6921c56990e13eedfbc8a559a)) +* docker add puppeteer support ([23c6915](https://github.com/msgbyte/tianji/commit/23c691541db0a51b4765dd0943488def7741c0f4)) +* downgrade alpine version to 3.19 to avoid issue ([e6df595](https://github.com/msgbyte/tianji/commit/e6df595af8ecddf734fe7e72a797921d84dad2c3)) +* improve docker build and lighthouse config ([57ebaf6](https://github.com/msgbyte/tianji/commit/57ebaf6ad361cae3403263009b94566cc7de2293)) +* improve websocket log ([b44e57d](https://github.com/msgbyte/tianji/commit/b44e57dde8d027eb05b7e8db20d490d2b62607cc)) +* try to resolve no screenshot problem by remove single process. ([fe432f1](https://github.com/msgbyte/tianji/commit/fe432f13325adf5fb4cde3dbb2f4f1218cb789e7)), closes [/github.com/GoogleChrome/lighthouse/issues/11537#issuecomment-799895027](https://github.com/msgbyte//github.com/GoogleChrome/lighthouse/issues/11537/issues/issuecomment-799895027) +* unity esbuild version to resolve vulnerabilities which cause by esbuild ([bcc215c](https://github.com/msgbyte/tianji/commit/bcc215ca5d33126b368b58a9056d02fd93d5a99a)) +* update dockerfile, carry back auto install dependency ([de09059](https://github.com/msgbyte/tianji/commit/de09059e6561a27e160f0e39e7987da8ad05edaa)) +* update translation ([9c35bca](https://github.com/msgbyte/tianji/commit/9c35bca68508f2009434ebf578f0488a948e6b75)) +* upgrade axios version to latest to resolve vulnerabilities ([d73fa10](https://github.com/msgbyte/tianji/commit/d73fa108978b3c965b07dda725fbe6ae20bc4140)) +* upgrade puppeteer to make sure can fit with alpine image chromium version ([f59793d](https://github.com/msgbyte/tianji/commit/f59793d6f18625ad66b26d0a74aaa14c604ea812)) +* upgrade puppeteer usage to fit new version ([1322741](https://github.com/msgbyte/tianji/commit/13227416c05e2eae5a1a99e5d5f3396679e83d89)) +* upgrade puppeteer version to 23.4.1 ([e942769](https://github.com/msgbyte/tianji/commit/e942769af2e2570da71eb23f3ad489e8dcb72e95)) + +## [1.15.3](https://github.com/msgbyte/tianji/compare/v1.15.2...v1.15.3) (2024-09-24) + + +### Features + +* add fixed server list ([4f2c112](https://github.com/msgbyte/tianji/commit/4f2c1129a0421934b43d3b6e02b17d629d275614)) + + +### Others + +* add language fallback to make sure its can be display correct ([31e8ce4](https://github.com/msgbyte/tianji/commit/31e8ce4ab9beec4af730b9302c0c3b861234123e)) +* clear unused code ([cdc3ce1](https://github.com/msgbyte/tianji/commit/cdc3ce122386e632f6b4350bc3dd4bdf4c17e0ed)) +* improve monitor detail style, enhance style difference ([f2ce1fb](https://github.com/msgbyte/tianji/commit/f2ce1fb10c92a2e2583ec3b36866308954958e1e)) + +## [1.15.2](https://github.com/msgbyte/tianji/compare/v1.15.1...v1.15.2) (2024-09-23) + + +### Features + +* add admin role and change most owner permission to admin ([79ed059](https://github.com/msgbyte/tianji/commit/79ed059d995da6eaabc452a0844b9acb69dc981c)) +* add label map for device type in website ([f16ccb5](https://github.com/msgbyte/tianji/commit/f16ccb56895f65dea530be295b19f04a03c8ed99)) +* add lighthouse reporter generate in website ([d29785a](https://github.com/msgbyte/tianji/commit/d29785a31184fe48913f7c49833c2d35a92c244a)) +* add status page incident model ([d182041](https://github.com/msgbyte/tianji/commit/d1820416f4924b2fc1920383b2d22b042f6e0381)) +* add workspace role permission check, hide non permission action ([4f4f9b5](https://github.com/msgbyte/tianji/commit/4f4f9b5d3f36192ea0f416997a43691674aa79fd)) + + +### Others + +* change default workspace name ([2058647](https://github.com/msgbyte/tianji/commit/205864720cdcbb5f46bee92fa6c577769a05f167)) +* fix light mode color issues ([fb75a8b](https://github.com/msgbyte/tianji/commit/fb75a8b6545a507c81acafa9cb526afccd39cd35)) +* invite add id support ([6a1f413](https://github.com/msgbyte/tianji/commit/6a1f413a384021d3c91e136be36a8c6375c74f99)) +* update README roadmap ([4a1d704](https://github.com/msgbyte/tianji/commit/4a1d704fbb88bb87f7e9db61f3da1364fb7543c0)) +* update translation ([6bf65cb](https://github.com/msgbyte/tianji/commit/6bf65cb529a2b0c52204063f2a10ad33e7b39aa5)) + +## [1.15.1](https://github.com/msgbyte/tianji/compare/v1.15.0...v1.15.1) (2024-09-19) + + +### Features + +* add custom oidc/oauth provider support ([d0afdf5](https://github.com/msgbyte/tianji/commit/d0afdf5c91d2112d177ab7bb0315586cb64ad8d7)) + + +### Bug Fixes + +* fix website cannot delete problem [#91](https://github.com/msgbyte/tianji/issues/91) ([90953e4](https://github.com/msgbyte/tianji/commit/90953e490ceea8e5256fd564e4d220b2e7da50b3)) + + +### Others + +* add account provider ([84e4722](https://github.com/msgbyte/tianji/commit/84e4722f2fc8026de25fa33d23e17db61a6d4437)) +* fix ci problem ([63484d0](https://github.com/msgbyte/tianji/commit/63484d0db59e1ccc178c7427ec7d60fd7f1484b0)) + +## [1.15.0](https://github.com/msgbyte/tianji/compare/v1.14.7...v1.15.0) (2024-09-18) + + +### Features + +* add delete workspace feature [#96](https://github.com/msgbyte/tianji/issues/96) ([2b9a14c](https://github.com/msgbyte/tianji/commit/2b9a14c969c824d630452e0e4e30834f2a9a1b47)) +* add group feature in backend ([4d39cb5](https://github.com/msgbyte/tianji/commit/4d39cb5ef4d95626e600289a1e949e78ccd7906f)) +* add lighthouse endpoint ([28d982e](https://github.com/msgbyte/tianji/commit/28d982e497bfd04351c8495ec9ddd58fc205e771)) +* add lighthouse html report endpoint ([943f7f5](https://github.com/msgbyte/tianji/commit/943f7f594ba90aa037ab5cb1b057f496e8a5fcb2)) +* add logout button in switch workspace page ([6ce2f7f](https://github.com/msgbyte/tianji/commit/6ce2f7fd4dbcd56b5a5f493108138e8c8447619b)) +* add sortable group component ([ef30750](https://github.com/msgbyte/tianji/commit/ef307508026bf516d321da933c298d87efd0b902)) +* refactor sortable group component and add edit body component ([946ecaf](https://github.com/msgbyte/tianji/commit/946ecaf9f946dfb2d85541170a7441ba6e782e5a)) + + +### Others + +* add body spaces ([12b8ba9](https://github.com/msgbyte/tianji/commit/12b8ba95b7720d384f8ae607cea0f4133d5f4fc4)) +* add new editable text component which allow to change group title ([e323e10](https://github.com/msgbyte/tianji/commit/e323e104e03569b7131dc1aac1e15c548ebe8485)) +* add sortable group component which using react-beautiful-dnd ([91ade2a](https://github.com/msgbyte/tianji/commit/91ade2ab555e43dc36ec32c7e9cb2856ffccd5ae)) +* change edit style and logic, create new MonitorPicker component ([72a1e7b](https://github.com/msgbyte/tianji/commit/72a1e7b0249c69510af7650e19bf939ed88cf550)) +* fix ci problem and remove unused code ([95b51ca](https://github.com/msgbyte/tianji/commit/95b51ca2e160bf835aafb770e0a11b8ad0fc5858)) +* improve admin style in status page ([ed2141a](https://github.com/msgbyte/tianji/commit/ed2141af22a6103ea5be850eff57be7f24d9011b)) +* improve some style in server status page ([427e9e3](https://github.com/msgbyte/tianji/commit/427e9e3eb7684a58a4c2cb597283c5a5319d9dd3)) +* refactor server status edit form with react-hook-form ([6160d7b](https://github.com/msgbyte/tianji/commit/6160d7bcb9d3bf2a8b617b422e80fe7df8839967)) +* remove sender name in notification ([f309000](https://github.com/msgbyte/tianji/commit/f309000a0c3a58a4e1df9c44803ea6a0299fe9ac)) +* remove unused code and improve display view in status page ([f5151aa](https://github.com/msgbyte/tianji/commit/f5151aa2a4185714f1f988a2bad336b90f410b69)) +* update translation ([ef3d344](https://github.com/msgbyte/tianji/commit/ef3d34423b71969f9a9afee0f64394e17a66143f)) +* update translation ([8b86dcd](https://github.com/msgbyte/tianji/commit/8b86dcdceaf738cd27bbe6253825c9ef967c675f)) +* update translation ([42f41cd](https://github.com/msgbyte/tianji/commit/42f41cdbcb2a4452fbdcf6013451d6008d6d97f1)) +* upgrade @radix-ui/react-scroll-area version ([fc1e67e](https://github.com/msgbyte/tianji/commit/fc1e67e005fa7f4502807df1ab02c49b863bc4e4)) + +## [1.14.7](https://github.com/msgbyte/tianji/compare/v1.14.6...v1.14.7) (2024-09-10) + + +### Document + +* add document and website entry in app ([f74289f](https://github.com/msgbyte/tianji/commit/f74289ff0539b4cf4141eafbc6fc6cec12529357)) + + +### Others + +* improve data table resizer width to make it more easy to use ([2e60945](https://github.com/msgbyte/tianji/commit/2e609452b55c6aa957fdff3dd96b6b617bda13ed)) +* improve notification and feed channel filter logic ([e770e42](https://github.com/msgbyte/tianji/commit/e770e428936aa84c4ce0811c820ef4e43c2cdea3)) +* update sentry feed content ([1895ac7](https://github.com/msgbyte/tianji/commit/1895ac772cec64c92c490a1a06d78df2eae05191)) + +## [1.14.6](https://github.com/msgbyte/tianji/compare/v1.14.5...v1.14.6) (2024-09-09) + + +### Features + +* add unknown integration log ([d2afa54](https://github.com/msgbyte/tianji/commit/d2afa54301bcdd6a40fe5116b8191196c9b7bb33)) + +## [1.14.5](https://github.com/msgbyte/tianji/compare/v1.14.4...v1.14.5) (2024-09-07) + + +### Bug Fixes + +* fix row header style issue ([cf4531c](https://github.com/msgbyte/tianji/commit/cf4531c5ddc1758a2d11776f058559622771b311)) + +## [1.14.4](https://github.com/msgbyte/tianji/compare/v1.14.3...v1.14.4) (2024-09-03) + + +### Document + +* fix edit page url ([8ccace1](https://github.com/msgbyte/tianji/commit/8ccace127ba6aae012e5c164dbcaa42ee299196c)) +* update manual install to include code update ([2cc098a](https://github.com/msgbyte/tianji/commit/2cc098a5f1f184fa8e627e3d8d65a7910d9967c6)) + + +### Others + +* fix ci problem which cause build failed ([c4211c2](https://github.com/msgbyte/tianji/commit/c4211c270ffd6b1a6355a871676f0abed0f1e24f)) + +## [1.14.3](https://github.com/msgbyte/tianji/compare/v1.14.2...v1.14.3) (2024-09-02) + + +### Features + +* add feed event url support ([8534ab7](https://github.com/msgbyte/tianji/commit/8534ab7ba029e4c98a642f4c927902455e97d4a9)) +* add sentry webhook integration ([546055e](https://github.com/msgbyte/tianji/commit/546055e5559cf460e7d0f1dcce835e905baafc1e)) + + +### Bug Fixes + +* fix health bar style problem in page ([01d774d](https://github.com/msgbyte/tianji/commit/01d774d3958abd5ee15631eb771e22d5771f405a)) + +## [1.14.2](https://github.com/msgbyte/tianji/compare/v1.14.1...v1.14.2) (2024-09-02) + + +### Features + +* add archive feature ([3270164](https://github.com/msgbyte/tianji/commit/3270164710179a534692eabca77285dd28d887a7)) +* add curl feed api guide ([5588aca](https://github.com/msgbyte/tianji/commit/5588aca522646d88b7c9ddb5bff98e23c1d3bc15)) +* add feed archive page ([87b4000](https://github.com/msgbyte/tianji/commit/87b4000c4791a935a0f9247d1c219529105d0801)) +* add socket state ([e095a08](https://github.com/msgbyte/tianji/commit/e095a081b949880a088fd3e2512005d71d024769)) +* feishu add markdown syntax support ([33de808](https://github.com/msgbyte/tianji/commit/33de808f3e4e6a763fc36de96f24e01055907aba)) + + +### Document + +* update Chinese translation ([9fcc6dd](https://github.com/msgbyte/tianji/commit/9fcc6dda60a6496fb1909cdc5facd0ebb60e9448)) + + +### Others + +* improve feed event report style ([88f47db](https://github.com/msgbyte/tianji/commit/88f47db118968aa323b6ee0eac6b14e4fe9aa608)) +* update translations ([9966c12](https://github.com/msgbyte/tianji/commit/9966c1277c6bc8df74d9c3ca742b9e98c7577087)) + +## [1.14.1](https://github.com/msgbyte/tianji/compare/v1.14.0...v1.14.1) (2024-08-29) + + +### Others + +* update pnpm version ([b9f5582](https://github.com/msgbyte/tianji/commit/b9f5582a02afffaff5777c85126e91e243ef82aa)) + +## [1.14.0](https://github.com/msgbyte/tianji/compare/v1.13.1...v1.14.0) (2024-08-29) + + +### Features + +* add create workspace and switch workspace ([fac0838](https://github.com/msgbyte/tianji/commit/fac0838d8c7b14c7940170b733db0a33ca297b73)) +* add delete workspace endpoint ([6fecde0](https://github.com/msgbyte/tianji/commit/6fecde0caa422c25ddbb9ec564afb44031b761da)) +* add invite endpoint ([8c8b960](https://github.com/msgbyte/tianji/commit/8c8b960f61926aae415954dbef772e263d738ec5)) +* add invite user form ([e0e0449](https://github.com/msgbyte/tianji/commit/e0e044945f02451ad2b1db8041e91a738589cd5d)) +* add tick trpc endpoint ([7f33e2d](https://github.com/msgbyte/tianji/commit/7f33e2de0d0e0e1b4c2ff5d172f5d6c89e8dcd15)) +* add unstar feed ([446ddaf](https://github.com/msgbyte/tianji/commit/446ddafa0afb534e02f767457973a1594a190f8f)) +* add workspace page ([4918071](https://github.com/msgbyte/tianji/commit/491807165c7a4bd27b30961fdb3d525187d05ca3)) + + +### Bug Fixes + +* fix a style issue which workspace switch style broken with long name ([cbdb1c4](https://github.com/msgbyte/tianji/commit/cbdb1c4a079fcd19f03750dc3379f3e1aaaeb772)) +* fix some case(maybe) can not key problem ([b64ca8b](https://github.com/msgbyte/tianji/commit/b64ca8b300f2bcbbbcbdf95b4e0d9780c1f64b1b)) +* fix virtualize table loading and column style problem ([bb84661](https://github.com/msgbyte/tianji/commit/bb846616127e8ef823441b945390327a87b2f689)) + + +### Document + +* add private-policy page ([d136460](https://github.com/msgbyte/tianji/commit/d136460e39a69510e66952e76d42bca4016337a4)) +* update openapi files ([79a7a92](https://github.com/msgbyte/tianji/commit/79a7a923d247a2581cca5b0e912bcbe35a892851)) +* update website feed feature list ([ebd1e5e](https://github.com/msgbyte/tianji/commit/ebd1e5eb6648a424f078047125f31ce3a93bff03)) + + +### Others + +* add default error style problem ([3cc678f](https://github.com/msgbyte/tianji/commit/3cc678f09ec8d743f17b7b2f296475b2d37c8841)) +* fix ci problem ([f7e1c81](https://github.com/msgbyte/tianji/commit/f7e1c8114b38c740f37e69125bd31fb26e1c2a1f)) +* fix tsconfig problem in tsx ([40df49e](https://github.com/msgbyte/tianji/commit/40df49e1dbb5afda4a2d01b94e298e4f2dfaa2d5)) +* improve healthbar display, will responsive with container size ([3990b0a](https://github.com/msgbyte/tianji/commit/3990b0a872d963900f52a97e869e7f26227c8107)) +* update translation ([e983092](https://github.com/msgbyte/tianji/commit/e9830920378c71371946d526ef20335176cc8f18)) +* upgrade @radix-ui/react-scroll-area to resolve scroll problem ([b862dd7](https://github.com/msgbyte/tianji/commit/b862dd74273faa78c65de916dce0a8fdafe9e834)) +* upgrade package manager ([fa328fb](https://github.com/msgbyte/tianji/commit/fa328fb0bfe9ef47cce1d44ae16aee2628921e30)) +* workspace switcher style and submit form reset ([5f47831](https://github.com/msgbyte/tianji/commit/5f47831f8e3f8df48fd287f6c4a23cb65270c21b)) + +## [1.13.1](https://github.com/msgbyte/tianji/compare/v1.13.0...v1.13.1) (2024-08-16) + + +### Features + +* add feed template string of survey ([22fc5f9](https://github.com/msgbyte/tianji/commit/22fc5f98f8b646d6505a7a518074f5ce3f40215f)) +* add FeedChannelPicker component ([5f6147e](https://github.com/msgbyte/tianji/commit/5f6147e3b6329c6fbeb7f8b1ac981f38fbe3e97a)) +* add survey result send to feed channel feature ([d986210](https://github.com/msgbyte/tianji/commit/d9862105edd0f528dcf91d29142eaca9d78a8001)) + + +### Document + +* remove unmaintained readme ([5447f53](https://github.com/msgbyte/tianji/commit/5447f53b303fd43f8121fe9cccf5efc0326b7ace)) + + +### Others + +* fix ci problem ([3e3dc4c](https://github.com/msgbyte/tianji/commit/3e3dc4c22d765d7eea1d38e9f85c913982c656b6)) +* remove lodash ([49d0da3](https://github.com/msgbyte/tianji/commit/49d0da3a6d54a65db7e11b1e9bb2e45fee228bdc)) +* upgrade i18next-toolkit version ([59840b5](https://github.com/msgbyte/tianji/commit/59840b5f7b1cde4e6173dc1fa3b1bf39d3f701a7)) + +## [1.13.0](https://github.com/msgbyte/tianji/compare/v1.12.1...v1.13.0) (2024-08-11) + + +### Features + +* add authjs backend support ([06d6ecd](https://github.com/msgbyte/tianji/commit/06d6ecd2a3384056be017c5608df282968802196)) +* add avatar and nickname display in user info scope ([03bc9b5](https://github.com/msgbyte/tianji/commit/03bc9b5125070d2675b422723404c96fd2ac95ad)) +* add duplicate feature for monitor ([827cf07](https://github.com/msgbyte/tianji/commit/827cf07c2a70b3437a8381ab3ee16838a348fd91)) +* add email restrict ([0a0a275](https://github.com/msgbyte/tianji/commit/0a0a27549ace51bf0b8c9ef135c50fd859980525)) +* add feed channel into search command panel ([275f30f](https://github.com/msgbyte/tianji/commit/275f30f0487a02e96289d8df5ea107bdd591212e)) +* add github auth integrate ([7f7c95b](https://github.com/msgbyte/tianji/commit/7f7c95b11c664a15732f18b28ea1d154f289fca9)) +* add logout and socketio auth ([e9c64c5](https://github.com/msgbyte/tianji/commit/e9c64c57e7b8bd8912669aef93d3958bb754a057)) +* add none in feed channel ([73dd8c2](https://github.com/msgbyte/tianji/commit/73dd8c25b7f782a882682039a3cba94526af9906)) +* add prisma migrate ([37757f6](https://github.com/msgbyte/tianji/commit/37757f6563d6de71a59aa1b021e7e29e9235eb3b)) +* add support for legacy traditional login methods ([3afac06](https://github.com/msgbyte/tianji/commit/3afac062c417bfeef0536ee49a459de96ac7ae72)) +* add survey count and feed event count ([f149642](https://github.com/msgbyte/tianji/commit/f1496429d30e13af8e810ae1dbc7ba74707d621d)) +* add virtualized data table resizer ([f1aaa70](https://github.com/msgbyte/tianji/commit/f1aaa7040e7953d85b956f398df65873d9104205)) +* add VirtualizedInfiniteDataTable and refactor survey result list ([b2dccec](https://github.com/msgbyte/tianji/commit/b2dccec2834a486dc018ac7b1d267bb327d48422)) + + +### Bug Fixes + +* fix tencentCloudAlarmMetricSchema incorrect problem ([914046a](https://github.com/msgbyte/tianji/commit/914046aefacafc0500585d55f8a2119685777ac3)) + + +### Document + +* add custom example for match text ([e4eee42](https://github.com/msgbyte/tianji/commit/e4eee420ea013068bc3d5fc0d9de436a6f69d65f)) +* update README preview images ([bb76c8e](https://github.com/msgbyte/tianji/commit/bb76c8e895d5cb2ba5c8e5889d1a8ae15a605b48)) + + +### Others + +* add error log for tencent alarm ([af47920](https://github.com/msgbyte/tianji/commit/af4792024f3adc0b152f101e066f84077c98ecf7)) +* add more log for tencent cloud alarm ([ad18666](https://github.com/msgbyte/tianji/commit/ad186668515c40e4127108a689accacc5b782760)) +* add more translations ([05c358b](https://github.com/msgbyte/tianji/commit/05c358b2e5b18d74596fac8bb7ef2a0caf06b527)) +* change all import with .js suffix, which will help nodejs(esm) to import code clear. ([d5d0446](https://github.com/msgbyte/tianji/commit/d5d04468cb210d0e2313ab66d494e09a8337a9d0)) +* fix ci issue of typescript type check ([e5c2b94](https://github.com/msgbyte/tianji/commit/e5c2b9484fb761a2dff2f1e9f3dff3368f371712)) +* fix ci problem ([c7ff366](https://github.com/msgbyte/tianji/commit/c7ff3666a7814936d8e5266df8e183fafbec6f96)) +* fix react-router version ([20e95ef](https://github.com/msgbyte/tianji/commit/20e95ef97328fa1c0a3caab6a0ae20d54480eea0)) +* remove ts-node and change to tsx ([b04ddd4](https://github.com/msgbyte/tianji/commit/b04ddd40ad483b773eeba0141b21ce80bfb4edbe)) +* translate server side code into esm ([5dca262](https://github.com/msgbyte/tianji/commit/5dca262482adaadf6e25385bba0b1061ed9d33a4)) +* update translation file ([7e38e32](https://github.com/msgbyte/tianji/commit/7e38e327bf3d8662e86a9c4320589f15d122ecd1)) +* wip: add auth.js ([3cf3cfa](https://github.com/msgbyte/tianji/commit/3cf3cfa427b1d3ff6704e8839b60781eb7c15b32)) + +## [1.12.1](https://github.com/msgbyte/tianji/compare/v1.12.0...v1.12.1) (2024-07-25) + + +### Features + +* add tencent cloud integration ([8585ea4](https://github.com/msgbyte/tianji/commit/8585ea4196e61934508f33e5120df8d854d6b18f)) + + +### Bug Fixes + +* fix code block not display well in light mode [#80](https://github.com/msgbyte/tianji/issues/80) ([0835fc5](https://github.com/msgbyte/tianji/commit/0835fc588bd0967d578abe34af9596fea2f29390)) + + +### Document + +* update pnpm version in manual install document ([9a7afed](https://github.com/msgbyte/tianji/commit/9a7afed08cb4aa80839fb83328a8ff300ac2141e)) + + +### Others + +* add fade in animation ([35a6e20](https://github.com/msgbyte/tianji/commit/35a6e20717d42ed4719b7dd441a89b079973d30e)) +* change website config tabs to shadcn ui and improve ui ([f112adc](https://github.com/msgbyte/tianji/commit/f112adc696f7d2ded5d7619cf900e8156ea92d37)) + +## [1.12.0](https://github.com/msgbyte/tianji/compare/v1.11.4...v1.12.0) (2024-07-22) + + +### Features + +* add channel feed notification ([67bfda3](https://github.com/msgbyte/tianji/commit/67bfda30bc95b5b8d11ff3994c6d097106e2c248)) +* add colorized text for server status which help user find problem ([f2b20c5](https://github.com/msgbyte/tianji/commit/f2b20c5ef9a8d46aecb51b7c33720831c4e1ddf6)) +* add custom feed integration ([765cc41](https://github.com/msgbyte/tianji/commit/765cc41c0637879c08f0cbb882d0decd03fc6e66)) +* add date range and improve report display ([6e68a80](https://github.com/msgbyte/tianji/commit/6e68a8044dbda7a391800589ebb4cc2c828a8dbc)) +* add dialog wrapper and improve display of webhook modal ([adb1cc3](https://github.com/msgbyte/tianji/commit/adb1cc391926b9fcea71f2db780e387b980b0b1d)) +* add feed channel count ([a7688f0](https://github.com/msgbyte/tianji/commit/a7688f02af6a51d3786d4c91114a0e398c880fab)) +* add feed endpoint ([f459c6b](https://github.com/msgbyte/tianji/commit/f459c6beeadaea6368e07375efa4845fe994305b)) +* add feed event item created time ([926ea98](https://github.com/msgbyte/tianji/commit/926ea980ff130ba93c491fce9445b668f88175aa)) +* add feed event notification with event and daily ([7bfd92b](https://github.com/msgbyte/tianji/commit/7bfd92be0b90cc5b9556a91f38627bdf015492d9)) +* add feed page ([96a5a33](https://github.com/msgbyte/tianji/commit/96a5a33ad61c8b7bb4e4eae535acc659632aa914)) +* add github integration support ([12fe9f0](https://github.com/msgbyte/tianji/commit/12fe9f0384ab08bc9013b22eb29140b14dae559f)) +* add integration modal ([af5f6ad](https://github.com/msgbyte/tianji/commit/af5f6ad9f5853c344ceefe7a5e34730f364bfeae)) +* add list content token ([7736bf8](https://github.com/msgbyte/tianji/commit/7736bf89dc94524d0cce6dc0b42a6ef430ecab2e)) +* add more clear job ([b6bca6c](https://github.com/msgbyte/tianji/commit/b6bca6c250ded389b863e6e11adb77e5aa1b2911)) +* add realtime feed event and desc feed list ([478d0c2](https://github.com/msgbyte/tianji/commit/478d0c2af3dd65d743a1ab36c0099a8449dc5224)) +* add VirtualList support for feed events ([caf7e9c](https://github.com/msgbyte/tianji/commit/caf7e9ca72358772f3e47b593e7553b78578b226)) +* add weekly and monthly cron job ([03904d2](https://github.com/msgbyte/tianji/commit/03904d26e08fbb755983568ed4dec7667f52982a)) +* feed add markdown support ([56bbe09](https://github.com/msgbyte/tianji/commit/56bbe09005013276c0ac7324354cb63533ecd18e)) +* github feed add star and issue support ([29939b6](https://github.com/msgbyte/tianji/commit/29939b6709e143ab9f68d008b79be38b3f13a6e7)) + + +### Bug Fixes + +* fix auditlog cannot fetch more data problem ([1b859e3](https://github.com/msgbyte/tianji/commit/1b859e31768b0f5b1a844989745e37e30d6ed478)) +* fix problem of send notification ([82bb2ad](https://github.com/msgbyte/tianji/commit/82bb2ad267ae1f2922924fddb986b9f4e619eb1e)) + + +### Document + +* update category order ([b2480b0](https://github.com/msgbyte/tianji/commit/b2480b0ed57eccbc741de0652edbda8548b68db9)) +* update roadmap ([e10cdfd](https://github.com/msgbyte/tianji/commit/e10cdfdf2612448938711b42fab2b8f160c3cab2)) +* update webhooks document ([b355a67](https://github.com/msgbyte/tianji/commit/b355a677d3e36de4f53a73c59e23ab1aa0cb690e)) +* update wechat qrcode ([503df45](https://github.com/msgbyte/tianji/commit/503df4546da7d11035f301db18650b4552d484f0)) + + +### Others + +* add document for endpoint ([537503f](https://github.com/msgbyte/tianji/commit/537503f288735de3c73f759291338ca74ce8d5d1)) +* add dynamic virtual list ([01d81f3](https://github.com/msgbyte/tianji/commit/01d81f39296b80899c4fcaadda8dffa3f3f28803)) +* add empty description message ([66ec94f](https://github.com/msgbyte/tianji/commit/66ec94fd08c24b901a0b8814cec5e246b6f6454f)) +* add env openapi default value ([685d050](https://github.com/msgbyte/tianji/commit/685d05074b9a994081eade85cceb5f3b001c74df)) +* add feed event url ([ac930cd](https://github.com/msgbyte/tianji/commit/ac930cd05e19e69e5ec9a7b059e56043685426ee)) +* add preview text ([3d9b67a](https://github.com/msgbyte/tianji/commit/3d9b67a430536d04adaab035e29bb21d4f2b0051)) +* add simple virtual list ([b7670da](https://github.com/msgbyte/tianji/commit/b7670da7db231d7f46f0cafc4ed30c2d46981c0a)) +* change create feed event to local ([ab179e9](https://github.com/msgbyte/tianji/commit/ab179e9af6f2f17f2ac93be8e046a71b67f90eb1)) +* change feed channel notifyFrequency type to enum ([2ce5597](https://github.com/msgbyte/tianji/commit/2ce5597dfe1c51cbec86c13b1b6a3fe5eecf2e53)) +* change push message in github event ([1d4aecf](https://github.com/msgbyte/tianji/commit/1d4aecff9559e30d5274073f8c64002cce54aaef)) +* fix ci problem ([865e56f](https://github.com/msgbyte/tianji/commit/865e56f40e7351d21c995f7a9c0fafbcc7b75993)) +* fix ci problem ([4d15ccc](https://github.com/msgbyte/tianji/commit/4d15cccd1b8718f862a482564703eb905f1839c2)) +* improve display in feed channel list ([15c6290](https://github.com/msgbyte/tianji/commit/15c6290587521abd6ce4a6325fc13456d1b10f42)) +* improve feed event item display ([17f87c1](https://github.com/msgbyte/tianji/commit/17f87c191a9b1fcbcb39e73e1830ccb29b6e634c)) +* improve logger and test case ([9796d42](https://github.com/msgbyte/tianji/commit/9796d428466f21adcfa42e04f04cbfe2d15aff3c)) +* remove unused code ([2f6e92d](https://github.com/msgbyte/tianji/commit/2f6e92d166ac1635b87151147c12f13146136d38)) +* remove unused size changer ([6ccd0ed](https://github.com/msgbyte/tianji/commit/6ccd0ede7b4c1bd8caad0697f66e61db8ea1feb9)) +* skip event report if not have any events ([f814691](https://github.com/msgbyte/tianji/commit/f814691538578972bdeefe574e74a8dacb261a59)) +* split integration route from feed route ([a4c31fe](https://github.com/msgbyte/tianji/commit/a4c31fe2da2b4213726579042ac93aa0547f0cc7)) +* update feed guide ([c34b012](https://github.com/msgbyte/tianji/commit/c34b0124fac27fa2e48d2705cab8f07935d7fa02)) +* update openapi base url and regenerate openapi document ([616a623](https://github.com/msgbyte/tianji/commit/616a623e40ea86e68595449f7a9d290630a5b116)) +* update tag and content ([85a2a59](https://github.com/msgbyte/tianji/commit/85a2a598d76a5835e32772f04b6b2cf0e0d6dccf)) +* update translation ([fc6ee73](https://github.com/msgbyte/tianji/commit/fc6ee733663231347a22aa7df83e4f4a454f4bd6)) +* upgrade pnpm version in ci ([a2cb8b0](https://github.com/msgbyte/tianji/commit/a2cb8b0538d69d2209faa9574c509e49ec7d55ee)) +* upgrade pnpm version in dockerfile ([1b89c3b](https://github.com/msgbyte/tianji/commit/1b89c3b5a808b57f1a695b86a8f35e4199e0ed7c)) +* upgrade pnpm version to v9.5.0 ([63de6d7](https://github.com/msgbyte/tianji/commit/63de6d7aa514a5e1a2e206785a8426336cb323fa)) + +## [1.11.4](https://github.com/msgbyte/tianji/compare/v1.11.3...v1.11.4) (2024-06-21) + + +### Features + +* add server install script usage guide ([4943d2d](https://github.com/msgbyte/tianji/commit/4943d2dd8e4495f3e03631d7f332b9a630e79b49)) +* add webhook notification ([90df8e8](https://github.com/msgbyte/tianji/commit/90df8e8e36c618868110c3b9a0119b1b69184546)) +* webhook add title and time ([a91d1ff](https://github.com/msgbyte/tianji/commit/a91d1ffffe41b61b9792d92865e8d8f65db27b0f)) + + +### Document + +* add document about server status page custom domain ([f06e788](https://github.com/msgbyte/tianji/commit/f06e788f454df877d7030e603aff5b882fcf7a82)) +* add webhook document ([61c1b0e](https://github.com/msgbyte/tianji/commit/61c1b0e06504fca8fc35c41a7bb15130e7c08f24)) +* update changelog ([ee16e6c](https://github.com/msgbyte/tianji/commit/ee16e6cd76c9138ce343b208f6c8d0026f0ee6c9)) +* update wechat qrcode ([0d2c4f9](https://github.com/msgbyte/tianji/commit/0d2c4f97f96494a4874c4b3a30bd633202f35b2f)) + + +### Others + +* update release it ([3bfd11a](https://github.com/msgbyte/tianji/commit/3bfd11a7b6b2395cbe24653fea52c415c14bc1ca)) + +## [1.11.3](https://github.com/msgbyte/tianji/compare/tianji-0.1.17...1.11.3) (2024-06-15) + + +### Bug Fixes + +* fix setting page not display correct problem ([fdce6b4](https://github.com/msgbyte/tianji/commit/fdce6b42f1e9817dba76072adaf732040bf3f8d3)) + + +### Document + +* [#68](https://github.com/msgbyte/tianji/issues/68) add document to how to install with helm ([95a8e99](https://github.com/msgbyte/tianji/commit/95a8e9968ba72f6e13db227c0b5695f6d12e388a)) +* add improve monitor reporter usage roadmap [#75](https://github.com/msgbyte/tianji/issues/75) ([caab72d](https://github.com/msgbyte/tianji/commit/caab72dac58f2f6131d195f3bdbb29e41fa8bb0f)) +* update changelog ([0deec1f](https://github.com/msgbyte/tianji/commit/0deec1fc55e30dcb1a71f835ba51b48b46310e3d)) + + +### Others + +* improve mobile display for tianji ([e9a1b61](https://github.com/msgbyte/tianji/commit/e9a1b61a7f3eec1050df9cf7e4ad3644f787091b)) +* improve sidebar hide logic ([cae0c1d](https://github.com/msgbyte/tianji/commit/cae0c1d6c094a1662e1e390962ed10b8eabe73ea)) +* update cr config ([f91110b](https://github.com/msgbyte/tianji/commit/f91110b313fb7f874813d2f76919476a4cf24631)) + +## [1.11.2](https://github.com/msgbyte/tianji/compare/v1.11.1...v1.11.2) (2024-06-07) + + +### Features + +* add createdAt field in survey download csv ([618aedf](https://github.com/msgbyte/tianji/commit/618aedf1963559c07af696fb3483d4c073ba7c29)) +* add document entry ([ad4b67c](https://github.com/msgbyte/tianji/commit/ad4b67ca459837cabcb1f274c7e10ec03bf128f5)) +* add website view count in website list ([8ac5b11](https://github.com/msgbyte/tianji/commit/8ac5b11d4962de05cefe3d5be7c014f4f8bb7c9a)) + + +### Document + +* add install script uninstall document ([bffb9d6](https://github.com/msgbyte/tianji/commit/bffb9d6729adba7fd66468e9a618b68c68d09366)) +* add prepare markdown ([98a8878](https://github.com/msgbyte/tianji/commit/98a887825f5df8385dc14c45e7e8ce2bc49c4b87)) +* update changelog ([0c5c993](https://github.com/msgbyte/tianji/commit/0c5c993236ba51dfb0242af01459f4024e2038a6)) +* update environment document ([52a8927](https://github.com/msgbyte/tianji/commit/52a89276c8ef707e5283161336296c3f040354d2)) +* update manual document ([1dafea6](https://github.com/msgbyte/tianji/commit/1dafea61c78e01ed28e665ee9048afde433415a9)) +* update manual install document [#56](https://github.com/msgbyte/tianji/issues/56) ([154b8b4](https://github.com/msgbyte/tianji/commit/154b8b4b6405c721342a681f366d3914536dc62a)) +* update manual install faq ([4564347](https://github.com/msgbyte/tianji/commit/45643476985f65e730c4906a719a3931849cd9bb)) +* update readme roadmap ([58445f9](https://github.com/msgbyte/tianji/commit/58445f9249eb8785003d381cc4527835edba485c)) +* update wechat qrcode ([26da461](https://github.com/msgbyte/tianji/commit/26da4613683394bb628f9af444bf0f620d4b563a)) + + +### Others + +* increase timeout factor of interval ([4e8d761](https://github.com/msgbyte/tianji/commit/4e8d7613a40ba20e425caa2308846f663029ffe2)) +* remove unused code ([328a4e8](https://github.com/msgbyte/tianji/commit/328a4e856cee0cf38c0beb1611ac2bc643bbd981)) +* update env example ([80713e0](https://github.com/msgbyte/tianji/commit/80713e0fceac5ab07045532cd5759ff3a54522db)) +* update sdk publish file module type ([ed0c2e9](https://github.com/msgbyte/tianji/commit/ed0c2e9d1da882acaa55854229336aa05476fd06)) +* update translation ([d74ba8d](https://github.com/msgbyte/tianji/commit/d74ba8d283cb804cd7947ffc3804608ef24c41f7)) +* upgrade prisma version to 5.14.0 ([a0ab1da](https://github.com/msgbyte/tianji/commit/a0ab1da6b60b3c608fb72b24f36b80e6ef954fe9)) + +## [1.11.1](https://github.com/msgbyte/tianji/compare/v1.11.0...v1.11.1) (2024-05-21) + + +### Bug Fixes + +* fix display problem in docker panel ([e3d0555](https://github.com/msgbyte/tianji/commit/e3d0555c454cf7e49a9301a28f65cf863fc50573)) + + +### Others + +* add survey add state ([3ecd7aa](https://github.com/msgbyte/tianji/commit/3ecd7aa171f7be0b8c7dfdeff7d140294d8819bc)) + +## [1.11.0](https://github.com/msgbyte/tianji/compare/v1.10.0...v1.11.0) (2024-05-20) + + +### Features + +* add reporter send docker info ([1dfa24d](https://github.com/msgbyte/tianji/commit/1dfa24df1b52544bee134cc6dcc94f744026bc03)) +* add server docker expend view ([c6433f3](https://github.com/msgbyte/tianji/commit/c6433f310b821b4e8b3cb55df1e9ccadda7d97f4)) + + +### Document + +* new homepage ([a20396a](https://github.com/msgbyte/tianji/commit/a20396ad97cec9a54461411e8e79af9fc6571c6b)) +* update website style ([8e96c06](https://github.com/msgbyte/tianji/commit/8e96c06d94b71c74235769eb5ff691c951cc2064)) +* uprade docs website to v3.3.2 ([eacf7fc](https://github.com/msgbyte/tianji/commit/eacf7fc56f2f23b301470eca51747d52fe1d78e4)) + + +### Others + +* add loading state for common list ([00d40c8](https://github.com/msgbyte/tianji/commit/00d40c8410c0c7c94438518344cd7c946cc64879)) +* change datatable expend icon and add transition ([74bd9ef](https://github.com/msgbyte/tianji/commit/74bd9ef3d96c1f2940c0717b61466edc7d0b44ca)) +* move dependency place ([dec6a8b](https://github.com/msgbyte/tianji/commit/dec6a8b7c59deac561e68abed46334e7a072f8c5)) +* update survey icon ([0ea7515](https://github.com/msgbyte/tianji/commit/0ea7515ad21e8d4e3fd798bf3e1341f0caf56821)) +* upgrade tianji-client-sdk version ([9a0a1ea](https://github.com/msgbyte/tianji/commit/9a0a1eacb693dd816ee68db2e217f8f6e48528c6)) +* upgrade trpc version to 10.45.2 ([7c94caf](https://github.com/msgbyte/tianji/commit/7c94caf0ed777f9558bbdc84c26eba32d60105a1)) + +## [1.10.0](https://github.com/msgbyte/tianji/compare/v1.9.4...v1.10.0) (2024-05-15) + + +### Features + +* [#62](https://github.com/msgbyte/tianji/issues/62) add title section in website ([d5895dc](https://github.com/msgbyte/tianji/commit/d5895dc4a9d9d161fd17ebeb7d55b0304101b3aa)) +* add survey backend endpoint ([2764262](https://github.com/msgbyte/tianji/commit/27642625ac9612ceeb1329d0aa3db8004af87844)) +* add survey command panel ([c9bf016](https://github.com/msgbyte/tianji/commit/c9bf016fbf0c7216aa944118efda6b6f0acde350)) +* add survey delete action ([12cd54e](https://github.com/msgbyte/tianji/commit/12cd54eafe63446471b0366d18cdaaea4ac95532)) +* add survey download feature ([eebf00f](https://github.com/msgbyte/tianji/commit/eebf00f882cd8906aff2316936da9b2496018a53)) +* add survey usage button ([2b75a8e](https://github.com/msgbyte/tianji/commit/2b75a8edad4285c524846438c79acfae7b013923)) +* add tianji event for pricing page ([6606b25](https://github.com/msgbyte/tianji/commit/6606b253d84da09e1d821c00bd97bd7b2abf7452)) +* add tianji-client-react package and useTianjiSurvey hooks which can easy to get survey info ([0fc112f](https://github.com/msgbyte/tianji/commit/0fc112fc329f0364914044f6f2727765307023ef)) +* add website pricing page ([6674c19](https://github.com/msgbyte/tianji/commit/6674c19e87d3a628a2a305f9d5db48e365189b0c)) +* survey basic fe framework and add new form ([010fd00](https://github.com/msgbyte/tianji/commit/010fd00be348cb5de0207cf1aea58fb43117130a)) +* survey detail and edit ([a596011](https://github.com/msgbyte/tianji/commit/a596011960db230a8241e4f59bac5d33597cdc9a)) +* survey sdk and openapi client ([f7f191a](https://github.com/msgbyte/tianji/commit/f7f191a53da03334b8b08844c7c51b19727828d1)) + + +### Document + +* Add one-click deploy on sealos ([#64](https://github.com/msgbyte/tianji/issues/64)) ([fc79c57](https://github.com/msgbyte/tianji/commit/fc79c5758817d95b1058e4c0093c88b262973d52)) +* clear email input when submit success in price page ([3d16d8e](https://github.com/msgbyte/tianji/commit/3d16d8edd8c89fd3d553ad461a5ae383862b80fb)) +* reduce homepage image size to improve user network ([6865a7c](https://github.com/msgbyte/tianji/commit/6865a7ca0bae30f017837a5e54a171be8ce6d85a)) +* update custom script document ([7d370b4](https://github.com/msgbyte/tianji/commit/7d370b4fc51a52ee91b2d94afcc9d2367fcc5fbb)) +* update qrcode ([ebb6c51](https://github.com/msgbyte/tianji/commit/ebb6c51f81d75b2736213403772f5418e778224e)) + + +### Others + +* add tooltip and time display on table ([9d3c034](https://github.com/msgbyte/tianji/commit/9d3c0344eee7b4309fad2e7ab827090136541d51)) +* change weight of commit list search ([cc0bd73](https://github.com/msgbyte/tianji/commit/cc0bd73ed1c48c1fed0da95fcfc2890d1cd73012)) +* change word: Countries -> Country or Region ([1ad9aa9](https://github.com/msgbyte/tianji/commit/1ad9aa95f9886342a6771cd6501d448f7801916e)) +* common list add loading state ([9780aff](https://github.com/msgbyte/tianji/commit/9780affebca31f709d4f8f20e6c8f2593f1b198a)) +* define survey model ([9143cc4](https://github.com/msgbyte/tianji/commit/9143cc468cd15d83b5419521adc9834576488b56)) +* improve mobile display ([b2fb183](https://github.com/msgbyte/tianji/commit/b2fb1832e1a2573c7e5ad8abc38a937e4e63d1a1)) +* improve mobile layout navbar display if have much features ([fd9108e](https://github.com/msgbyte/tianji/commit/fd9108e77fd8c0f4db5840512f1f0e9a73bce4d3)) +* remove unused code ([342c076](https://github.com/msgbyte/tianji/commit/342c076966dcd2ede5341067a069743c09b967d1)) +* update jwt secret generator more safe for user ([6e8c280](https://github.com/msgbyte/tianji/commit/6e8c28026e890774ce3edb49febde1f606aa0aee)) + +## [1.9.4](https://github.com/msgbyte/tianji/compare/v1.9.3...v1.9.4) (2024-05-06) + + +### Features + +* add add page selected state for add button in sidebar header ([d31d203](https://github.com/msgbyte/tianji/commit/d31d20364ef214de9f4e1c9f864303c7d440a63c)) +* add cronjob for send https certificate expired notification ([7b95c55](https://github.com/msgbyte/tianji/commit/7b95c55a70c290eba4bef464589fc406536b0354)) +* add feishu notification ([f6fc210](https://github.com/msgbyte/tianji/commit/f6fc210b65948fe19e4166ed99f083ddda3a1282)) +* monitor add trending mode ([d77e132](https://github.com/msgbyte/tianji/commit/d77e1321f43c034434564d3d1d090b8713df963a)) + + +### Bug Fixes + +* [#61](https://github.com/msgbyte/tianji/issues/61) fix font family problem ([b4872a4](https://github.com/msgbyte/tianji/commit/b4872a47e7144178cb9357b4777f01913f7d18e4)) +* fix status page markdown editor preview cannot render correct problem ([eccb322](https://github.com/msgbyte/tianji/commit/eccb322ead49a09458b5961377be99392c44aa83)) + + +### Document + +* add example of docker pull image size ([4761c2d](https://github.com/msgbyte/tianji/commit/4761c2dd20e354f649ed2e5ebfce80870e535120)) +* adding one-click deploy buttons to README.md ([#60](https://github.com/msgbyte/tianji/issues/60)) ([7cfafe4](https://github.com/msgbyte/tianji/commit/7cfafe49d792cf7884d7919def46fe30f3b36d20)) +* update release date of version 1.7.0 ([755aeaf](https://github.com/msgbyte/tianji/commit/755aeafa23d91c3bedc02a18b284f002e72d749b)) + + +### Others + +* add docker canary image version display ([dc4d88f](https://github.com/msgbyte/tianji/commit/dc4d88fc4731d3e81ef38c078bd71abc8731bc1e)) +* fix ci problem ([18f3073](https://github.com/msgbyte/tianji/commit/18f3073e94b314da086cb6fe8d617781200de7ca)) +* improve server list update at display ([51675f6](https://github.com/msgbyte/tianji/commit/51675f6129752ccdbf1e987b2228043333c9af11)) +* improve TLS chain fetch way, to make sure can get tls info correct ([cc910b7](https://github.com/msgbyte/tianji/commit/cc910b7ee67f151fa92944f10c3b1e365bc74cb3)) +* update translation ([e4b0150](https://github.com/msgbyte/tianji/commit/e4b01502bcad94d96b7ee5e8fe9a693a6a944cda)) + +## [1.9.3](https://github.com/msgbyte/tianji/compare/v1.9.2...v1.9.3) (2024-05-01) + + +### Features + +* add markdown editor for page description ([7e1faf8](https://github.com/msgbyte/tianji/commit/7e1faf82588d4a3ca8cd3423a9df2b779053eeac)) + + +### Others + +* add server page reconnect ([c950e4a](https://github.com/msgbyte/tianji/commit/c950e4a3f8fc45a73c70079813658302ee364aee)) +* update Polish translation ([#59](https://github.com/msgbyte/tianji/issues/59)) ([dd8ea66](https://github.com/msgbyte/tianji/commit/dd8ea6623fa5bb5631eb00d8c79c2e9ebbc82e1d)) + +## [1.9.2](https://github.com/msgbyte/tianji/compare/v1.9.1...v1.9.2) (2024-04-25) + + +### Features + +* add delete feature for pages ([6500f90](https://github.com/msgbyte/tianji/commit/6500f90096c6b7bd954c75621a22f9b6f87f8e07)) + + +### Bug Fixes + +* [#57](https://github.com/msgbyte/tianji/issues/57) fix domain validator must be required problem ([5767d45](https://github.com/msgbyte/tianji/commit/5767d4595ba7a233adf4a13cc7bd7b29f05d2f17)) + + +### Others + +* improve domainRegex ([8c64481](https://github.com/msgbyte/tianji/commit/8c6448132070ecc4402ca16c476d4a7e3255bd5c)) + +## [1.9.1](https://github.com/msgbyte/tianji/compare/v1.9.0...v1.9.1) (2024-04-24) + + +### Features + +* add env DISABLE_AUTO_CLEAR ([5938c38](https://github.com/msgbyte/tianji/commit/5938c38349f25037df9f14b1f06a60e42e78f965)) + + +### Bug Fixes + +* fix ping action throw error in windows ([89fed46](https://github.com/msgbyte/tianji/commit/89fed4666d81fbbcfe704aa5a88e0270eafb367a)) + + +### Document + +* add document for how to install tianji without docker [#56](https://github.com/msgbyte/tianji/issues/56) ([fe0b596](https://github.com/msgbyte/tianji/commit/fe0b596d29ecdd546110443d9101815f792db4ef)) + + +### Others + +* add daily cronjob to clear old monitor data ([3d1be2b](https://github.com/msgbyte/tianji/commit/3d1be2b5e9b20f6ba13ec71b68c047d94e2c171e)) +* improve monitor query performance ([f5c13cb](https://github.com/msgbyte/tianji/commit/f5c13cb02f1cbe760ebcb7a0c3c120eff649ded8)) +* update polish translation ([#55](https://github.com/msgbyte/tianji/issues/55)) ([47cd26c](https://github.com/msgbyte/tianji/commit/47cd26cd868ccae61207c0702faee48c786967e1)) + +## [1.9.0](https://github.com/msgbyte/tianji/compare/v1.8.2...v1.9.0) (2024-04-22) + + +### Features + +* add custom domain support for status page ([a737204](https://github.com/msgbyte/tianji/commit/a73720411cd16376e01dac4fc27fb8d29b057045)) +* add layout header which ensure title of page is correct ([120df2d](https://github.com/msgbyte/tianji/commit/120df2d8b59c50ca104e83f264193af2aacdd125)) +* add pl language support ([761bc71](https://github.com/msgbyte/tianji/commit/761bc712d6a91bce3f622c488bc312ab1ecb9cf6)) +* add server count in desktop layout ([720a1d7](https://github.com/msgbyte/tianji/commit/720a1d744ead46a28845bd3e59926ad37e02cb27)) + + +### Bug Fixes + +* fix a problem which custom domain canot direct save by domain check ([e2fc922](https://github.com/msgbyte/tianji/commit/e2fc9224c757701031b4c2ce9fe1261db73fcd7f)) +* fix monitor edit can not scroll problem ([06ddd9b](https://github.com/msgbyte/tianji/commit/06ddd9b6f108a10331fe02a8e9d89b77300780a5)) + + +### Document + +* update document ([92f8700](https://github.com/msgbyte/tianji/commit/92f870025456cf9f9cc6794ea406c0e765b42d6a)) + + +### Others + +* improve mobile display for monitor and base layout ([fc259f7](https://github.com/msgbyte/tianji/commit/fc259f7d8e9eb0927005189b4acf796520b7ad6f)) +* improve monitor retry logic. make sure send notice when retried ([ffee0b8](https://github.com/msgbyte/tianji/commit/ffee0b8799df8dc6c2537ea4d7e50884faee7591)) +* improve style of antd switch in new UI ([1bd18fa](https://github.com/msgbyte/tianji/commit/1bd18fa412955a69c8560bf93af788816dab982a)) +* redesign servers table in new design ([ccf7b8d](https://github.com/msgbyte/tianji/commit/ccf7b8d4aa5895b1430f4554924923bd122cffc4)) +* update translation ([6e8e6ac](https://github.com/msgbyte/tianji/commit/6e8e6acc70a01a40f36bf8aa2d9b0e1273726355)) +* upgrade @i18next-toolkit/cli to v1.2.1 ([f0bd4dd](https://github.com/msgbyte/tianji/commit/f0bd4dd993e18c42b5bac2e81064e2404e6853aa)) + +## [1.8.2](https://github.com/msgbyte/tianji/compare/v1.8.1...v1.8.2) (2024-04-16) + + +### Features + +* add telemetry event count ([8a3c93f](https://github.com/msgbyte/tianji/commit/8a3c93fff71a42a48041af1008f701e9868982c7)) +* add track function ([6349931](https://github.com/msgbyte/tianji/commit/6349931714063edb9d17cb45b1a200bb04d48d25)) +* Adding Portuguese (pt-PT) Translation ([#52](https://github.com/msgbyte/tianji/issues/52)) ([49fa50c](https://github.com/msgbyte/tianji/commit/49fa50c3cd12a693738aa4b6a7ef3fbdd9fc1da6)) +* telemetry add force to improve url fetch logic ([ac7b44e](https://github.com/msgbyte/tianji/commit/ac7b44e86276819be34ed91eea0b108747fa2c66)) + + +### Document + +* update openapi ([1db0832](https://github.com/msgbyte/tianji/commit/1db0832d989123a05f034a128d9717da0091f1b7)) +* update README ([8acbbb5](https://github.com/msgbyte/tianji/commit/8acbbb56f5401e62e183e73606feb82a3d2890b4)) + + +### Others + +* add title for status page and update UI style for it. ([d3ce409](https://github.com/msgbyte/tianji/commit/d3ce4090022841fa1a7f5624cd958d0ba2dc4e6c)) +* fix ci problem ([932d78b](https://github.com/msgbyte/tianji/commit/932d78b172feb4a49a2fd8079180ce9c1f38147f)) +* init client sdk ([699aedc](https://github.com/msgbyte/tianji/commit/699aedc272b4ba614280d2cfd92020734303a707)) +* remove cache for monitor badge ([10d9438](https://github.com/msgbyte/tianji/commit/10d943854143dff66810793f965d3b0992c569dd)) +* rename and add init tracker function ([cdcd6e2](https://github.com/msgbyte/tianji/commit/cdcd6e228467780f87ebab1e2af3f9e22d849196)) +* update docker registry name ([48210d2](https://github.com/msgbyte/tianji/commit/48210d29acf50e9fd1d42d23afb22ab431012f2b)) +* update translation ([9ce882b](https://github.com/msgbyte/tianji/commit/9ce882ba39b131ad5be89071bd33ff599edeaf4e)) +* upgrade i18next-toolkit and ant design icons, and update translation ([1dec411](https://github.com/msgbyte/tianji/commit/1dec411dd342ca2001d8a276bd4076d1b89a59fe)) + +## [1.8.1](https://github.com/msgbyte/tianji/compare/v1.8.0...v1.8.1) (2024-04-10) + + +### Bug Fixes + +* fix cannot add http monitor problem ([6e68cd1](https://github.com/msgbyte/tianji/commit/6e68cd1985967ba7bcda7aa65287a3367a4d1b7d)) + + +### Document + +* update preview images ([4f9cff7](https://github.com/msgbyte/tianji/commit/4f9cff747d1572b69ed7e404b9bee4f389b19581)) +* update qrcode ([7df1691](https://github.com/msgbyte/tianji/commit/7df16918903b859f6daf59dd500bc10645484d63)) + +## [1.8.0](https://github.com/msgbyte/tianji/compare/v1.7.4...v1.8.0) (2024-04-09) + + +### Features + +* add default not found page and dashboard redirect ([3c60261](https://github.com/msgbyte/tianji/commit/3c60261f379e905e8ec7ade80151f3bce00b5789)) +* add telemetry route ([f27f3f2](https://github.com/msgbyte/tianji/commit/f27f3f2f11c031cfbbea4a927e5fad3866920a7d)) +* add translation for new design ([07f64a8](https://github.com/msgbyte/tianji/commit/07f64a87ff1de761501cd0698adb038a0a3a70b3)) +* add website add button and fuse search ([68ace91](https://github.com/msgbyte/tianji/commit/68ace913217bb033537f41a32c224e1fcaf65566)) +* **v2:** add all settings pages ([fa7534a](https://github.com/msgbyte/tianji/commit/fa7534a8e00d62a462351b8dbbb06291bb7d2953)) +* **v2:** add command panel ([af4c6f6](https://github.com/msgbyte/tianji/commit/af4c6f6bd18c83389f2910b3bc7e3a901d46ca83)) +* **v2:** add delete telemetry feature ([07cb0b0](https://github.com/msgbyte/tianji/commit/07cb0b066a1eb40fd333f929dbb5dafde356cb6a)) +* **v2:** add mobile layout ([af1d99d](https://github.com/msgbyte/tianji/commit/af1d99d2ff720d48af03bfcd192ce1da627e3fdb)) +* **v2:** add monitor health bar in list ([539f242](https://github.com/msgbyte/tianji/commit/539f24244a2b76d395cdca3ee19d29b7a26896d1)) +* **v2:** add page list/add/detail ([d97c671](https://github.com/msgbyte/tianji/commit/d97c6719133ee1c2d8c6faa29d18e4520036722d)) +* **v2:** add server page ([20e1963](https://github.com/msgbyte/tianji/commit/20e19633aeacf971dfc0c4855604a2c60d58198b)) +* **v2:** add website detail ([958b1c0](https://github.com/msgbyte/tianji/commit/958b1c09329864463bef724256aee825b770147e)) +* **v2:** add website overview ([6a5de70](https://github.com/msgbyte/tianji/commit/6a5de70fdbf4cbf706274e9f40278fc8dcbd5b25)) +* **v2:** monitor feature ([402b8a6](https://github.com/msgbyte/tianji/commit/402b8a6955278937b8512307f304c428d4f3a441)) + + +### Bug Fixes + +* fix custom tracker script file route error in production ([2a3e2af](https://github.com/msgbyte/tianji/commit/2a3e2af528e492f587de561988100ada6efb2050)) +* fix custom tracker script name not display in fe problem ([5d8c187](https://github.com/msgbyte/tianji/commit/5d8c18716a1502c1db8f95850f0f762bf4455706)) +* fix website list not refresh when delete website problem ([fcb30a3](https://github.com/msgbyte/tianji/commit/fcb30a375b8f033592cc5af40602d826d527470c)) + + +### Document + +* add docker image size badge ([cc581f9](https://github.com/msgbyte/tianji/commit/cc581f9c2286eb83d7cbb2486ec6c9adc92c1c02)) +* add document about telemetry parameters ([a219f3b](https://github.com/msgbyte/tianji/commit/a219f3bdbff44f17e9a24a562770ec3b5952d54b)) + + +### Others + +* add AppRouter to make sure useUserStore run under trpc provider ([2c5ff71](https://github.com/msgbyte/tianji/commit/2c5ff71725a10d9c2c5de3c49be94afa198987d0)) +* basic new layout ([0987ca3](https://github.com/msgbyte/tianji/commit/0987ca37d53eb0bfc74360f0b6995560b6544e6c)) +* improve layout navbar ([6fbf316](https://github.com/msgbyte/tianji/commit/6fbf316c5d1af65a9d741cc5a89798229f5df9c6)) +* move add desktop layout into ([0711c3c](https://github.com/msgbyte/tianji/commit/0711c3c003d1e86130afece1178d2fb32d0d7191)) +* new layout and new router ([3f13447](https://github.com/msgbyte/tianji/commit/3f13447e1fb29cb87a4beacdf454522dfea9ea3d)) +* remvoe old router ([62cc934](https://github.com/msgbyte/tianji/commit/62cc934035683e252368af78ed21975dc2d5d329)) +* replace all old router to new router ([3bb2cc8](https://github.com/msgbyte/tianji/commit/3bb2cc871513cc553adfd0e5014a121d098f2aa3)) +* shadcn ui init ([aeee6ca](https://github.com/msgbyte/tianji/commit/aeee6ca5932504e7bc7284e4ee46687a59687502)) +* **v2:** add common sidebar ([f9a51e4](https://github.com/msgbyte/tianji/commit/f9a51e4c792519b527a467a6f3fecc72e7fbb129)) +* **v2:** login view and register and default error handler and more ([4d260dc](https://github.com/msgbyte/tianji/commit/4d260dc45e6509d649f5afa87de9eb2a5123bd6d)) + +## [1.7.4](https://github.com/msgbyte/tianji/compare/v1.7.3...v1.7.4) (2024-04-03) + + +### Features + +* add custom tracker script [#37](https://github.com/msgbyte/tianji/issues/37) ([16e0cb0](https://github.com/msgbyte/tianji/commit/16e0cb0f112091238109acf38591af23c7b76b92)) +* add delete telemetry feature [#34](https://github.com/msgbyte/tianji/issues/34) ([d862e6c](https://github.com/msgbyte/tianji/commit/d862e6cb62b716e1fa64c6b206c1961708d2ed1d)) +* add DNS monitor [#45](https://github.com/msgbyte/tianji/issues/45) ([99610cf](https://github.com/msgbyte/tianji/commit/99610cffaeb6da469ce66396d0b6cd6347087d37)) +* add telemetry events chart ([941861f](https://github.com/msgbyte/tianji/commit/941861f8856ec02935b221dbbd488226d69884c4)) + + +### Bug Fixes + +* fix etag problem for http cache if not show full telemetry number in badge ([bd4e6b5](https://github.com/msgbyte/tianji/commit/bd4e6b5b05b7154b4e34792665e66004bd426286)) + + +### Document + +* add docker pull badge in README ([cf5edd4](https://github.com/msgbyte/tianji/commit/cf5edd4ab4b2c8d47d14724902ffc541c5527b33)) +* update changelog ([6f556f3](https://github.com/msgbyte/tianji/commit/6f556f3e909ba4a09bf4625bbd9fa3a8924f6e98)) +* update qrcode ([f841361](https://github.com/msgbyte/tianji/commit/f84136106a8ca192f4c4c3bc3f0571bcba2c8d22)) + + +### Others + +* add article about telemetry introduce ([e06d7ca](https://github.com/msgbyte/tianji/commit/e06d7ca4986bc4926c39c4f3500a45da4d3b9242)) +* add default handler for delete telemetry action ([6c84a04](https://github.com/msgbyte/tianji/commit/6c84a04fb5fee6330d836e12766678702e927213)) +* add prettier-plugin-tailwindcss ([11fd1a6](https://github.com/msgbyte/tianji/commit/11fd1a616bbff0dcf3b400acf12c865c0cab9aed)) +* update qrcode ([f8f8c14](https://github.com/msgbyte/tianji/commit/f8f8c14e23b4619ce82b5e76d108282bc8c2cc01)) + +## [1.7.3](https://github.com/msgbyte/tianji/compare/v1.7.2...v1.7.3) (2024-03-10) + + +### Others + +* add fullNum props in telemetry badge ([44d5fb3](https://github.com/msgbyte/tianji/commit/44d5fb3f50c8e08fc9cc1e93133dad7883e251bd)) +* remove http cache to fit github environment(maybe) ([6f76a04](https://github.com/msgbyte/tianji/commit/6f76a04abe3e572ae56b873b7d2cd6f9e419c15a)) + +## [1.7.2](https://github.com/msgbyte/tianji/compare/v1.7.1...v1.7.2) (2024-03-09) + + +### Bug Fixes + +* fix telemetry session query failed problem ([c5dfa15](https://github.com/msgbyte/tianji/commit/c5dfa15096e80c030ecc45abef798b74c7676f72)) + + +### Document + +* add README visitor countor ([f0a1ed3](https://github.com/msgbyte/tianji/commit/f0a1ed3cdf17f7b0988c03efeb4f3d264100c227)) + +## [1.7.1](https://github.com/msgbyte/tianji/compare/v1.7.0...v1.7.1) (2024-03-09) + + +### Bug Fixes + +* fix geo library dont copy problem ([b19292e](https://github.com/msgbyte/tianji/commit/b19292e8f69e23ec4a5ba5bff8368b574dc20b53)) + +## [1.7.0](https://github.com/msgbyte/tianji/compare/v1.6.0...v1.7.0) (2024-03-08) + + +### Features + +* add alpha mode and usage page ([d3df3f2](https://github.com/msgbyte/tianji/commit/d3df3f2692b7bf97759d104f748710afd7b38f00)) +* add telemetry event count ([e79c4b4](https://github.com/msgbyte/tianji/commit/e79c4b48190c9c1867210399b13e8e4d7386fcd0)) +* add telemetry feature desc ([011ac6c](https://github.com/msgbyte/tianji/commit/011ac6c71389f0430d995396992e649fa0d5bc91)) +* add telemetry list ([5e720ab](https://github.com/msgbyte/tianji/commit/5e720abb11ae3615b07155aff320f46b5d8f95d1)) +* add telemetry metrics table ([38dd60f](https://github.com/msgbyte/tianji/commit/38dd60feeec35142cd08e4fe4fe2151b5acee400)) +* add telemetry overview ([5bad815](https://github.com/msgbyte/tianji/commit/5bad815e62352fa2ee5835233e98d3f1370890b8)) +* add telemetry trpc feature ([0bd98ad](https://github.com/msgbyte/tianji/commit/0bd98adf96fde351cda499ca215cedf36d5ec724)) +* add tianji anonymous telemetry ([ceef5c4](https://github.com/msgbyte/tianji/commit/ceef5c4b71e89bfdb60bf24406a5ab5b6eb930d7)) +* add usage button for telemetry ([daef9ff](https://github.com/msgbyte/tianji/commit/daef9ff08471f3de5a7cf975265d7fae3b28062e)) +* telemetry add telemetryId report ([355690e](https://github.com/msgbyte/tianji/commit/355690eb7505a3a3174e88eb7febd17fc0e288c7)) + + +### Bug Fixes + +* fix audit log too long will broken style problem ([b02e0b7](https://github.com/msgbyte/tianji/commit/b02e0b75d67cdf7e64e6606adad9496c2ff722af)) +* fix logic problem of retry ([ce891e9](https://github.com/msgbyte/tianji/commit/ce891e9254ef82ace8993232b50dee4ffa0673f2)) +* fix pageviews not include timezone problem ([439eb83](https://github.com/msgbyte/tianji/commit/439eb83748b2fd6386027fdf63252f0a60ab8582)) +* fix telemetry lost timezone problem ([40e1bac](https://github.com/msgbyte/tianji/commit/40e1bac541c198fcd6e35600e17fffe1cdd4ddfd)) + + +### Document + +* add framework support document ([1802717](https://github.com/msgbyte/tianji/commit/18027170b8da78a05f362c1fdcfc0cdbfa018514)) +* add Q&A for tianji reporter ([b50de6b](https://github.com/msgbyte/tianji/commit/b50de6b4e026491ecc7e33b876a68ba2282c2faf)) +* update changelog ([4b35cc0](https://github.com/msgbyte/tianji/commit/4b35cc0588e0e7d0ff54795b5c57fa9bef647b7c)) +* update docs, add account tip in intro page ([5d99bff](https://github.com/msgbyte/tianji/commit/5d99bffa87efc5b8279707041c63b6fbd6b42027)) +* update intro document ([2afb5e5](https://github.com/msgbyte/tianji/commit/2afb5e55f7689a02542af37e4e98d27f7d8bef53)) +* update keyword ([1872a5d](https://github.com/msgbyte/tianji/commit/1872a5d437a1e61a459c16372533b93f6e54a1e0)) +* update openapi ([5b36d10](https://github.com/msgbyte/tianji/commit/5b36d10346477a047a0af801a7842ea0fa84e4ae)) +* update openapi.json ([6f77452](https://github.com/msgbyte/tianji/commit/6f77452ae9a8dff1849e139c9496a026868ab746)) +* update preview/wechat image, and add feature tag of i18n ([a94e10b](https://github.com/msgbyte/tianji/commit/a94e10b93f0233377bc1ac7edbeb5d65c1fc28d1)) +* update tianji keywords ([d0c8bf9](https://github.com/msgbyte/tianji/commit/d0c8bf9f0e3e2e0b8d025998e47c0d182f3c6db1)) +* update wechat qrcode ([070f499](https://github.com/msgbyte/tianji/commit/070f4990a65cf322611f4ecce798639ba51898dc)) + + +### Others + +* add sealos config ([f87e5b3](https://github.com/msgbyte/tianji/commit/f87e5b32f9c08fa3fd31d15547ec559f3f897432)) +* improve dockerfile build ([6464a59](https://github.com/msgbyte/tianji/commit/6464a59d791b3fa93ca0b97ca0e1776274bc09b6)) +* refactor chart render and perf pageview data handle logic ([4fe61a6](https://github.com/msgbyte/tianji/commit/4fe61a6d7c11c99ce3fd4b8ec0cfa2c78cb5c077)) +* refactor website pageview endpoint to trpc ([dd0ad8c](https://github.com/msgbyte/tianji/commit/dd0ad8c5deb217673285ada601c3121815eb37b8)) +* remove alpha mode for telemetry ([e387b70](https://github.com/msgbyte/tianji/commit/e387b70c3a1511772a548f727bcadfc4a43a92d0)) +* rename MetricsTable to WebsiteMetricsTable ([1306187](https://github.com/msgbyte/tianji/commit/1306187f01e91d400f228bcd3f4c7ed4ad8576d4)) +* update docker file to multi stage builds ([ebc91d9](https://github.com/msgbyte/tianji/commit/ebc91d95317418241a01cf580c423947559f593c)) +* update translation ([49fc136](https://github.com/msgbyte/tianji/commit/49fc136fb836cfa178c20809f758bbc13cbdaa46)) +* upgrade @i18next-toolkit/cli version ([d8b23a7](https://github.com/msgbyte/tianji/commit/d8b23a746801b3f35d9d86126b37eef53bd84f17)) +* upgrade i18next-toolkit cli version and add translation of telemetry ([4711987](https://github.com/msgbyte/tianji/commit/4711987779733bc12a156767a87cfd6c19b6c8c7)) + +## [1.6.0](https://github.com/msgbyte/tianji/compare/v1.5.1...v1.6.0) (2024-02-15) + + +### Features + +* add i18n support [#3](https://github.com/msgbyte/tianji/issues/3) ([bf6b121](https://github.com/msgbyte/tianji/commit/bf6b1210417bdf4f59c8dd5d699a0f862bf356c8)) +* add timeout in http monitor [#24](https://github.com/msgbyte/tianji/issues/24) ([8d096d5](https://github.com/msgbyte/tianji/commit/8d096d55e1473af5a4459d73a0ddee37a6b7809c)) +* monitor add max retries to avoid network fluctuation ([97d55da](https://github.com/msgbyte/tianji/commit/97d55da4546aa67d96cb6d49b2fe36cb853e9ba6)) + + +### Others + +* add i18n update trigger in data filter menu ([80f7933](https://github.com/msgbyte/tianji/commit/80f7933c7b3d1db0551c483819e357c91d36b688)) +* add top padding in smtp template ([393e580](https://github.com/msgbyte/tianji/commit/393e58080a54ba8dcc055bee9a1a2ba2b152f495)) +* beautify SMTP email style ([461e23b](https://github.com/msgbyte/tianji/commit/461e23be2f57094c47b8ff937ce53c05eec501e4)) +* fix ci problem ([0f7b11d](https://github.com/msgbyte/tianji/commit/0f7b11d8422384e771c7ce2164ee5ab72ab058b5)) + +## [1.5.1](https://github.com/msgbyte/tianji/compare/v1.5.0...v1.5.1) (2024-02-08) + + +### Bug Fixes + +* add table label ellipsis to avoid much long label [#23](https://github.com/msgbyte/tianji/issues/23) ([343c05c](https://github.com/msgbyte/tianji/commit/343c05c3abf52cf70310d49b88f540fc2da5cf2f)) + + +### Document + +* update QRCode ([da5149b](https://github.com/msgbyte/tianji/commit/da5149b1665b8271d539c99aa29018dfa8a15633)) + + +### Others + +* http url allow link ([e9b783f](https://github.com/msgbyte/tianji/commit/e9b783f0478f67bee0ca81a0115d1680e38c6e3c)) +* move delete button into more button dropdown ([70c60b5](https://github.com/msgbyte/tianji/commit/70c60b5ee1f734a955ca38a3c5cf6faf44a2a93f)) + +## [1.5.0](https://github.com/msgbyte/tianji/compare/v1.4.3...v1.5.0) (2024-02-01) + + +### Features + +* add audit log ([d912c78](https://github.com/msgbyte/tianji/commit/d912c788c534a5aff956d5a4b7d1f45208cc5d5e)) +* add leaflet visitor map if user not wanna register any token ([3baa1ab](https://github.com/msgbyte/tianji/commit/3baa1ab55b8ab399fd2b5a50840ac5d0162ae28c)) +* add previous period in website overview ([8ff5db8](https://github.com/msgbyte/tianji/commit/8ff5db80e21a8489a347be7a907cb5dad8f37aba)) +* add some audit log for monitor ([a176bbc](https://github.com/msgbyte/tianji/commit/a176bbcfb47d1b1fa2d3e8afb49785338df27580)) +* add visitor map entry button in country list ([835d7ff](https://github.com/msgbyte/tianji/commit/835d7ff43d77a236443929e52b75da6a2ecc9d9c)) +* add website visitor map ([5c633ae](https://github.com/msgbyte/tianji/commit/5c633ae38c21ee5e7a4a46ae0ee93c50efd8ef93)) +* add workspace audit log db model ([7243b99](https://github.com/msgbyte/tianji/commit/7243b991ae4ed5978b365994896de26669863ab1)) +* parse ip location and storage in db ([99a6c91](https://github.com/msgbyte/tianji/commit/99a6c91b1b63eb0b07ed8f05b42175f8ea6d9f51)) + + +### Bug Fixes + +* add more space in network traffic column [#20](https://github.com/msgbyte/tianji/issues/20) ([c8671e8](https://github.com/msgbyte/tianji/commit/c8671e865699edcf4d4ebce09acc1ef3470a7357)) +* fix @tianji/shared build problem ([f5f0a56](https://github.com/msgbyte/tianji/commit/f5f0a560f1e45bddd8e4034699ef31feb00ff3b8)) +* fix a bug which data will reuse prev state in notification in production ([5c2e0e4](https://github.com/msgbyte/tianji/commit/5c2e0e46e1a081d63a53c1aaf1497798c5f3ce8b)) +* fix library path load problem ([c049a62](https://github.com/msgbyte/tianji/commit/c049a624933e26ed8f4dabfa0e9e19534f1d1829)) + + +### Document + +* fix README/website style problem ([63a3e7c](https://github.com/msgbyte/tianji/commit/63a3e7c78121a7eabb3823cdf65f832466d5e4ee)) +* improve changelog date string display ([bb26196](https://github.com/msgbyte/tianji/commit/bb26196179d6c98363771955edae2858b383533d)) +* update qrcord ([211e921](https://github.com/msgbyte/tianji/commit/211e92100b74d0b90b481fccd8361027ef87df00)) +* update wechat ([d2171ce](https://github.com/msgbyte/tianji/commit/d2171ce2b34494bc58563de2ff2dba5de8a32718)) + + +### Others + +* add audit log list empty state ([f153145](https://github.com/msgbyte/tianji/commit/f153145e0d3c19df256a0329fe1b92121d0d55a7)) +* add config ([48605ab](https://github.com/msgbyte/tianji/commit/48605abe245250dea1f6b073150caaff53e877cd)) +* add global ts-node in root path ([1063eca](https://github.com/msgbyte/tianji/commit/1063eca4ff6cd695987b2c74b38a330df4f73e65)) +* add populate ip location script ([788b43b](https://github.com/msgbyte/tianji/commit/788b43b86d1855e0f220d4a2796ad9896da406af)) +* add settings menu in user menu ([00a0459](https://github.com/msgbyte/tianji/commit/00a045915e7bc651d10273b82377856cba059fc0)) +* add shared packacage ([39550ec](https://github.com/msgbyte/tianji/commit/39550ece8339ea98915ba1b41398548d31e8c09a)) +* change project strcut to monorepo ([601c167](https://github.com/msgbyte/tianji/commit/601c167d365e73dbd6b3f036d4930bc66fdf7759)) +* change website stat endpoint to trpc ([537edcf](https://github.com/msgbyte/tianji/commit/537edcf5066fee879be2506dab17e0e583ed9729)) +* docker base image change to node 18 ([88a87a4](https://github.com/msgbyte/tianji/commit/88a87a4b58678061e6acc43b40e1713439ea54b6)) +* fix ci problem ([0502185](https://github.com/msgbyte/tianji/commit/0502185e7cb14150ae497a5bf218f85e5bc69807)) +* fixpnpm run path problem ([814c7bf](https://github.com/msgbyte/tianji/commit/814c7bf237a75cd65cb46d635a2af2aee170c6b0)) +* improve dev env support ([70f0f56](https://github.com/msgbyte/tianji/commit/70f0f56b47f71d7dd62c9c502d553a3c491cf9e2)) +* improve monitor badge display, add title in copy text ([4a1ea85](https://github.com/msgbyte/tianji/commit/4a1ea8557aa2620573806ec1bb33536a1c676ec8)) +* improve SEO description ([4ed941b](https://github.com/msgbyte/tianji/commit/4ed941b013a24ca3e1603cbec68c415438710ec4)) +* remove kill others in @tianji/shared build ([dde617f](https://github.com/msgbyte/tianji/commit/dde617fa0cdbd3e29f6c484661e7a445409e627c)) +* resolve shared import problem in production ([486587f](https://github.com/msgbyte/tianji/commit/486587f232190d271b82b576c5d60e398cc67846)) +* run db:generate at first ([8a42ef6](https://github.com/msgbyte/tianji/commit/8a42ef616ee49bb09acbef4f2c2a424acab3b1a9)) +* update build track.js output path ([74948ff](https://github.com/msgbyte/tianji/commit/74948ff53394caa80dfbda10c42638f83ed12429)) +* update docker file, move to 20-alpine ([caad780](https://github.com/msgbyte/tianji/commit/caad780e9a5d77b83bbd1822ced5cd88f93b1dcf)) +* update dockerfile, move python3 into bottom to avoid compile isolated-vm ([153e2bf](https://github.com/msgbyte/tianji/commit/153e2bf499b3bbf253c565a822573d751d8000cb)) +* update dockerfile, try use 20-bullseye ([9b0db9e](https://github.com/msgbyte/tianji/commit/9b0db9e444730e7a35fb20d95c4582972de0a706)) +* upgrade isolated-vm ([bf1604d](https://github.com/msgbyte/tianji/commit/bf1604d9eccd011559cdc678b80748985640f60e)) + +## [1.4.3](https://github.com/msgbyte/tianji/compare/v1.4.2...v1.4.3) (2024-01-14) + + +### Others + +* update base image to node:20-bookworm ([4a4f08b](https://github.com/msgbyte/tianji/commit/4a4f08b300b26c1e0bdb6c701c7103fa52a7b04d)) + +## [1.4.2](https://github.com/msgbyte/tianji/compare/v1.4.1...v1.4.2) (2024-01-14) + + +### Others + +* change docker base image to node:20-bookworm-slim ([65c37d8](https://github.com/msgbyte/tianji/commit/65c37d89a0b0276dc55dbdeccde8fd699bf15485)) +* update docker publish action config ([6b74e60](https://github.com/msgbyte/tianji/commit/6b74e605ada8296fd6e14d746de14f25aad0bf4c)) + +## [1.4.1](https://github.com/msgbyte/tianji/compare/v1.4.0...v1.4.1) (2024-01-14) + + +### Bug Fixes + +* fix pip install problem when docker build ([fb8184f](https://github.com/msgbyte/tianji/commit/fb8184fe9cde26e7991e05f3833adfec1a0a7206)) + + +### Others + +* update docker ci push conditions ([a7a7e6b](https://github.com/msgbyte/tianji/commit/a7a7e6b87dd7cbe2cfe16da53a458f65e9ac181e)) + +## [1.4.0](https://github.com/msgbyte/tianji/compare/v1.3.1...v1.4.0) (2024-01-14) + + +### Features + +* add apprise notification [#10](https://github.com/msgbyte/tianji/issues/10) ([09c3cfa](https://github.com/msgbyte/tianji/commit/09c3cfa0dc4fb6ace8be0ed1c9e10efaf442d83d)) +* add status page delete action ([551f86b](https://github.com/msgbyte/tianji/commit/551f86b8e32eb1c71ef2c73454b8d73362acce41)) +* add tcp port monitor ([9892d3a](https://github.com/msgbyte/tianji/commit/9892d3ac5ac22442f488b0cef846f1c11c3407b9)) +* allow display current response value in monitor list ([ec591f0](https://github.com/msgbyte/tianji/commit/ec591f0c54eed0af66a40c0ade64e11c32c8164c)) + + +### Bug Fixes + +* fix custom display text not apply in health bar ([699fe6c](https://github.com/msgbyte/tianji/commit/699fe6c967cf4ee5278b723c27239957631b82c4)) +* fix openai balance calc logic ([1fccb10](https://github.com/msgbyte/tianji/commit/1fccb103fd79fad312dbf9403d935e0726da95af)) +* fix status page set monitor list in create action not work problem ([e24d822](https://github.com/msgbyte/tianji/commit/e24d82224ca2bbce8e72caaa07677dd7a2b947bc)) + + +### Document + +* update zh readme ([78b2ee8](https://github.com/msgbyte/tianji/commit/78b2ee84f5f7ea232b4fe92284c53d072c2b35c0)) + + +### Others + +* add docker build ci ([18867c6](https://github.com/msgbyte/tianji/commit/18867c614e8c3e1892a98f8756848666b816c011)) +* add notification picker empty state to guide user to create notification ([a567834](https://github.com/msgbyte/tianji/commit/a567834e459838af6b40c88b086e2f71a5d52fce)) +* add url token and fix telegram adapt problem ([e6f0267](https://github.com/msgbyte/tianji/commit/e6f02677e558586633db055a72649a4db7229ff4)) +* fix ci problem ([9fec5fc](https://github.com/msgbyte/tianji/commit/9fec5fc02fe3686e962e0b234da7d62cf311685c)) +* remove unused action in docker ci task ([3d3f07c](https://github.com/msgbyte/tianji/commit/3d3f07c3806a90183756bcea48452903aec98561)) + +## [1.3.1](https://github.com/msgbyte/tianji/compare/v1.3.0...v1.3.1) (2024-01-10) + + +### Bug Fixes + +* fix manual start monitor will not loop run bug [#15](https://github.com/msgbyte/tianji/issues/15) ([bcfaab6](https://github.com/msgbyte/tianji/commit/bcfaab6959f56a5c8890937761ce909953d193aa)) + +## [1.3.0](https://github.com/msgbyte/tianji/compare/v1.2.3...v1.3.0) (2024-01-09) + + +### Features + +* add daily cron job to calc workspace usage ([1fad81f](https://github.com/msgbyte/tianji/commit/1fad81f3ab445bf276c4db234383c1d18c74d2a7)) +* add mobile nav menu support ([1e0d077](https://github.com/msgbyte/tianji/commit/1e0d077f2a26e926b7df0ae41a17d2c7101f28d7)) +* add monitor badge [#6](https://github.com/msgbyte/tianji/issues/6) ([3e9760d](https://github.com/msgbyte/tianji/commit/3e9760d895bf55e625aa35752e617b8b2701e29e)) +* add telegram notification support ([e5a5225](https://github.com/msgbyte/tianji/commit/e5a52257eac5a9cf1aeb38a976b640b7453c8c94)) +* add tokenizer for notification ([ce6fd56](https://github.com/msgbyte/tianji/commit/ce6fd56d51c50e9f6cec903a2d753bc3820d9d15)) +* add version display ([ac7f401](https://github.com/msgbyte/tianji/commit/ac7f4011cd4dfa0938aea888e7f004d93321c9af)) +* dashboard card title allow edit ([d03f60f](https://github.com/msgbyte/tianji/commit/d03f60f6a14be87d1f7f059b625faea5c030512d)) + + +### Bug Fixes + +* fix monitor data sorting problem, add order by and add created at for fetch monitor data ([023a657](https://github.com/msgbyte/tianji/commit/023a6573a1b5853ba6e5957e7204430fb0119ba3)) +* fix typo error for montior -> monitor ([90f22af](https://github.com/msgbyte/tianji/commit/90f22afe282602d48563b6ccdbc07110e319c5d2)) + + +### Document + +* update changelog dark theme color ([f585dba](https://github.com/msgbyte/tianji/commit/f585dba2063d0570ff739079d06e3aee2d3353f6)) +* update website document ([0076a3b](https://github.com/msgbyte/tianji/commit/0076a3bed7b8a4cd2bf48d39f8db29f6fa1302fa)) +* update wechat image and home page tagline ([0eedd7d](https://github.com/msgbyte/tianji/commit/0eedd7d088ad0bcc74150b733473ed5cb4035ef3)) + + +### Others + +* add db debug support ([f9ea835](https://github.com/msgbyte/tianji/commit/f9ea835dac961a3bc56b4f03236dd225310c56ab)) +* extract some monitor logic into model ([19e7ed5](https://github.com/msgbyte/tianji/commit/19e7ed516bae41ac80ca9e167f548ae7a0dabfa9)) + +## [1.2.3](https://github.com/msgbyte/tianji/compare/v1.2.2...v1.2.3) (2024-01-04) + + +### Bug Fixes + +* fix a bug which script not support makeTransferable json which data include null ([824bd89](https://github.com/msgbyte/tianji/commit/824bd89edeb1d453a120c6883b096e7e5a4c6a28)) +* fix domain schema check problem in add website server ([a04aa67](https://github.com/msgbyte/tianji/commit/a04aa67f85cee08715aeb5148eb0433eda79a64a)) + + +### Others + +* add ci action and update release it config ([1c8b4d9](https://github.com/msgbyte/tianji/commit/1c8b4d9285042c490ed40eecdd74ee09131b05c1)) +* improve server status table list display ([28dd506](https://github.com/msgbyte/tianji/commit/28dd506cb4aa10bc17c01c53b9081bdfa6864628)) +* improve UI ([deceafa](https://github.com/msgbyte/tianji/commit/deceafad4fb766e81921348b16846f1efe4715d1)) +* remove env SERVER_URL and use url to get real server url ([81e7f86](https://github.com/msgbyte/tianji/commit/81e7f8651ae90c97cdd2a6b22bdd4889f171f675)) +* update docusaurus config for seo ([927136e](https://github.com/msgbyte/tianji/commit/927136e3fb27bd015fe175d2d8289db43bc50f8a)) + +## [1.2.2](https://github.com/msgbyte/tianji/compare/v1.2.1...v1.2.2) (2024-01-02) + + +### Bug Fixes + +* fix a bug which can not raise monitor with changeActive if monitor never run before ([e38df27](https://github.com/msgbyte/tianji/commit/e38df2706ebfc59aa0a36294d7a35e4bf9ae1492)) + + +### Document + +* add demo link ([4bdc838](https://github.com/msgbyte/tianji/commit/4bdc8380f98e7fb8b83b17a9dd41dde1680edbdb)) +* update changelog and update roadmap ([df01dcb](https://github.com/msgbyte/tianji/commit/df01dcb8571b95b6642c2332623e42c4f2e57a06)) + +## [1.2.1](https://github.com/msgbyte/tianji/compare/v1.2.0...v1.2.1) (2024-01-02) + + +### Bug Fixes + +* add Suspense to fix lazy load problem ([d093df9](https://github.com/msgbyte/tianji/commit/d093df928c9137d11310affafbfa99c6b2dd74ac)) + +## [1.2.0](https://github.com/msgbyte/tianji/compare/v1.1.0...v1.2.0) (2024-01-01) + + +### Features + +* add custom monitor test code ([3e30535](https://github.com/msgbyte/tianji/commit/3e305351875d873ea03cbf25d1f7790053f4a221)) +* add custom script monitor provider ([8dfa679](https://github.com/msgbyte/tianji/commit/8dfa6791277043f39930acd94688d31db531f096)) +* add monaco editor for custom monitor provider ([96e2e00](https://github.com/msgbyte/tianji/commit/96e2e008eba1dcbcb004f298b1faa5c5948e1740)) + + +### Bug Fixes + +* fix update monitor not update runner problem ([e0e338f](https://github.com/msgbyte/tianji/commit/e0e338f819e2c543e31e8255e8ad3fd35ab94e11)) + + +### Document + +* add social block ([aedf719](https://github.com/msgbyte/tianji/commit/aedf719d68bba4d30ec4a1595ab5b6be4a7b2dfa)) +* upcase tianji slogan ([4c07229](https://github.com/msgbyte/tianji/commit/4c072290e0353bfade6c6103b311b5d75d59f975)) + + +### Others + +* change custom monitor label and formatter ([0c542fa](https://github.com/msgbyte/tianji/commit/0c542faf3bf32b536f0d814de45af8a3caacb1e0)) +* split the monitor's manager and runner into different files ([59bd1c4](https://github.com/msgbyte/tianji/commit/59bd1c42f7dbee239324ee3ea75e228859dff0b1)) +* update release it json config ([3134f61](https://github.com/msgbyte/tianji/commit/3134f615f5c80bd8aaf40f7e4f553a4f99497860)) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d2ebf50 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,92 @@ +# tianji reporter +FROM golang:1.25.11-bookworm AS reporter +ENV PATH="/usr/local/go/bin:${PATH}" +WORKDIR /app + +COPY ./reporter/ ./reporter/ + +RUN apt update +RUN cd reporter && CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o tianji-reporter . + +# Base ------------------------------ +# Pin below Alpine 3.24 until Docker Hub static scans handle it reliably. +FROM node:22.22-alpine3.23 AS base + +RUN npm install -g pnpm@10.27.0 + +# For apprise and Prisma +RUN apk add --update --no-cache python3 py3-pip g++ make openssl + +# For puppeteer +RUN apk upgrade --no-cache --available glib \ + && apk add --no-cache \ + chromium-swiftshader \ + ttf-freefont \ + font-noto-emoji \ + && apk add --no-cache \ + --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \ + font-wqy-zenhei + +# For zeromq +RUN apk add --update --no-cache curl cmake + +# Tianji frontend ------------------------------ +FROM base AS static +WORKDIR /app/tianji + +# use with --build-arg VERSION=xxxx +ARG VERSION + +COPY . . + +RUN pnpm install --filter @tianji/client... --config.dedupe-peer-dependents=false --frozen-lockfile + +ENV VITE_VERSION=$VERSION +ENV NODE_OPTIONS="--max-old-space-size=4096" + +RUN pnpm build:static + +# Tianji server ------------------------------ +FROM base AS app +WORKDIR /app/tianji + +# We don't need the standalone Chromium in alpine. +ENV PUPPETEER_SKIP_DOWNLOAD=true +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser + +COPY . . + +RUN pnpm install --filter @tianji/server... --config.dedupe-peer-dependents=false + +RUN mkdir -p ./src/server/public +COPY --from=static /app/tianji/geo /app/tianji/geo +COPY --from=static /app/tianji/src/server/public /app/tianji/src/server/public + +# Copy reporter binary from reporter stage +COPY --from=reporter /app/reporter/tianji-reporter /usr/local/bin/tianji-reporter +RUN chmod +x /usr/local/bin/tianji-reporter + +RUN pnpm build:server + +RUN CI=true pnpm prune --prod --config.dedupe-peer-dependents=false +RUN CI=true pnpm install --filter @tianji/server... --prod --offline --ignore-scripts --config.dedupe-peer-dependents=false + +RUN pip install apprise cryptography --break-system-packages +RUN rm -rf /usr/local/lib/node_modules/npm \ + /usr/local/lib/node_modules/pnpm \ + /usr/local/bin/npm \ + /usr/local/bin/npx \ + /usr/local/bin/pnpm \ + /usr/local/bin/pnpx + +RUN rm -rf ./src/client +RUN rm -rf ./website +RUN rm -rf ./reporter + +EXPOSE 12345 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost:12345/health || exit 1 + +CMD ["sh", "-c", "(cd /app/tianji/src/server && ./node_modules/.bin/prisma migrate deploy && ./node_modules/.bin/tsx ./clickhouse/scripts/apply.ts && NODE_ENV=production node ./dist/src/server/main.js) & sleep 10; /usr/local/bin/tianji-reporter --url \"http://localhost:12345\" --workspace \"clnzoxcy10001vy2ohi4obbi0\" --name \"tianji-container\" --silent > /dev/null & wait -n"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c0b10b9 --- /dev/null +++ b/README.md @@ -0,0 +1,103 @@ +[![CI](https://github.com/msgbyte/tianji/actions/workflows/ci.yaml/badge.svg)](https://github.com/msgbyte/tianji/actions/workflows/ci.yaml) +[![Build Reporter Release](https://github.com/msgbyte/tianji/actions/workflows/reporter-release.yml/badge.svg)](https://github.com/msgbyte/tianji/actions/workflows/reporter-release.yml) +[![Docker Build CI](https://github.com/msgbyte/tianji/actions/workflows/ci-docker.yaml/badge.svg)](https://github.com/msgbyte/tianji/actions/workflows/ci-docker.yaml) +![Docker Pulls](https://img.shields.io/docker/pulls/moonrailgun/tianji) +![Docker Image Size](https://img.shields.io/docker/image-size/moonrailgun/tianji) +![Tianji Visitor](https://tianji.moonrailgun.com/telemetry/clnzoxcy10001vy2ohi4obbi0/cltjxvcwm02wdut4e106maek7/badge.svg?url=http://github.com/msgbyte/tianji) + +# Tianji + + + +**All-in-One Insight Hub** + +`Website analytics` + `Uptime Monitor` + `Server Status` = `Tianji` + +All in one project! + +## Motivation + +During our observations of the website. We often need to use multiple applications together. For example, we need analysis tools such as `GA`/`umami` to check pv/uv and the number of visits to each page, we need an uptime monitor to check the network quality and connectivity of the server, and we need to use prometheus to obtain the status reported by the server to check the quality of the server. In addition, if we develop an application that allows open source deployment, we often need a telemetry system to help us collect the simplest information about other people's deployment situations. + +I think these tools should serve the same purpose, so is there an application that can integrate these common needs in a lightweight way? After all, most of the time we don't need very professional and in-depth functions. But in order to achieve comprehensive monitoring, I need to install so many services. + +It's good to specialize in one thing, if we are experts in related abilities we need such specialized tools. But for most users who only have lightweight needs, an **All-in-One** application will be more convenient and easier to use. + +## Roadmap + +- [x] website analysis +- [x] monitor + - [x] support passive reception of results +- [x] server status +- [x] problem notification +- [x] telemetry +- [x] openapi +- [x] website +- [x] team collaboration +- [x] utm track +- [x] waitlist +- [x] survey + - [ ] survey page +- [x] lighthouse report +- [x] hooks +- [x] helm install support + - [x] allow install from public +- [x] improve monitor reporter usage + - [x] uninstall guide + - [x] download from server + - [x] custom params guide + +## Preview + +![](./website/static/img/preview/1.png) + +![](./website/static/img/preview/2.png) + +![](./website/static/img/preview/3.png) + +![](./website/static/img/preview/4.png) + +![](./website/static/img/preview/5.png) + +![](./website/static/img/preview/6.png) + +## Translation + +### Add a new translation + +modify those file: +- `src/client/i18next-toolkit.config.cjs` in this file, edit country code +- `src/client/utils/i18n.ts` in this file, add for display + +Then, run below code to auto generate + +```bash +cd src/client +pnpm install +pnpm run translation:extract +pnpm run translation:translate # this will call chatgpt to run auto translation, so you need set env `OPENAPI_KEY` to make sure run correct +``` + +Then manual check translation file in `src/client/public/locales` + +### Improve translation + +Direct update `src/client/public/locales` + +## Open Source + +`Tianji` is open source with `Apache 2.0` license. + +And its inspired by `umami` license which under `MIT` and `uptime-kuma` which under `MIT` license too + +### One-Click Deployment + +[![Deploy on Hostinger](https://assets.hostinger.com/vps/deploy.svg)](https://www.hostinger.com/vps/docker-hosting?compose_url=https://github.com/msgbyte/tianji/) + +[![Deploy on Sealos](https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-template%3FtemplateName%3Dtianji) + +[![Deploy to RepoCloud](https://d16t0pc4846x52.cloudfront.net/deploylobe.svg)](https://repocloud.io/details/?app_id=270) + +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/msgbyte/tianji) + +[![Run on ClawCloud](https://raw.githubusercontent.com/ClawCloud/Run-Template/refs/heads/main/Run-on-ClawCloud.svg)](https://template.run.claw.cloud/?referralCode=TNW6NVWTLHPQ&openapp=system-fastdeploy%3FtemplateName%3Dtianji) diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..b7fde46 --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`msgbyte/tianji` +- 原始仓库:https://github.com/msgbyte/tianji +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/apps/appstore-review/.gitignore b/apps/appstore-review/.gitignore new file mode 100644 index 0000000..aefa952 --- /dev/null +++ b/apps/appstore-review/.gitignore @@ -0,0 +1,4 @@ +published_reviews.json +publisher.json +config.json +lib diff --git a/apps/appstore-review/README.md b/apps/appstore-review/README.md new file mode 100644 index 0000000..0d7a5ab --- /dev/null +++ b/apps/appstore-review/README.md @@ -0,0 +1,3 @@ +## appstore review to tianji survey + +Quick start and fork from `https://github.com/TradeMe/ReviewMe` diff --git a/apps/appstore-review/bin/appreview.js b/apps/appstore-review/bin/appreview.js new file mode 100644 index 0000000..b8da333 --- /dev/null +++ b/apps/appstore-review/bin/appreview.js @@ -0,0 +1,25 @@ +#! /usr/bin/env node +var fs = require('fs'); +var path = require('path'); +var reviewer = require('../lib/index'); +var program = require('commander'); + +var configFile; +var version = JSON.parse(fs.readFileSync(path.resolve(__dirname, './../package.json'), 'utf8')).version; + +program + .version(version, '-v, --version') + .arguments('') + .action(function (file) { + configFile = file; + }) + .parse(process.argv); + +if (typeof configFile === 'undefined') { + console.error('No config file specified'); + process.exit(1); +} + +console.log('Loading config:', configFile) +var config = JSON.parse(fs.readFileSync(configFile)); +reviewer.start(config); diff --git a/apps/appstore-review/package.json b/apps/appstore-review/package.json new file mode 100644 index 0000000..58b777c --- /dev/null +++ b/apps/appstore-review/package.json @@ -0,0 +1,30 @@ +{ + "name": "tianji-appstore-review", + "version": "1.0.4", + "description": "", + "main": "index.js", + "bin": { + "appreview": "bin/appreview.js" + }, + "files": [ + "bin", + "lib" + ], + "scripts": { + "dev": "tsx ./src/dev.ts", + "build": "tsc", + "prepare": "tsc", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "moonrailgun ", + "license": "MIT", + "dependencies": { + "app-store-scraper": "^0.18.0", + "axios": "1.7.7", + "commander": "^4.1.1", + "google-play-scraper": "^9.1.1", + "googleapis": "^144.0.0", + "tianji-client-sdk": "workspace:*" + } +} diff --git a/apps/appstore-review/src/appstorereviews.ts b/apps/appstore-review/src/appstorereviews.ts new file mode 100644 index 0000000..522c0cf --- /dev/null +++ b/apps/appstore-review/src/appstorereviews.ts @@ -0,0 +1,264 @@ +import axios from 'axios'; +import { markReviewAsPublished, reviewPublished } from './reviews'; +import { regions } from './regions'; +import { AppInformation, AppstoreConfig, Review } from './types'; +import { postToTianji } from './tianji'; + +const DEFAULT_INTERVAL_SECONDS = 3600; + +export const startReview = ( + config: AppstoreConfig, + firstRun: boolean +): void => { + if (config.regions === true) { + try { + config.regions = regions; + } catch (err) { + config.regions = ['us']; + } + } + + config.regions = config.regions || ['us']; + config.interval = config.interval || DEFAULT_INTERVAL_SECONDS; + + config.regions.forEach(async (region, i) => { + try { + const globalAppInformation = await fetchAppInformation(config, region); + const appInformation = { ...globalAppInformation }; + + try { + const reviews = await fetchAppStoreReviews(config, appInformation); + if (firstRun) { + reviews.forEach((review) => markReviewAsPublished(config, review)); + + if (config.dryRun && reviews.length > 0) { + publishReview( + appInformation, + config, + reviews[reviews.length - 1], + true + ); + } + } else { + handleFetchedAppStoreReviews(config, appInformation, reviews); + } + } catch (error) { + if (config.verbose) { + console.error( + `ERROR: Failed to fetch App Store reviews (${config.appId}) (${appInformation.region})`, + error + ); + } + } + + const intervalSeconds = + (config.interval ?? DEFAULT_INTERVAL_SECONDS) + i * 10; + + setInterval(async () => { + if (config.verbose) { + console.log(`INFO: [${config.appId}] Fetching App Store reviews`); + } + + try { + const reviews = await fetchAppStoreReviews(config, appInformation); + handleFetchedAppStoreReviews(config, appInformation, reviews); + } catch (error) { + if (config.verbose) { + console.error( + `ERROR: Failed to fetch App Store reviews during interval (${config.appId}) (${appInformation.region})`, + error + ); + } + } + }, intervalSeconds * 1000); + } catch (error) { + if (config.verbose) { + console.error( + `ERROR: Failed to fetch app information (${config.appId}) (${region})`, + error + ); + } + } + }); +}; + +const fetchAppStoreReviewsByPage = ( + config: AppstoreConfig, + appInformation: AppInformation, + page: number +): Promise => { + const url = `https://itunes.apple.com/${appInformation.region}/rss/customerreviews/page=${page}/id=${config.appId}/sortBy=mostRecent/json`; + + return axios + .get(url) + .then((res) => { + const rss = res.data; + const entries = rss.feed.entry; + if (!entries || entries.length === 0) { + if (config.verbose) { + console.log( + `INFO: No reviews from App Store (${config.appId}) (${appInformation.region})` + ); + } + return []; + } + + if (config.verbose) { + console.log( + `INFO: Received reviews from App Store (${config.appId}) (${appInformation.region})` + ); + } + + const reviews = entries + .filter((entry: any) => !isAppInformationEntry(entry)) + .reverse() + .map((entry: any) => parseAppStoreReview(entry, appInformation)); + + return reviews; + }) + .catch((error) => { + if (config.verbose) { + console.error( + `ERROR: Error fetching reviews from App Store (${config.appId}) (${appInformation.region})`, + error + ); + } + return []; + }); +}; + +export const fetchAppStoreReviews = async ( + config: AppstoreConfig, + appInformation: AppInformation +): Promise => { + let page = 1; + const allReviews: Review[] = []; + + while (page <= 10) { + try { + const reviews = await fetchAppStoreReviewsByPage( + config, + appInformation, + page + ); + allReviews.push(...reviews); + + if (reviews.length === 0) { + break; + } + } catch (error) { + if (config.verbose) { + console.error( + `ERROR: Failed to fetch App Store reviews for page ${page} (${config.appId}) (${appInformation.region})`, + error + ); + } + // Continue to next page even if current page fails + } + + page++; + } + + return allReviews; +}; + +export const handleFetchedAppStoreReviews = ( + config: AppstoreConfig, + appInformation: AppInformation, + reviews: Review[] +): void => { + if (config.verbose) { + console.log( + `INFO: Handling fetched reviews for (${config.appId}) (${appInformation.region})` + ); + } + reviews.forEach((review) => + publishReview(appInformation, config, review, false) + ); +}; + +const parseAppStoreReview = ( + rssItem: any, + appInformation: AppInformation +): Review => { + return { + id: rssItem.id.label, + version: rssItem['im:version']?.label, + title: rssItem.title.label, + text: rssItem.content.label, + rating: parseInt(rssItem['im:rating']?.label || '-1', 10), + author: rssItem.author?.name.label || '', + link: rssItem.author?.uri.label || appInformation.appLink || '', + storeName: 'App Store', + }; +}; + +const publishReview = ( + appInformation: AppInformation, + config: AppstoreConfig, + review: Review, + force: boolean +): void => { + if (!reviewPublished(config, review) || force) { + if (config.verbose) { + console.log(`INFO: New review: ${JSON.stringify(review)}`); + } + + postToTianji(review, config, appInformation); + markReviewAsPublished(config, review); + } else { + if (config.verbose) { + console.log(`INFO: Review already published: ${review.text}`); + } + } +}; + +export const fetchAppInformation = ( + config: AppstoreConfig, + region: string +): Promise => { + const url = `https://itunes.apple.com/lookup?id=${config.appId}&country=${region}`; + const appInformation: AppInformation = { + // appName: config.appName, + // appIcon: config.appIcon, + // appLink: config.appLink, + appName: '', + appIcon: '', + appLink: '', + region, + }; + + return axios + .get(url) + .then(({ data }) => { + const entries = data.results; + if (!entries || entries.length === 0) { + if (config.verbose) + console.log(`INFO: No data from App Store (${config.appId})`); + return appInformation; + } + + if (config.verbose) + console.log(`INFO: Received app data from App Store (${config.appId})`); + const entry = entries[0]; + appInformation.appName = + appInformation.appName || entry.trackCensoredName; + appInformation.appIcon = appInformation.appIcon || entry.artworkUrl100; + appInformation.appLink = appInformation.appLink || entry.trackViewUrl; + + return appInformation; + }) + .catch((error) => { + if (config.verbose) { + console.error( + `ERROR: Error fetching app data from App Store (${config.appId})`, + error + ); + } + return appInformation; + }); +}; + +const isAppInformationEntry = (entry: any): boolean => { + return !!entry['im:name']; +}; diff --git a/apps/appstore-review/src/const.ts b/apps/appstore-review/src/const.ts new file mode 100644 index 0000000..a940022 --- /dev/null +++ b/apps/appstore-review/src/const.ts @@ -0,0 +1,7 @@ +export const REVIEWS_LIMIT = 1000; +export const DEFAULT_INTERVAL_SECONDS = 300; + +export const REVIEWS_STORES = { + APP_STORE: 'app-store', + GOOGLE_PLAY: 'google-play', +}; diff --git a/apps/appstore-review/src/dev.ts b/apps/appstore-review/src/dev.ts new file mode 100644 index 0000000..da7a19f --- /dev/null +++ b/apps/appstore-review/src/dev.ts @@ -0,0 +1,5 @@ +import { start } from './'; +import fs from 'fs'; + +var config = JSON.parse(String(fs.readFileSync('./config.json'))); +start(config); diff --git a/apps/appstore-review/src/googleplayreviews.ts b/apps/appstore-review/src/googleplayreviews.ts new file mode 100644 index 0000000..97babed --- /dev/null +++ b/apps/appstore-review/src/googleplayreviews.ts @@ -0,0 +1,201 @@ +import { google } from 'googleapis'; +// @ts-ignore +import playScraper from 'google-play-scraper'; +import fs from 'fs'; +import { markReviewAsPublished, reviewPublished } from './reviews'; +import { DEFAULT_INTERVAL_SECONDS } from './const'; +import { AppInformation, GooglePlayConfig, Review } from './types'; +import { postToTianji } from './tianji'; + +export const startReview = async ( + config: GooglePlayConfig, + firstRun: boolean +): Promise => { + const appInformation: AppInformation = { + appName: '', + appIcon: '', + }; + + try { + // Scrape Google Play for app information + const appData = await playScraper.app({ appId: config.appId }); + appInformation.appName = appData.title; + appInformation.appIcon = appData.icon; + + try { + const reviews = await fetchGooglePlayReviews(config, appInformation); + if (firstRun) { + reviews.forEach((review) => { + markReviewAsPublished(config, review); + }); + + if (config.dryRun && reviews.length > 0) { + publishReview( + appInformation, + config, + reviews[reviews.length - 1], + true + ); + } + } else { + handleFetchedGooglePlayReviews(config, appInformation, reviews); + } + } catch (error) { + if (config.verbose) { + console.error( + `ERROR: Failed to fetch Google Play reviews (${config.appId})`, + error + ); + } + } + + const intervalSeconds = config.interval || DEFAULT_INTERVAL_SECONDS; + setInterval(async () => { + if (config.verbose) + console.log(`INFO: [${config.appId}] Fetching Google Play reviews`); + + try { + const reviews = await fetchGooglePlayReviews(config, appInformation); + handleFetchedGooglePlayReviews(config, appInformation, reviews); + } catch (error) { + if (config.verbose) { + console.error( + `ERROR: Failed to fetch Google Play reviews during interval (${config.appId})`, + error + ); + } + } + }, intervalSeconds * 1000); + } catch (error) { + console.error( + `ERROR: [${config.appId}] Could not scrape Google Play, ${error}` + ); + } +}; + +const publishReview = ( + appInformation: AppInformation, + config: GooglePlayConfig, + review: Review, + force: boolean +): void => { + if (!reviewPublished(config, review) || force) { + if (config.verbose) { + console.log(`INFO: Received new review: ${JSON.stringify(review)}`); + } + + postToTianji(review, config, appInformation); + markReviewAsPublished(config, review); + } else { + if (config.verbose) + console.log(`INFO: Review already published: ${review.text}`); + } +}; + +export const handleFetchedGooglePlayReviews = ( + config: GooglePlayConfig, + appInformation: AppInformation, + reviews: Review[] +): void => { + if (config.verbose) { + console.log(`INFO: [${config.appId}] Handling fetched reviews`); + } + + reviews.forEach((review) => { + publishReview(appInformation, config, review, false); + }); +}; + +export const fetchGooglePlayReviews = ( + config: GooglePlayConfig, + appInformation: AppInformation +): Promise => { + return new Promise((resolve, reject) => { + if (config.verbose) + console.log(`INFO: Fetching Google Play reviews for ${config.appId}`); + + const scopes = ['https://www.googleapis.com/auth/androidpublisher']; + let publisherJson: any; + + if (typeof config.publisherKey === 'object') { + publisherJson = config.publisherKey; + } else { + try { + publisherJson = JSON.parse( + fs.readFileSync(config.publisherKey, 'utf8') + ); + } catch (e) { + console.warn(e); + reject(e); + return; + } + } + + let jwt; + try { + jwt = new google.auth.JWT( + publisherJson.client_id, + undefined, + publisherJson.private_key, + scopes + ); + } catch (e) { + console.warn(e); + reject(e); + return; + } + + jwt.authorize((err) => { + if (err) { + console.error(err); + reject(err); + return; + } + + google.androidpublisher('v3').reviews.list( + { + auth: jwt, + packageName: config.appId, + }, + (err, resp) => { + if (err) { + console.error( + `ERROR: [${config.appId}] Could not fetch Google Play reviews, ${err}` + ); + reject(err); + return; + } + + if (config.verbose) + console.log( + `INFO: [${config.appId}] Received reviews from Google Play` + ); + + if (!resp?.data?.reviews) { + resolve([]); + return; + } + + const reviews = resp.data.reviews.map((review) => { + const comment = review.comments?.[0].userComment; + + return { + id: String(review.reviewId), + author: String(review.authorName), + version: String(comment?.appVersionName), + versionCode: Number(comment?.appVersionCode), + osVersion: Number(comment?.androidOsVersion), + device: String(comment?.deviceMetadata?.productName), + text: String(comment?.text), + rating: Number(comment?.starRating), + link: `https://play.google.com/store/apps/details?id=${config.appId}&reviewId=${review.reviewId}`, + storeName: 'Google Play', + } satisfies Review; + }); + + resolve(reviews); + } + ); + }); + }); +}; diff --git a/apps/appstore-review/src/index.ts b/apps/appstore-review/src/index.ts new file mode 100644 index 0000000..afe9dfa --- /dev/null +++ b/apps/appstore-review/src/index.ts @@ -0,0 +1,22 @@ +import * as reviews from './reviews'; +import { Config } from './types'; + +export function start(config: Config) { + for (let i = 0; i < config.apps.length; i++) { + const app = config.apps[i]; + + reviews.start({ + verbose: config.verbose, + dryRun: config.dryRun, + interval: config.interval, + tianji: config.tianji, + + // @ts-ignore + publisherKey: app.publisherKey, + appId: app.appId, + store: app.store, + // @ts-ignore + regions: app.regions, + }); + } +} diff --git a/apps/appstore-review/src/regions.ts b/apps/appstore-review/src/regions.ts new file mode 100644 index 0000000..1dea218 --- /dev/null +++ b/apps/appstore-review/src/regions.ts @@ -0,0 +1,166 @@ +export const regions = [ + 'cl', + 'us', + 'bo', + 'bh', + 'az', + 'fm', + 'dz', + 'bd', + 'co', + 'bz', + 'at', + 'mz', + 'ai', + 'au', + 'cz', + 'ca', + 'kn', + 'bf', + 'by', + 'al', + 'pa', + 'fr', + 'ie', + 'bm', + 'mu', + 'ms', + 'ne', + 'bb', + 'bw', + 'ec', + 'uy', + 'sb', + 'pt', + 'ke', + 'si', + 'bg', + 'sa', + 'ag', + 'cv', + 'se', + 'bj', + 'mt', + 'td', + 'ye', + 'cm', + 'vn', + 'lr', + 'bs', + 'is', + 'il', + 'ky', + 'sl', + 'no', + 'hr', + 'kh', + 'ar', + 'vc', + 'ug', + 'py', + 'kg', + 'nl', + 'mx', + 'tt', + 'lt', + 'cg', + 'bt', + 'my', + 'jp', + 'gy', + 'ci', + 'ph', + 'sg', + 'tz', + 'na', + 'lb', + 'ly', + 'ni', + 'qa', + 'pg', + 'lv', + 'kz', + 'dk', + 'la', + 'bn', + 'gh', + 'tr', + 'th', + 'be', + 'mg', + 'uz', + 'cy', + 'md', + 've', + 'ee', + 'kr', + 'ro', + 'mm', + 'mv', + 'ch', + 'ao', + 'gd', + 'am', + 'tj', + 'de', + 'mk', + 'cn', + 'hn', + 'sv', + 'mn', + 'br', + 'do', + 'zw', + 'id', + 'sk', + 'st', + 'np', + 'pl', + 'vg', + 'gb', + 'gr', + 'pk', + 'hu', + 'nz', + 'dm', + 'mo', + 'et', + 'tn', + 'fi', + 'sn', + 'in', + 'tc', + 'gt', + 'lk', + 'jo', + 'it', + 'ru', + 'fj', + 'za', + 'rs', + 'kw', + 'sr', + 'ae', + 'cr', + 'mw', + 'ml', + 'gw', + 'sc', + 'li', + 'eg', + 'lu', + 'sz', + 'jm', + 'es', + 'ps', + 'hk', + 'ng', + 'pe', + 'ua', + 'pw', + 'lc', + 'tm', + 'gm', + 'om', + 'tw', +]; diff --git a/apps/appstore-review/src/reviews.ts b/apps/appstore-review/src/reviews.ts new file mode 100644 index 0000000..689e64b --- /dev/null +++ b/apps/appstore-review/src/reviews.ts @@ -0,0 +1,88 @@ +import * as appstore from './appstorereviews'; +import * as googlePlay from './googleplayreviews'; +import fs from 'fs'; +import { AppstoreConfig, GooglePlayConfig } from './types'; + +interface Review { + id: string; +} + +const REVIEWS_LIMIT = 5000; + +let publishedReviews: Record; + +try { + publishedReviews = JSON.parse( + fs.readFileSync('./published_reviews.json', 'utf8') + ); +} catch (err) { + publishedReviews = {}; +} + +export const start = (config: AppstoreConfig | GooglePlayConfig): void => { + if (config.store === 'app-store') { + appstore.startReview(config, !publishedReviews[config.appId]); + console.log('Start Review:', config.store, config.appId); + } else { + googlePlay.startReview(config, !publishedReviews[config.appId]); + console.log('Start Review:', config.store, config.appId); + } +}; + +export const markReviewAsPublished = ( + config: AppstoreConfig | GooglePlayConfig, + review: Review +): void => { + if (!review || !review.id || reviewPublished(config, review)) return; + + if (!publishedReviews[config.appId]) { + publishedReviews[config.appId] = []; + } + + if (config.verbose) { + console.log( + `INFO: Checking if we need to prune published reviews have (${publishedReviews[config.appId].length}) limit (${REVIEWS_LIMIT})` + ); + } + + if (publishedReviews[config.appId].length >= REVIEWS_LIMIT) { + publishedReviews[config.appId] = publishedReviews[config.appId].slice( + 0, + REVIEWS_LIMIT + ); + } + + publishedReviews[config.appId].unshift(review.id); + + if (config.verbose) { + console.log( + `INFO: Review marked as published: ${JSON.stringify(publishedReviews[config.appId])}` + ); + } + + fs.writeFileSync( + './published_reviews.json', + JSON.stringify(publishedReviews), + { flag: 'w' } + ); +}; + +export const reviewPublished = ( + config: AppstoreConfig | GooglePlayConfig, + review: Review +): boolean => { + if (!review || !review.id || !publishedReviews[config.appId]) { + return false; + } + + return publishedReviews[config.appId].includes(review.id); +}; + +export const getPublishedReviews = (): Record => { + return publishedReviews; +}; + +export const resetPublishedReviews = (): Record => { + publishedReviews = {}; + return publishedReviews; +}; diff --git a/apps/appstore-review/src/tianji.ts b/apps/appstore-review/src/tianji.ts new file mode 100644 index 0000000..1e6aec6 --- /dev/null +++ b/apps/appstore-review/src/tianji.ts @@ -0,0 +1,22 @@ +import { AppInformation, AppstoreConfig, GooglePlayConfig } from './types'; +import { submitSurvey, initOpenapiSDK } from 'tianji-client-sdk'; + +export async function postToTianji( + review: any, + config: GooglePlayConfig | AppstoreConfig, + appInformation: AppInformation +) { + initOpenapiSDK(config.tianji.baseUrl); + + const payload = { + ...review, + ...appInformation, + }; + + console.log('Report to tianji:', payload); + await submitSurvey( + config.tianji.workspaceId, + config.tianji.surveyId, + payload + ); +} diff --git a/apps/appstore-review/src/types.ts b/apps/appstore-review/src/types.ts new file mode 100644 index 0000000..d55990a --- /dev/null +++ b/apps/appstore-review/src/types.ts @@ -0,0 +1,67 @@ +export type StoreType = 'app-store' | 'google-play'; + +interface TianjiConfig { + baseUrl: string; + workspaceId: string; + surveyId: string; +} + +export interface Config { + tianji: TianjiConfig; + apps: ( + | { + appId: string; + publisherKey: string; + store: 'google-play'; + } + | { + appId: string; + regions: string[] | false; + store: 'app-store'; + } + )[]; + verbose?: boolean; + dryRun?: boolean; + interval?: number; +} + +export interface AppstoreConfig { + tianji: TianjiConfig; + appId: string; + regions: string[] | true; + store: 'app-store'; + verbose?: boolean; + dryRun?: boolean; + interval?: number; +} + +export interface GooglePlayConfig { + tianji: TianjiConfig; + appId: string; + publisherKey: string | object; + store: 'google-play'; + verbose?: boolean; + dryRun?: boolean; + interval?: number; +} + +export interface AppInformation { + appName: string; + appIcon: string; + appLink?: string; + region?: string; +} + +export interface Review { + id: string; + author: string; + version: string; + versionCode?: number; + osVersion?: number; + device?: string; + title?: string; + text: string; + rating: number; + link: string; + storeName: 'Google Play' | 'App Store'; +} diff --git a/apps/appstore-review/tsconfig.json b/apps/appstore-review/tsconfig.json new file mode 100644 index 0000000..cdf45c1 --- /dev/null +++ b/apps/appstore-review/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./lib", + "noEmit": false + }, + "include": [ + "./src/**/*" + ] +} diff --git a/apps/daily-ai-trigger/.editorconfig b/apps/daily-ai-trigger/.editorconfig new file mode 100644 index 0000000..a727df3 --- /dev/null +++ b/apps/daily-ai-trigger/.editorconfig @@ -0,0 +1,12 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = tab +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.yml] +indent_style = space diff --git a/apps/daily-ai-trigger/.gitignore b/apps/daily-ai-trigger/.gitignore new file mode 100644 index 0000000..3b0fe33 --- /dev/null +++ b/apps/daily-ai-trigger/.gitignore @@ -0,0 +1,172 @@ +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +\*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +\*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +\*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +\*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.cache +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +.cache/ + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp +.cache + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.\* + +# wrangler project + +.dev.vars +.wrangler/ diff --git a/apps/daily-ai-trigger/.prettierrc b/apps/daily-ai-trigger/.prettierrc new file mode 100644 index 0000000..5c7b5d3 --- /dev/null +++ b/apps/daily-ai-trigger/.prettierrc @@ -0,0 +1,6 @@ +{ + "printWidth": 140, + "singleQuote": true, + "semi": true, + "useTabs": true +} diff --git a/apps/daily-ai-trigger/README.md b/apps/daily-ai-trigger/README.md new file mode 100644 index 0000000..1949f1f --- /dev/null +++ b/apps/daily-ai-trigger/README.md @@ -0,0 +1,80 @@ +# Daily AI Trigger (for Tianji) + +A Cloudflare Workers-based scheduled trigger project for executing timed Tianji Survey AI-related tasks and summary generation. + +## Features + +- Built on Cloudflare Workers +- Supports scheduled task triggering +- Developed with TypeScript + +## Development Requirements + +- Node.js +- pnpm +- Wrangler CLI (Cloudflare Workers CLI tool) + +## Installation + +```bash +pnpm install +``` + +## Usage + +### Development + +```bash +npm run dev:scheduled +``` + +### Deployment + +```bash +# Deploy to Cloudflare Workers +npm run deploy +``` + +## Environment Variables + +The project uses a `.dev.vars` file to manage local development environment variables. When deploying to production, ensure these environment variables are properly set in the Cloudflare Workers console. + +### Required Environment Variables + +#### Tianji Configuration +- `BASE_URL`: Base URL for Tianji API +- `API_KEY`: Tianji API key +- `WORKSPACE_ID`: Workspace ID, can be found in workspace information +- `SURVEY_ID`: Tianji Survey ID +- `PAYLOAD_CONTENT_FIELD`: Field name for the main survey content (target for classification and translation) +- `LANGUAGE`: Language setting, e.g., 'en', helps Tianji AI generate results in the corresponding language + +#### Feishu Configuration (Optional) +- `FEISHU_WEBHOOK`: Feishu bot webhook URL +- `FEISHU_APP_ID`: Feishu application ID +- `FEISHU_APP_SECRET`: Feishu application secret +- `FEISHU_TABLE_APPTOKEN`: Feishu multi-dimensional table AppToken +- `FEISHU_TABLE_ID`: Feishu multi-dimensional table ID + +### Local Development + +1. Create a `.dev.vars` file in the project root directory +2. Copy the above environment variables and fill in their respective values + +### Production Environment + +In the Cloudflare Workers console, configure the same environment variables. Ensure all required environment variables are properly set. + +## Project Structure + +- `src/` - Source code directory +- `wrangler.jsonc` - Cloudflare Workers configuration file +- `worker-configuration.d.ts` - Workers type definition file + +## Tech Stack + +- [Cloudflare Workers](https://workers.cloudflare.com/) +- [TypeScript](https://www.typescriptlang.org/) +- [Vitest](https://vitest.dev/) +- [dayjs](https://day.js.org/) +- [tianji-client-sdk](https://github.com/msgbyte/tianji) diff --git a/apps/daily-ai-trigger/package.json b/apps/daily-ai-trigger/package.json new file mode 100644 index 0000000..a50721e --- /dev/null +++ b/apps/daily-ai-trigger/package.json @@ -0,0 +1,24 @@ +{ + "name": "daily-ai-trigger", + "version": "0.0.0", + "private": true, + "scripts": { + "deploy": "wrangler deploy", + "dev": "wrangler dev", + "dev:scheduled": "wrangler dev --test-scheduled", + "start": "wrangler dev", + "test": "vitest", + "cf-typegen": "wrangler types" + }, + "devDependencies": { + "@cloudflare/vitest-pool-workers": "^0.7.5", + "@cloudflare/workers-types": "^4.20250310.0", + "typescript": "^5.5.2", + "vitest": "~3.0.8", + "wrangler": "^4.20.5" + }, + "dependencies": { + "dayjs": "^1.11.9", + "tianji-client-sdk": "workspace:*" + } +} diff --git a/apps/daily-ai-trigger/src/handler.ts b/apps/daily-ai-trigger/src/handler.ts new file mode 100644 index 0000000..48cac21 --- /dev/null +++ b/apps/daily-ai-trigger/src/handler.ts @@ -0,0 +1,129 @@ +import dayjs from 'dayjs'; +import { openApiClient } from 'tianji-client-sdk'; +import { addFeishuBitableRecord, notifyToFeishu } from './notify/feishu'; + +export async function handleTriggerAITask(env: Env) { + const workspaceId = env.WORKSPACE_ID; + const surveyId = env.SURVEY_ID; + const payloadContentField = env.PAYLOAD_CONTENT_FIELD; + + let suggestionCategory: string[] = []; + if (typeof env.FOCUS_CATEGORY === 'string' && env.FOCUS_CATEGORY.length > 0) { + suggestionCategory = env.FOCUS_CATEGORY.split(','); + } else { + const survey = await openApiClient.SurveyService.surveyGet({ + workspaceId, + surveyId, + }); + suggestionCategory = survey?.recentSuggestionCategory || []; + } + + const startAt = dayjs().subtract(1, 'day').startOf('day').valueOf(); + const endAt = dayjs().endOf('day').valueOf(); + + await Promise.all([ + await openApiClient.AiService.aiClassifySurvey({ + requestBody: { + workspaceId, + surveyId, + startAt, + endAt, + payloadContentField, + suggestionCategory, + languageStrategy: 'user', + runStrategy: 'skipInSuggest', + }, + }), + await openApiClient.AiService.aiTranslateSurvey({ + requestBody: { + workspaceId, + surveyId, + startAt, + endAt, + payloadContentField, + languageStrategy: 'user', + runStrategy: 'skipExist', + }, + }), + ]); + + console.log('Run aiClassifySurvey completed.'); +} + +export async function handleCreateDailyReport(env: Env) { + console.log('handleCreateDailyReport'); + + const workspaceId = env.WORKSPACE_ID; + const surveyId = env.SURVEY_ID; + + const startDate = dayjs().subtract(1, 'day').startOf('day'); + const endDate = dayjs().subtract(1, 'day').endOf('day'); + + const { items } = await openApiClient.SurveyService.surveyResultList({ + workspaceId, + surveyId, + startAt: startDate.valueOf(), + endAt: endDate.valueOf(), + limit: 1000, + }); + + const resultList = items.filter((item) => Boolean(item.aiCategory)); + + const title = `[${startDate.format('YYYY-MM-DD HH:mm:ss')} ~ ${endDate.format('HH:mm:ss')}] Survey Daily Report`; + + let content = ''; + + const categoryCount: { [category: string]: number } = {}; + resultList.forEach((record) => { + const category = record.aiCategory || 'Uncategorized'; + categoryCount[category] = (categoryCount[category] || 0) + 1; + }); + + const sortedCategories = Object.entries(categoryCount).sort((a, b) => b[1] - a[1]); + + content += `All Categories(${resultList.length}):\n`; + sortedCategories.forEach(([category, count]) => { + content += `- ${category}(${count})\n`; + }); + + // 3. Get top 3 categories with the most records + const top3Categories = sortedCategories.slice(0, 3); + + content += `\n\nTop Issues:\n`; + top3Categories.forEach(([category, count]) => { + // Filter all records of the corresponding category + const records = resultList.filter((record) => record.aiCategory === category); + // Sample 3 records (or return all if less than 3) + const sampledRecords = sampleItems(records, 3); + + content += `- ${category} (${count})\n`; + sampledRecords.forEach((record) => { + content += ` - ${record.aiTranslation}\n`; + }); + }); + + await Promise.all([ + notifyToFeishu(env, title, content), + addFeishuBitableRecord( + env, + items.map((item) => ({ + id: item.id, + content: item.aiTranslation ?? '', + category: item.aiCategory ?? '', + createdAt: item.createdAt ?? dayjs(), + })), + ), + ]); +} + +/** + * Randomly sample a specified number of elements from an array + */ +function sampleItems(items: T[], sampleSize: number): T[] { + const copy = [...items]; + for (let i = copy.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [copy[i], copy[j]] = [copy[j], copy[i]]; + } + return copy.slice(0, sampleSize); +} diff --git a/apps/daily-ai-trigger/src/index.ts b/apps/daily-ai-trigger/src/index.ts new file mode 100644 index 0000000..1d7b108 --- /dev/null +++ b/apps/daily-ai-trigger/src/index.ts @@ -0,0 +1,58 @@ +/** + * Welcome to Cloudflare Workers! This is your first worker. + * + * - Run `npm run dev` in your terminal to start a development server + * - Open a browser tab at http://localhost:8787/ to see your worker in action + * - Run `npm run deploy` to publish your worker + * + * Bind resources to your worker in `wrangler.jsonc`. After adding bindings, a type definition for the + * `Env` object can be regenerated with `npm run cf-typegen`. + * + * Learn more at https://developers.cloudflare.com/workers/ + */ + +import { initOpenapiSDK } from 'tianji-client-sdk'; +import { handleCreateDailyReport, handleTriggerAITask } from './handler'; + +export default { + async fetch(request, env, ctx): Promise { + initOpenapiSDK(env.BASE_URL, { + apiKey: env.API_KEY, + header: { + 'accept-language': env.LANGUAGE ?? 'en', + }, + }); + + const url = new URL(request.url); + + if (url.pathname === '/__/handleTriggerAITask') { + await handleTriggerAITask(env); + return new Response('handleTriggerAITask DONE'); + } + + if (url.pathname === '/__/handleCreateDailyReport') { + await handleCreateDailyReport(env); + return new Response('handleCreateDailyReport DONE'); + } + + return new Response('Hello World!'); + }, + async scheduled(controller, env, ctx) { + initOpenapiSDK(env.BASE_URL, { + apiKey: env.API_KEY, + header: { + 'accept-language': env.LANGUAGE ?? 'en', + }, + }); + + switch (controller.cron) { + case '0 1 * * *': + await handleTriggerAITask(env); + break; + case '0 2 * * *': + await handleCreateDailyReport(env); + break; + } + console.log('cron processed'); + }, +} satisfies ExportedHandler; diff --git a/apps/daily-ai-trigger/src/notify/feishu.ts b/apps/daily-ai-trigger/src/notify/feishu.ts new file mode 100644 index 0000000..7c4f239 --- /dev/null +++ b/apps/daily-ai-trigger/src/notify/feishu.ts @@ -0,0 +1,103 @@ +import dayjs from 'dayjs'; + +export async function notifyToFeishu(env: Env, title: string, content: string) { + const feishuWebhook = env.FEISHU_WEBHOOK; + + if (!feishuWebhook) { + return; + } + + await sendRequestToFeishu(feishuWebhook, { + msg_type: 'interactive', + card: { + elements: [ + { + tag: 'markdown', + content, + }, + ], + header: { + title: { + content: title, + tag: 'plain_text', + }, + template: 'yellow', + text_tag_list: [ + { + tag: 'text_tag', + text: { + tag: 'plain_text', + content: 'Tianji Agent', + }, + color: 'neutral', + }, + ], + }, + }, + }); +} + +/** + * Reference: https://open.feishu.cn/document/uAjLw4CM/ukzMukzMukzM/feishu-cards/card-components/content-components/title + */ +async function sendRequestToFeishu(feishuWebhook: string, body: any) { + await fetch(feishuWebhook, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); +} + +/** + * https://open.feishu.cn/document/server-docs/authentication-management/access-token/tenant_access_token_internal + */ +async function getFeishuTenantAccessToken(env: Env) { + const res = await fetch('https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', { + method: 'POST', + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + body: JSON.stringify({ + app_id: env.FEISHU_APP_ID, + app_secret: env.FEISHU_APP_SECRET, + }), + }).then((res) => res.json()); + + if (!res.tenant_access_token) { + throw new Error('Get tenant access token failed'); + } + + const token = res.tenant_access_token; + + return token; +} + +export async function addFeishuBitableRecord(env: Env, records: { id: string; content: string; category: string; createdAt: string }[]) { + if (!env.FEISHU_TABLE_APPTOKEN || !env.FEISHU_TABLE_ID) { + return; + } + + const token = await getFeishuTenantAccessToken(env); + await fetch( + `https://open.feishu.cn/open-apis/bitable/v1/apps/${env.FEISHU_TABLE_APPTOKEN}/tables/${env.FEISHU_TABLE_ID}/records/batch_create`, + { + headers: { + 'Content-Type': 'application/json; charset=utf-8', + Authorization: `Bearer ${token}`, + }, + method: 'POST', + body: JSON.stringify({ + records: records.map((r) => ({ + fields: { + id: r.id, + content: r.content, + category: r.category, + createdAt: dayjs(r.createdAt).valueOf(), + }, + })), + }), + }, + ); +} diff --git a/apps/daily-ai-trigger/test/index.spec.ts b/apps/daily-ai-trigger/test/index.spec.ts new file mode 100644 index 0000000..fbee335 --- /dev/null +++ b/apps/daily-ai-trigger/test/index.spec.ts @@ -0,0 +1,25 @@ +// test/index.spec.ts +import { env, createExecutionContext, waitOnExecutionContext, SELF } from 'cloudflare:test'; +import { describe, it, expect } from 'vitest'; +import worker from '../src/index'; + +// For now, you'll need to do something like this to get a correctly-typed +// `Request` to pass to `worker.fetch()`. +const IncomingRequest = Request; + +describe('Hello World worker', () => { + it('responds with Hello World! (unit style)', async () => { + const request = new IncomingRequest('http://example.com'); + // Create an empty context to pass to `worker.fetch()`. + const ctx = createExecutionContext(); + const response = await worker.fetch(request, env, ctx); + // Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions + await waitOnExecutionContext(ctx); + expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`); + }); + + it('responds with Hello World! (integration style)', async () => { + const response = await SELF.fetch('https://example.com'); + expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`); + }); +}); diff --git a/apps/daily-ai-trigger/test/tsconfig.json b/apps/daily-ai-trigger/test/tsconfig.json new file mode 100644 index 0000000..7fc4362 --- /dev/null +++ b/apps/daily-ai-trigger/test/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["@cloudflare/workers-types/experimental", "@cloudflare/vitest-pool-workers"] + }, + "include": ["./**/*.ts", "../worker-configuration.d.ts"], + "exclude": [] +} diff --git a/apps/daily-ai-trigger/tsconfig.json b/apps/daily-ai-trigger/tsconfig.json new file mode 100644 index 0000000..33bdb79 --- /dev/null +++ b/apps/daily-ai-trigger/tsconfig.json @@ -0,0 +1,46 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es2021", + /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "lib": ["es2021"], + /* Specify what JSX code is generated. */ + "jsx": "react-jsx", + + /* Specify what module code is generated. */ + "module": "es2022", + /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "Bundler", + /* Specify type package names to be included without being referenced in a source file. */ + "types": [ + "@cloudflare/workers-types/2023-07-01" + ], + /* Enable importing .json files */ + "resolveJsonModule": true, + + /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + "allowJs": true, + /* Enable error reporting in type-checked JavaScript files. */ + "checkJs": false, + + /* Disable emitting files from a compilation. */ + "noEmit": true, + + /* Ensure that each file can be safely transpiled without relying on other imports. */ + "isolatedModules": true, + /* Allow 'import x from y' when a module doesn't have a default export. */ + "allowSyntheticDefaultImports": true, + /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true, + + /* Enable all strict type-checking options. */ + "strict": true, + + /* Skip type checking all .d.ts files. */ + "skipLibCheck": true + }, + "exclude": ["test"], + "include": ["worker-configuration.d.ts", "src/**/*.ts"] +} diff --git a/apps/daily-ai-trigger/vitest.config.mts b/apps/daily-ai-trigger/vitest.config.mts new file mode 100644 index 0000000..977f64c --- /dev/null +++ b/apps/daily-ai-trigger/vitest.config.mts @@ -0,0 +1,11 @@ +import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'; + +export default defineWorkersConfig({ + test: { + poolOptions: { + workers: { + wrangler: { configPath: './wrangler.jsonc' }, + }, + }, + }, +}); diff --git a/apps/daily-ai-trigger/worker-configuration.d.ts b/apps/daily-ai-trigger/worker-configuration.d.ts new file mode 100644 index 0000000..f5d3ca9 --- /dev/null +++ b/apps/daily-ai-trigger/worker-configuration.d.ts @@ -0,0 +1,7340 @@ +/* eslint-disable */ +// Generated by Wrangler by running `wrangler types` (hash: d839f4c39d9da38f47cf894323c30dd0) +// Runtime types generated with workerd@1.20250617.0 2025-03-10 +declare namespace Cloudflare { + interface Env { + BASE_URL: string; + API_KEY: string; + WORKSPACE_ID: string; + SURVEY_ID: string; + PAYLOAD_CONTENT_FIELD: string; + LANGUAGE: string; + FOCUS_CATEGORY: string; + FEISHU_WEBHOOK: string; + FEISHU_APP_ID: string; + FEISHU_APP_SECRET: string; + FEISHU_TABLE_APPTOKEN: string; + FEISHU_TABLE_ID: string; + } +} +interface Env extends Cloudflare.Env {} + +// Begin runtime types +/*! ***************************************************************************** +Copyright (c) Cloudflare. All rights reserved. +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ +/* eslint-disable */ +// noinspection JSUnusedGlobalSymbols +declare var onmessage: never; +/** + * An abnormal event (called an exception) which occurs as a result of calling a method or accessing a property of a web API. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException) + */ +declare class DOMException extends Error { + constructor(message?: string, name?: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/message) */ + readonly message: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/name) */ + readonly name: string; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/code) + */ + readonly code: number; + static readonly INDEX_SIZE_ERR: number; + static readonly DOMSTRING_SIZE_ERR: number; + static readonly HIERARCHY_REQUEST_ERR: number; + static readonly WRONG_DOCUMENT_ERR: number; + static readonly INVALID_CHARACTER_ERR: number; + static readonly NO_DATA_ALLOWED_ERR: number; + static readonly NO_MODIFICATION_ALLOWED_ERR: number; + static readonly NOT_FOUND_ERR: number; + static readonly NOT_SUPPORTED_ERR: number; + static readonly INUSE_ATTRIBUTE_ERR: number; + static readonly INVALID_STATE_ERR: number; + static readonly SYNTAX_ERR: number; + static readonly INVALID_MODIFICATION_ERR: number; + static readonly NAMESPACE_ERR: number; + static readonly INVALID_ACCESS_ERR: number; + static readonly VALIDATION_ERR: number; + static readonly TYPE_MISMATCH_ERR: number; + static readonly SECURITY_ERR: number; + static readonly NETWORK_ERR: number; + static readonly ABORT_ERR: number; + static readonly URL_MISMATCH_ERR: number; + static readonly QUOTA_EXCEEDED_ERR: number; + static readonly TIMEOUT_ERR: number; + static readonly INVALID_NODE_TYPE_ERR: number; + static readonly DATA_CLONE_ERR: number; + get stack(): any; + set stack(value: any); +} +type WorkerGlobalScopeEventMap = { + fetch: FetchEvent; + scheduled: ScheduledEvent; + queue: QueueEvent; + unhandledrejection: PromiseRejectionEvent; + rejectionhandled: PromiseRejectionEvent; +}; +declare abstract class WorkerGlobalScope extends EventTarget { + EventTarget: typeof EventTarget; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console) */ +interface Console { + "assert"(condition?: boolean, ...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/clear_static) */ + clear(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/count_static) */ + count(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/countReset_static) */ + countReset(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/debug_static) */ + debug(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dir_static) */ + dir(item?: any, options?: any): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dirxml_static) */ + dirxml(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/error_static) */ + error(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/group_static) */ + group(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupCollapsed_static) */ + groupCollapsed(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupEnd_static) */ + groupEnd(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/info_static) */ + info(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static) */ + log(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/table_static) */ + table(tabularData?: any, properties?: string[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/time_static) */ + time(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timeEnd_static) */ + timeEnd(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timeLog_static) */ + timeLog(label?: string, ...data: any[]): void; + timeStamp(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/trace_static) */ + trace(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/warn_static) */ + warn(...data: any[]): void; +} +declare const console: Console; +type BufferSource = ArrayBufferView | ArrayBuffer; +type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array; +declare namespace WebAssembly { + class CompileError extends Error { + constructor(message?: string); + } + class RuntimeError extends Error { + constructor(message?: string); + } + type ValueType = "anyfunc" | "externref" | "f32" | "f64" | "i32" | "i64" | "v128"; + interface GlobalDescriptor { + value: ValueType; + mutable?: boolean; + } + class Global { + constructor(descriptor: GlobalDescriptor, value?: any); + value: any; + valueOf(): any; + } + type ImportValue = ExportValue | number; + type ModuleImports = Record; + type Imports = Record; + type ExportValue = Function | Global | Memory | Table; + type Exports = Record; + class Instance { + constructor(module: Module, imports?: Imports); + readonly exports: Exports; + } + interface MemoryDescriptor { + initial: number; + maximum?: number; + shared?: boolean; + } + class Memory { + constructor(descriptor: MemoryDescriptor); + readonly buffer: ArrayBuffer; + grow(delta: number): number; + } + type ImportExportKind = "function" | "global" | "memory" | "table"; + interface ModuleExportDescriptor { + kind: ImportExportKind; + name: string; + } + interface ModuleImportDescriptor { + kind: ImportExportKind; + module: string; + name: string; + } + abstract class Module { + static customSections(module: Module, sectionName: string): ArrayBuffer[]; + static exports(module: Module): ModuleExportDescriptor[]; + static imports(module: Module): ModuleImportDescriptor[]; + } + type TableKind = "anyfunc" | "externref"; + interface TableDescriptor { + element: TableKind; + initial: number; + maximum?: number; + } + class Table { + constructor(descriptor: TableDescriptor, value?: any); + readonly length: number; + get(index: number): any; + grow(delta: number, value?: any): number; + set(index: number, value?: any): void; + } + function instantiate(module: Module, imports?: Imports): Promise; + function validate(bytes: BufferSource): boolean; +} +/** + * This ServiceWorker API interface represents the global execution context of a service worker. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope) + */ +interface ServiceWorkerGlobalScope extends WorkerGlobalScope { + DOMException: typeof DOMException; + WorkerGlobalScope: typeof WorkerGlobalScope; + btoa(data: string): string; + atob(data: string): string; + setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; + setTimeout(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; + clearTimeout(timeoutId: number | null): void; + setInterval(callback: (...args: any[]) => void, msDelay?: number): number; + setInterval(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; + clearInterval(timeoutId: number | null): void; + queueMicrotask(task: Function): void; + structuredClone(value: T, options?: StructuredSerializeOptions): T; + reportError(error: any): void; + fetch(input: RequestInfo | URL, init?: RequestInit): Promise; + self: ServiceWorkerGlobalScope; + crypto: Crypto; + caches: CacheStorage; + scheduler: Scheduler; + performance: Performance; + Cloudflare: Cloudflare; + readonly origin: string; + Event: typeof Event; + ExtendableEvent: typeof ExtendableEvent; + CustomEvent: typeof CustomEvent; + PromiseRejectionEvent: typeof PromiseRejectionEvent; + FetchEvent: typeof FetchEvent; + TailEvent: typeof TailEvent; + TraceEvent: typeof TailEvent; + ScheduledEvent: typeof ScheduledEvent; + MessageEvent: typeof MessageEvent; + CloseEvent: typeof CloseEvent; + ReadableStreamDefaultReader: typeof ReadableStreamDefaultReader; + ReadableStreamBYOBReader: typeof ReadableStreamBYOBReader; + ReadableStream: typeof ReadableStream; + WritableStream: typeof WritableStream; + WritableStreamDefaultWriter: typeof WritableStreamDefaultWriter; + TransformStream: typeof TransformStream; + ByteLengthQueuingStrategy: typeof ByteLengthQueuingStrategy; + CountQueuingStrategy: typeof CountQueuingStrategy; + ErrorEvent: typeof ErrorEvent; + EventSource: typeof EventSource; + ReadableStreamBYOBRequest: typeof ReadableStreamBYOBRequest; + ReadableStreamDefaultController: typeof ReadableStreamDefaultController; + ReadableByteStreamController: typeof ReadableByteStreamController; + WritableStreamDefaultController: typeof WritableStreamDefaultController; + TransformStreamDefaultController: typeof TransformStreamDefaultController; + CompressionStream: typeof CompressionStream; + DecompressionStream: typeof DecompressionStream; + TextEncoderStream: typeof TextEncoderStream; + TextDecoderStream: typeof TextDecoderStream; + Headers: typeof Headers; + Body: typeof Body; + Request: typeof Request; + Response: typeof Response; + WebSocket: typeof WebSocket; + WebSocketPair: typeof WebSocketPair; + WebSocketRequestResponsePair: typeof WebSocketRequestResponsePair; + AbortController: typeof AbortController; + AbortSignal: typeof AbortSignal; + TextDecoder: typeof TextDecoder; + TextEncoder: typeof TextEncoder; + navigator: Navigator; + Navigator: typeof Navigator; + URL: typeof URL; + URLSearchParams: typeof URLSearchParams; + URLPattern: typeof URLPattern; + Blob: typeof Blob; + File: typeof File; + FormData: typeof FormData; + Crypto: typeof Crypto; + SubtleCrypto: typeof SubtleCrypto; + CryptoKey: typeof CryptoKey; + CacheStorage: typeof CacheStorage; + Cache: typeof Cache; + FixedLengthStream: typeof FixedLengthStream; + IdentityTransformStream: typeof IdentityTransformStream; + HTMLRewriter: typeof HTMLRewriter; +} +declare function addEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean): void; +declare function removeEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetEventListenerOptions | boolean): void; +/** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent) + */ +declare function dispatchEvent(event: WorkerGlobalScopeEventMap[keyof WorkerGlobalScopeEventMap]): boolean; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/btoa) */ +declare function btoa(data: string): string; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/atob) */ +declare function atob(data: string): string; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setTimeout) */ +declare function setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setTimeout) */ +declare function setTimeout(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/clearTimeout) */ +declare function clearTimeout(timeoutId: number | null): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setInterval) */ +declare function setInterval(callback: (...args: any[]) => void, msDelay?: number): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/setInterval) */ +declare function setInterval(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/clearInterval) */ +declare function clearInterval(timeoutId: number | null): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/queueMicrotask) */ +declare function queueMicrotask(task: Function): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/structuredClone) */ +declare function structuredClone(value: T, options?: StructuredSerializeOptions): T; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/reportError) */ +declare function reportError(error: any): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch) */ +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +declare const self: ServiceWorkerGlobalScope; +/** +* The Web Crypto API provides a set of low-level functions for common cryptographic tasks. +* The Workers runtime implements the full surface of this API, but with some differences in +* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms) +* compared to those implemented in most browsers. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/) +*/ +declare const crypto: Crypto; +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare const caches: CacheStorage; +declare const scheduler: Scheduler; +/** +* The Workers runtime supports a subset of the Performance API, used to measure timing and performance, +* as well as timing of subrequests and other operations. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/) +*/ +declare const performance: Performance; +declare const Cloudflare: Cloudflare; +declare const origin: string; +declare const navigator: Navigator; +interface TestController { +} +interface ExecutionContext { + waitUntil(promise: Promise): void; + passThroughOnException(): void; + props: any; +} +type ExportedHandlerFetchHandler = (request: Request>, env: Env, ctx: ExecutionContext) => Response | Promise; +type ExportedHandlerTailHandler = (events: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTraceHandler = (traces: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTailStreamHandler = (event: TailStream.TailEvent, env: Env, ctx: ExecutionContext) => TailStream.TailEventHandlerType | Promise; +type ExportedHandlerScheduledHandler = (controller: ScheduledController, env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerQueueHandler = (batch: MessageBatch, env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTestHandler = (controller: TestController, env: Env, ctx: ExecutionContext) => void | Promise; +interface ExportedHandler { + fetch?: ExportedHandlerFetchHandler; + tail?: ExportedHandlerTailHandler; + trace?: ExportedHandlerTraceHandler; + tailStream?: ExportedHandlerTailStreamHandler; + scheduled?: ExportedHandlerScheduledHandler; + test?: ExportedHandlerTestHandler; + email?: EmailExportedHandler; + queue?: ExportedHandlerQueueHandler; +} +interface StructuredSerializeOptions { + transfer?: any[]; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent) */ +declare abstract class PromiseRejectionEvent extends Event { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/promise) */ + readonly promise: Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/reason) */ + readonly reason: any; +} +declare abstract class Navigator { + sendBeacon(url: string, body?: (ReadableStream | string | (ArrayBuffer | ArrayBufferView) | Blob | FormData | URLSearchParams | URLSearchParams)): boolean; + readonly userAgent: string; + readonly hardwareConcurrency: number; +} +/** +* The Workers runtime supports a subset of the Performance API, used to measure timing and performance, +* as well as timing of subrequests and other operations. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/) +*/ +interface Performance { + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancetimeorigin) */ + readonly timeOrigin: number; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancenow) */ + now(): number; +} +interface AlarmInvocationInfo { + readonly isRetry: boolean; + readonly retryCount: number; +} +interface Cloudflare { + readonly compatibilityFlags: Record; +} +interface DurableObject { + fetch(request: Request): Response | Promise; + alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise; + webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise; + webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise; + webSocketError?(ws: WebSocket, error: unknown): void | Promise; +} +type DurableObjectStub = Fetcher & { + readonly id: DurableObjectId; + readonly name?: string; +}; +interface DurableObjectId { + toString(): string; + equals(other: DurableObjectId): boolean; + readonly name?: string; +} +interface DurableObjectNamespace { + newUniqueId(options?: DurableObjectNamespaceNewUniqueIdOptions): DurableObjectId; + idFromName(name: string): DurableObjectId; + idFromString(id: string): DurableObjectId; + get(id: DurableObjectId, options?: DurableObjectNamespaceGetDurableObjectOptions): DurableObjectStub; + jurisdiction(jurisdiction: DurableObjectJurisdiction): DurableObjectNamespace; +} +type DurableObjectJurisdiction = "eu" | "fedramp" | "fedramp-high"; +interface DurableObjectNamespaceNewUniqueIdOptions { + jurisdiction?: DurableObjectJurisdiction; +} +type DurableObjectLocationHint = "wnam" | "enam" | "sam" | "weur" | "eeur" | "apac" | "oc" | "afr" | "me"; +interface DurableObjectNamespaceGetDurableObjectOptions { + locationHint?: DurableObjectLocationHint; +} +interface DurableObjectState { + waitUntil(promise: Promise): void; + readonly id: DurableObjectId; + readonly storage: DurableObjectStorage; + container?: Container; + blockConcurrencyWhile(callback: () => Promise): Promise; + acceptWebSocket(ws: WebSocket, tags?: string[]): void; + getWebSockets(tag?: string): WebSocket[]; + setWebSocketAutoResponse(maybeReqResp?: WebSocketRequestResponsePair): void; + getWebSocketAutoResponse(): WebSocketRequestResponsePair | null; + getWebSocketAutoResponseTimestamp(ws: WebSocket): Date | null; + setHibernatableWebSocketEventTimeout(timeoutMs?: number): void; + getHibernatableWebSocketEventTimeout(): number | null; + getTags(ws: WebSocket): string[]; + abort(reason?: string): void; +} +interface DurableObjectTransaction { + get(key: string, options?: DurableObjectGetOptions): Promise; + get(keys: string[], options?: DurableObjectGetOptions): Promise>; + list(options?: DurableObjectListOptions): Promise>; + put(key: string, value: T, options?: DurableObjectPutOptions): Promise; + put(entries: Record, options?: DurableObjectPutOptions): Promise; + delete(key: string, options?: DurableObjectPutOptions): Promise; + delete(keys: string[], options?: DurableObjectPutOptions): Promise; + rollback(): void; + getAlarm(options?: DurableObjectGetAlarmOptions): Promise; + setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise; + deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise; +} +interface DurableObjectStorage { + get(key: string, options?: DurableObjectGetOptions): Promise; + get(keys: string[], options?: DurableObjectGetOptions): Promise>; + list(options?: DurableObjectListOptions): Promise>; + put(key: string, value: T, options?: DurableObjectPutOptions): Promise; + put(entries: Record, options?: DurableObjectPutOptions): Promise; + delete(key: string, options?: DurableObjectPutOptions): Promise; + delete(keys: string[], options?: DurableObjectPutOptions): Promise; + deleteAll(options?: DurableObjectPutOptions): Promise; + transaction(closure: (txn: DurableObjectTransaction) => Promise): Promise; + getAlarm(options?: DurableObjectGetAlarmOptions): Promise; + setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise; + deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise; + sync(): Promise; + sql: SqlStorage; + transactionSync(closure: () => T): T; + getCurrentBookmark(): Promise; + getBookmarkForTime(timestamp: number | Date): Promise; + onNextSessionRestoreBookmark(bookmark: string): Promise; +} +interface DurableObjectListOptions { + start?: string; + startAfter?: string; + end?: string; + prefix?: string; + reverse?: boolean; + limit?: number; + allowConcurrency?: boolean; + noCache?: boolean; +} +interface DurableObjectGetOptions { + allowConcurrency?: boolean; + noCache?: boolean; +} +interface DurableObjectGetAlarmOptions { + allowConcurrency?: boolean; +} +interface DurableObjectPutOptions { + allowConcurrency?: boolean; + allowUnconfirmed?: boolean; + noCache?: boolean; +} +interface DurableObjectSetAlarmOptions { + allowConcurrency?: boolean; + allowUnconfirmed?: boolean; +} +declare class WebSocketRequestResponsePair { + constructor(request: string, response: string); + get request(): string; + get response(): string; +} +interface AnalyticsEngineDataset { + writeDataPoint(event?: AnalyticsEngineDataPoint): void; +} +interface AnalyticsEngineDataPoint { + indexes?: ((ArrayBuffer | string) | null)[]; + doubles?: number[]; + blobs?: ((ArrayBuffer | string) | null)[]; +} +/** + * An event which takes place in the DOM. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event) + */ +declare class Event { + constructor(type: string, init?: EventInit); + /** + * Returns the type of event, e.g. "click", "hashchange", or "submit". + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/type) + */ + get type(): string; + /** + * Returns the event's phase, which is one of NONE, CAPTURING_PHASE, AT_TARGET, and BUBBLING_PHASE. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/eventPhase) + */ + get eventPhase(): number; + /** + * Returns true or false depending on how event was initialized. True if event invokes listeners past a ShadowRoot node that is the root of its target, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composed) + */ + get composed(): boolean; + /** + * Returns true or false depending on how event was initialized. True if event goes through its target's ancestors in reverse tree order, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/bubbles) + */ + get bubbles(): boolean; + /** + * Returns true or false depending on how event was initialized. Its return value does not always carry meaning, but true can indicate that part of the operation during which event was dispatched, can be canceled by invoking the preventDefault() method. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelable) + */ + get cancelable(): boolean; + /** + * Returns true if preventDefault() was invoked successfully to indicate cancelation, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/defaultPrevented) + */ + get defaultPrevented(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/returnValue) + */ + get returnValue(): boolean; + /** + * Returns the object whose event listener's callback is currently being invoked. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/currentTarget) + */ + get currentTarget(): EventTarget | undefined; + /** + * Returns the object to which event is dispatched (its target). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target) + */ + get target(): EventTarget | undefined; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/srcElement) + */ + get srcElement(): EventTarget | undefined; + /** + * Returns the event's timestamp as the number of milliseconds measured relative to the time origin. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/timeStamp) + */ + get timeStamp(): number; + /** + * Returns true if event was dispatched by the user agent, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/isTrusted) + */ + get isTrusted(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble) + */ + get cancelBubble(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble) + */ + set cancelBubble(value: boolean); + /** + * Invoking this method prevents event from reaching any registered event listeners after the current one finishes running and, when dispatched in a tree, also prevents event from reaching any other objects. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopImmediatePropagation) + */ + stopImmediatePropagation(): void; + /** + * If invoked when the cancelable attribute value is true, and while executing a listener for the event with passive set to false, signals to the operation that caused event to be dispatched that it needs to be canceled. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/preventDefault) + */ + preventDefault(): void; + /** + * When dispatched in a tree, invoking this method prevents event from reaching any objects other than the current object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation) + */ + stopPropagation(): void; + /** + * Returns the invocation target objects of event's path (objects on which listeners will be invoked), except for any nodes in shadow trees of which the shadow root's mode is "closed" that are not reachable from event's currentTarget. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composedPath) + */ + composedPath(): EventTarget[]; + static readonly NONE: number; + static readonly CAPTURING_PHASE: number; + static readonly AT_TARGET: number; + static readonly BUBBLING_PHASE: number; +} +interface EventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; +} +type EventListener = (event: EventType) => void; +interface EventListenerObject { + handleEvent(event: EventType): void; +} +type EventListenerOrEventListenerObject = EventListener | EventListenerObject; +/** + * EventTarget is a DOM interface implemented by objects that can receive events and may have listeners for them. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget) + */ +declare class EventTarget = Record> { + constructor(); + /** + * Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. + * + * The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. + * + * When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. + * + * When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. + * + * When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. + * + * If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted. + * + * The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) + */ + addEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean): void; + /** + * Removes the event listener in target's event listener list with the same type, callback, and options. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener) + */ + removeEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetEventListenerOptions | boolean): void; + /** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent) + */ + dispatchEvent(event: EventMap[keyof EventMap]): boolean; +} +interface EventTargetEventListenerOptions { + capture?: boolean; +} +interface EventTargetAddEventListenerOptions { + capture?: boolean; + passive?: boolean; + once?: boolean; + signal?: AbortSignal; +} +interface EventTargetHandlerObject { + handleEvent: (event: Event) => any | undefined; +} +/** + * A controller object that allows you to abort one or more DOM requests as and when desired. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController) + */ +declare class AbortController { + constructor(); + /** + * Returns the AbortSignal object associated with this object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/signal) + */ + get signal(): AbortSignal; + /** + * Invoking this method will set this object's AbortSignal's aborted flag and signal to any observers that the associated activity is to be aborted. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/abort) + */ + abort(reason?: any): void; +} +/** + * A signal object that allows you to communicate with a DOM request (such as a Fetch) and abort it if required via an AbortController object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal) + */ +declare abstract class AbortSignal extends EventTarget { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static) */ + static abort(reason?: any): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static) */ + static timeout(delay: number): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static) */ + static any(signals: AbortSignal[]): AbortSignal; + /** + * Returns true if this AbortSignal's AbortController has signaled to abort, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted) + */ + get aborted(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/reason) */ + get reason(): any; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + get onabort(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + set onabort(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/throwIfAborted) */ + throwIfAborted(): void; +} +interface Scheduler { + wait(delay: number, maybeOptions?: SchedulerWaitOptions): Promise; +} +interface SchedulerWaitOptions { + signal?: AbortSignal; +} +/** + * Extends the lifetime of the install and activate events dispatched on the global scope as part of the service worker lifecycle. This ensures that any functional events (like FetchEvent) are not dispatched until it upgrades database schemas and deletes the outdated cache entries. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent) + */ +declare abstract class ExtendableEvent extends Event { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent/waitUntil) */ + waitUntil(promise: Promise): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent) */ +declare class CustomEvent extends Event { + constructor(type: string, init?: CustomEventCustomEventInit); + /** + * Returns any custom data event was created with. Typically used for synthetic events. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent/detail) + */ + get detail(): T; +} +interface CustomEventCustomEventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; + detail?: any; +} +/** + * A file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob) + */ +declare class Blob { + constructor(type?: ((ArrayBuffer | ArrayBufferView) | string | Blob)[], options?: BlobOptions); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */ + get size(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */ + get type(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */ + slice(start?: number, end?: number, type?: string): Blob; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/arrayBuffer) */ + arrayBuffer(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/bytes) */ + bytes(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */ + text(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/stream) */ + stream(): ReadableStream; +} +interface BlobOptions { + type?: string; +} +/** + * Provides information about files and allows JavaScript in a web page to access their content. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/File) + */ +declare class File extends Blob { + constructor(bits: ((ArrayBuffer | ArrayBufferView) | string | Blob)[] | undefined, name: string, options?: FileOptions); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */ + get name(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */ + get lastModified(): number; +} +interface FileOptions { + type?: string; + lastModified?: number; +} +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare abstract class CacheStorage { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CacheStorage/open) */ + open(cacheName: string): Promise; + readonly default: Cache; +} +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare abstract class Cache { + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#delete) */ + delete(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#match) */ + match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#put) */ + put(request: RequestInfo | URL, response: Response): Promise; +} +interface CacheQueryOptions { + ignoreMethod?: boolean; +} +/** +* The Web Crypto API provides a set of low-level functions for common cryptographic tasks. +* The Workers runtime implements the full surface of this API, but with some differences in +* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms) +* compared to those implemented in most browsers. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/) +*/ +declare abstract class Crypto { + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/subtle) + */ + get subtle(): SubtleCrypto; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues) */ + getRandomValues(buffer: T): T; + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID) + */ + randomUUID(): string; + DigestStream: typeof DigestStream; +} +/** + * This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via Window.crypto). + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto) + */ +declare abstract class SubtleCrypto { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/encrypt) */ + encrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, plainText: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/decrypt) */ + decrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, cipherText: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/sign) */ + sign(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/verify) */ + verify(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, signature: ArrayBuffer | ArrayBufferView, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/digest) */ + digest(algorithm: string | SubtleCryptoHashAlgorithm, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey) */ + generateKey(algorithm: string | SubtleCryptoGenerateKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveKey) */ + deriveKey(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, derivedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveBits) */ + deriveBits(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, length?: number | null): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/importKey) */ + importKey(format: string, keyData: (ArrayBuffer | ArrayBufferView) | JsonWebKey, algorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/exportKey) */ + exportKey(format: string, key: CryptoKey): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/wrapKey) */ + wrapKey(format: string, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: string | SubtleCryptoEncryptAlgorithm): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/unwrapKey) */ + unwrapKey(format: string, wrappedKey: ArrayBuffer | ArrayBufferView, unwrappingKey: CryptoKey, unwrapAlgorithm: string | SubtleCryptoEncryptAlgorithm, unwrappedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + timingSafeEqual(a: ArrayBuffer | ArrayBufferView, b: ArrayBuffer | ArrayBufferView): boolean; +} +/** + * The CryptoKey dictionary of the Web Crypto API represents a cryptographic key. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey) + */ +declare abstract class CryptoKey { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/type) */ + readonly type: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/extractable) */ + readonly extractable: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/algorithm) */ + readonly algorithm: CryptoKeyKeyAlgorithm | CryptoKeyAesKeyAlgorithm | CryptoKeyHmacKeyAlgorithm | CryptoKeyRsaKeyAlgorithm | CryptoKeyEllipticKeyAlgorithm | CryptoKeyArbitraryKeyAlgorithm; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/usages) */ + readonly usages: string[]; +} +interface CryptoKeyPair { + publicKey: CryptoKey; + privateKey: CryptoKey; +} +interface JsonWebKey { + kty: string; + use?: string; + key_ops?: string[]; + alg?: string; + ext?: boolean; + crv?: string; + x?: string; + y?: string; + d?: string; + n?: string; + e?: string; + p?: string; + q?: string; + dp?: string; + dq?: string; + qi?: string; + oth?: RsaOtherPrimesInfo[]; + k?: string; +} +interface RsaOtherPrimesInfo { + r?: string; + d?: string; + t?: string; +} +interface SubtleCryptoDeriveKeyAlgorithm { + name: string; + salt?: (ArrayBuffer | ArrayBufferView); + iterations?: number; + hash?: (string | SubtleCryptoHashAlgorithm); + $public?: CryptoKey; + info?: (ArrayBuffer | ArrayBufferView); +} +interface SubtleCryptoEncryptAlgorithm { + name: string; + iv?: (ArrayBuffer | ArrayBufferView); + additionalData?: (ArrayBuffer | ArrayBufferView); + tagLength?: number; + counter?: (ArrayBuffer | ArrayBufferView); + length?: number; + label?: (ArrayBuffer | ArrayBufferView); +} +interface SubtleCryptoGenerateKeyAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + modulusLength?: number; + publicExponent?: (ArrayBuffer | ArrayBufferView); + length?: number; + namedCurve?: string; +} +interface SubtleCryptoHashAlgorithm { + name: string; +} +interface SubtleCryptoImportKeyAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + length?: number; + namedCurve?: string; + compressed?: boolean; +} +interface SubtleCryptoSignAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + dataLength?: number; + saltLength?: number; +} +interface CryptoKeyKeyAlgorithm { + name: string; +} +interface CryptoKeyAesKeyAlgorithm { + name: string; + length: number; +} +interface CryptoKeyHmacKeyAlgorithm { + name: string; + hash: CryptoKeyKeyAlgorithm; + length: number; +} +interface CryptoKeyRsaKeyAlgorithm { + name: string; + modulusLength: number; + publicExponent: ArrayBuffer | ArrayBufferView; + hash?: CryptoKeyKeyAlgorithm; +} +interface CryptoKeyEllipticKeyAlgorithm { + name: string; + namedCurve: string; +} +interface CryptoKeyArbitraryKeyAlgorithm { + name: string; + hash?: CryptoKeyKeyAlgorithm; + namedCurve?: string; + length?: number; +} +declare class DigestStream extends WritableStream { + constructor(algorithm: string | SubtleCryptoHashAlgorithm); + readonly digest: Promise; + get bytesWritten(): number | bigint; +} +/** + * A decoder for a specific method, that is a specific character encoding, like utf-8, iso-8859-2, koi8, cp1261, gbk, etc. A decoder takes a stream of bytes as input and emits a stream of code points. For a more scalable, non-native library, see StringView – a C-like representation of strings based on typed arrays. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder) + */ +declare class TextDecoder { + constructor(label?: string, options?: TextDecoderConstructorOptions); + /** + * Returns the result of running encoding's decoder. The method can be invoked zero or more times with options's stream set to true, and then once without options's stream (or set to false), to process a fragmented input. If the invocation without options's stream (or set to false) has no input, it's clearest to omit both arguments. + * + * ``` + * var string = "", decoder = new TextDecoder(encoding), buffer; + * while(buffer = next_chunk()) { + * string += decoder.decode(buffer, {stream:true}); + * } + * string += decoder.decode(); // end-of-queue + * ``` + * + * If the error mode is "fatal" and encoding's decoder returns error, throws a TypeError. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder/decode) + */ + decode(input?: (ArrayBuffer | ArrayBufferView), options?: TextDecoderDecodeOptions): string; + get encoding(): string; + get fatal(): boolean; + get ignoreBOM(): boolean; +} +/** + * TextEncoder takes a stream of code points as input and emits a stream of bytes. For a more scalable, non-native library, see StringView – a C-like representation of strings based on typed arrays. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder) + */ +declare class TextEncoder { + constructor(); + /** + * Returns the result of running UTF-8's encoder. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encode) + */ + encode(input?: string): Uint8Array; + /** + * Runs the UTF-8 encoder on source, stores the result of that operation into destination, and returns the progress made as an object wherein read is the number of converted code units of source and written is the number of bytes modified in destination. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encodeInto) + */ + encodeInto(input: string, buffer: ArrayBuffer | ArrayBufferView): TextEncoderEncodeIntoResult; + get encoding(): string; +} +interface TextDecoderConstructorOptions { + fatal: boolean; + ignoreBOM: boolean; +} +interface TextDecoderDecodeOptions { + stream: boolean; +} +interface TextEncoderEncodeIntoResult { + read: number; + written: number; +} +/** + * Events providing information related to errors in scripts or in files. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent) + */ +declare class ErrorEvent extends Event { + constructor(type: string, init?: ErrorEventErrorEventInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent/filename) */ + get filename(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent/message) */ + get message(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent/lineno) */ + get lineno(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent/colno) */ + get colno(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent/error) */ + get error(): any; +} +interface ErrorEventErrorEventInit { + message?: string; + filename?: string; + lineno?: number; + colno?: number; + error?: any; +} +/** + * Provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data". + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData) + */ +declare class FormData { + constructor(); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */ + append(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */ + append(name: string, value: Blob, filename?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/delete) */ + delete(name: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/get) */ + get(name: string): (File | string) | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/getAll) */ + getAll(name: string): (File | string)[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/has) */ + has(name: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set) */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set) */ + set(name: string, value: Blob, filename?: string): void; + /* Returns an array of key, value pairs for every entry in the list. */ + entries(): IterableIterator<[ + key: string, + value: File | string + ]>; + /* Returns a list of keys in the list. */ + keys(): IterableIterator; + /* Returns a list of values in the list. */ + values(): IterableIterator<(File | string)>; + forEach(callback: (this: This, value: File | string, key: string, parent: FormData) => void, thisArg?: This): void; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: File | string + ]>; +} +interface ContentOptions { + html?: boolean; +} +declare class HTMLRewriter { + constructor(); + on(selector: string, handlers: HTMLRewriterElementContentHandlers): HTMLRewriter; + onDocument(handlers: HTMLRewriterDocumentContentHandlers): HTMLRewriter; + transform(response: Response): Response; +} +interface HTMLRewriterElementContentHandlers { + element?(element: Element): void | Promise; + comments?(comment: Comment): void | Promise; + text?(element: Text): void | Promise; +} +interface HTMLRewriterDocumentContentHandlers { + doctype?(doctype: Doctype): void | Promise; + comments?(comment: Comment): void | Promise; + text?(text: Text): void | Promise; + end?(end: DocumentEnd): void | Promise; +} +interface Doctype { + readonly name: string | null; + readonly publicId: string | null; + readonly systemId: string | null; +} +interface Element { + tagName: string; + readonly attributes: IterableIterator; + readonly removed: boolean; + readonly namespaceURI: string; + getAttribute(name: string): string | null; + hasAttribute(name: string): boolean; + setAttribute(name: string, value: string): Element; + removeAttribute(name: string): Element; + before(content: string | ReadableStream | Response, options?: ContentOptions): Element; + after(content: string | ReadableStream | Response, options?: ContentOptions): Element; + prepend(content: string | ReadableStream | Response, options?: ContentOptions): Element; + append(content: string | ReadableStream | Response, options?: ContentOptions): Element; + replace(content: string | ReadableStream | Response, options?: ContentOptions): Element; + remove(): Element; + removeAndKeepContent(): Element; + setInnerContent(content: string | ReadableStream | Response, options?: ContentOptions): Element; + onEndTag(handler: (tag: EndTag) => void | Promise): void; +} +interface EndTag { + name: string; + before(content: string | ReadableStream | Response, options?: ContentOptions): EndTag; + after(content: string | ReadableStream | Response, options?: ContentOptions): EndTag; + remove(): EndTag; +} +interface Comment { + text: string; + readonly removed: boolean; + before(content: string, options?: ContentOptions): Comment; + after(content: string, options?: ContentOptions): Comment; + replace(content: string, options?: ContentOptions): Comment; + remove(): Comment; +} +interface Text { + readonly text: string; + readonly lastInTextNode: boolean; + readonly removed: boolean; + before(content: string | ReadableStream | Response, options?: ContentOptions): Text; + after(content: string | ReadableStream | Response, options?: ContentOptions): Text; + replace(content: string | ReadableStream | Response, options?: ContentOptions): Text; + remove(): Text; +} +interface DocumentEnd { + append(content: string, options?: ContentOptions): DocumentEnd; +} +/** + * This is the event type for fetch events dispatched on the service worker global scope. It contains information about the fetch, including the request and how the receiver will treat the response. It provides the event.respondWith() method, which allows us to provide a response to this fetch. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent) + */ +declare abstract class FetchEvent extends ExtendableEvent { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/request) */ + readonly request: Request; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/respondWith) */ + respondWith(promise: Response | Promise): void; + passThroughOnException(): void; +} +type HeadersInit = Headers | Iterable> | Record; +/** + * This Fetch API interface allows you to perform various actions on HTTP request and response headers. These actions include retrieving, setting, adding to, and removing. A Headers object has an associated header list, which is initially empty and consists of zero or more name and value pairs.  You can add to this using methods like append() (see Examples.) In all methods of this interface, header names are matched by case-insensitive byte sequence. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers) + */ +declare class Headers { + constructor(init?: HeadersInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/get) */ + get(name: string): string | null; + getAll(name: string): string[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/getSetCookie) */ + getSetCookie(): string[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/has) */ + has(name: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/set) */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/append) */ + append(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/delete) */ + delete(name: string): void; + forEach(callback: (this: This, value: string, key: string, parent: Headers) => void, thisArg?: This): void; + /* Returns an iterator allowing to go through all key/value pairs contained in this object. */ + entries(): IterableIterator<[ + key: string, + value: string + ]>; + /* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object. */ + keys(): IterableIterator; + /* Returns an iterator allowing to go through all values of the key/value pairs contained in this object. */ + values(): IterableIterator; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: string + ]>; +} +type BodyInit = ReadableStream | string | ArrayBuffer | ArrayBufferView | Blob | URLSearchParams | FormData; +declare abstract class Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) */ + get body(): ReadableStream | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */ + get bodyUsed(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */ + arrayBuffer(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bytes) */ + bytes(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/text) */ + text(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */ + json(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/formData) */ + formData(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ + blob(): Promise; +} +/** + * This Fetch API interface represents the response to a request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response) + */ +declare var Response: { + prototype: Response; + new (body?: BodyInit | null, init?: ResponseInit): Response; + error(): Response; + redirect(url: string, status?: number): Response; + json(any: any, maybeInit?: (ResponseInit | Response)): Response; +}; +/** + * This Fetch API interface represents the response to a request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response) + */ +interface Response extends Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/clone) */ + clone(): Response; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/status) */ + status: number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/statusText) */ + statusText: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/headers) */ + headers: Headers; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/ok) */ + ok: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/redirected) */ + redirected: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/url) */ + url: string; + webSocket: WebSocket | null; + cf: any | undefined; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/type) */ + type: "default" | "error"; +} +interface ResponseInit { + status?: number; + statusText?: string; + headers?: HeadersInit; + cf?: any; + webSocket?: (WebSocket | null); + encodeBody?: "automatic" | "manual"; +} +type RequestInfo> = Request | string; +/** + * This Fetch API interface represents a resource request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request) + */ +declare var Request: { + prototype: Request; + new >(input: RequestInfo | URL, init?: RequestInit): Request; +}; +/** + * This Fetch API interface represents a resource request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request) + */ +interface Request> extends Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/clone) */ + clone(): Request; + /** + * Returns request's HTTP method, which is "GET" by default. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/method) + */ + method: string; + /** + * Returns the URL of request as a string. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/url) + */ + url: string; + /** + * Returns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the "Host" header. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/headers) + */ + headers: Headers; + /** + * Returns the redirect mode associated with request, which is a string indicating how redirects for the request will be handled during fetching. A request will follow redirects by default. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/redirect) + */ + redirect: string; + fetcher: Fetcher | null; + /** + * Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/signal) + */ + signal: AbortSignal; + cf: Cf | undefined; + /** + * Returns request's subresource integrity metadata, which is a cryptographic hash of the resource being fetched. Its value consists of multiple hashes separated by whitespace. [SRI] + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/integrity) + */ + integrity: string; + /** + * Returns a boolean indicating whether or not request can outlive the global in which it was created. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/keepalive) + */ + keepalive: boolean; + /** + * Returns the cache mode associated with request, which is a string indicating how the request will interact with the browser's cache when fetching. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/cache) + */ + cache?: "no-store"; +} +interface RequestInit { + /* A string to set request's method. */ + method?: string; + /* A Headers object, an object literal, or an array of two-item arrays to set request's headers. */ + headers?: HeadersInit; + /* A BodyInit object or null to set request's body. */ + body?: BodyInit | null; + /* A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */ + redirect?: string; + fetcher?: (Fetcher | null); + cf?: Cf; + /* A string indicating how the request will interact with the browser's cache to set request's cache. */ + cache?: "no-store"; + /* A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */ + integrity?: string; + /* An AbortSignal to set request's signal. */ + signal?: (AbortSignal | null); + encodeResponseBody?: "automatic" | "manual"; +} +type Service = Fetcher; +type Fetcher = (T extends Rpc.EntrypointBranded ? Rpc.Provider : unknown) & { + fetch(input: RequestInfo | URL, init?: RequestInit): Promise; + connect(address: SocketAddress | string, options?: SocketOptions): Socket; +}; +interface KVNamespaceListKey { + name: Key; + expiration?: number; + metadata?: Metadata; +} +type KVNamespaceListResult = { + list_complete: false; + keys: KVNamespaceListKey[]; + cursor: string; + cacheStatus: string | null; +} | { + list_complete: true; + keys: KVNamespaceListKey[]; + cacheStatus: string | null; +}; +interface KVNamespace { + get(key: Key, options?: Partial>): Promise; + get(key: Key, type: "text"): Promise; + get(key: Key, type: "json"): Promise; + get(key: Key, type: "arrayBuffer"): Promise; + get(key: Key, type: "stream"): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"text">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"json">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"arrayBuffer">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"stream">): Promise; + get(key: Array, type: "text"): Promise>; + get(key: Array, type: "json"): Promise>; + get(key: Array, options?: Partial>): Promise>; + get(key: Array, options?: KVNamespaceGetOptions<"text">): Promise>; + get(key: Array, options?: KVNamespaceGetOptions<"json">): Promise>; + list(options?: KVNamespaceListOptions): Promise>; + put(key: Key, value: string | ArrayBuffer | ArrayBufferView | ReadableStream, options?: KVNamespacePutOptions): Promise; + getWithMetadata(key: Key, options?: Partial>): Promise>; + getWithMetadata(key: Key, type: "text"): Promise>; + getWithMetadata(key: Key, type: "json"): Promise>; + getWithMetadata(key: Key, type: "arrayBuffer"): Promise>; + getWithMetadata(key: Key, type: "stream"): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"text">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"json">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"arrayBuffer">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"stream">): Promise>; + getWithMetadata(key: Array, type: "text"): Promise>>; + getWithMetadata(key: Array, type: "json"): Promise>>; + getWithMetadata(key: Array, options?: Partial>): Promise>>; + getWithMetadata(key: Array, options?: KVNamespaceGetOptions<"text">): Promise>>; + getWithMetadata(key: Array, options?: KVNamespaceGetOptions<"json">): Promise>>; + delete(key: Key): Promise; +} +interface KVNamespaceListOptions { + limit?: number; + prefix?: (string | null); + cursor?: (string | null); +} +interface KVNamespaceGetOptions { + type: Type; + cacheTtl?: number; +} +interface KVNamespacePutOptions { + expiration?: number; + expirationTtl?: number; + metadata?: (any | null); +} +interface KVNamespaceGetWithMetadataResult { + value: Value | null; + metadata: Metadata | null; + cacheStatus: string | null; +} +type QueueContentType = "text" | "bytes" | "json" | "v8"; +interface Queue { + send(message: Body, options?: QueueSendOptions): Promise; + sendBatch(messages: Iterable>, options?: QueueSendBatchOptions): Promise; +} +interface QueueSendOptions { + contentType?: QueueContentType; + delaySeconds?: number; +} +interface QueueSendBatchOptions { + delaySeconds?: number; +} +interface MessageSendRequest { + body: Body; + contentType?: QueueContentType; + delaySeconds?: number; +} +interface QueueRetryOptions { + delaySeconds?: number; +} +interface Message { + readonly id: string; + readonly timestamp: Date; + readonly body: Body; + readonly attempts: number; + retry(options?: QueueRetryOptions): void; + ack(): void; +} +interface QueueEvent extends ExtendableEvent { + readonly messages: readonly Message[]; + readonly queue: string; + retryAll(options?: QueueRetryOptions): void; + ackAll(): void; +} +interface MessageBatch { + readonly messages: readonly Message[]; + readonly queue: string; + retryAll(options?: QueueRetryOptions): void; + ackAll(): void; +} +interface R2Error extends Error { + readonly name: string; + readonly code: number; + readonly message: string; + readonly action: string; + readonly stack: any; +} +interface R2ListOptions { + limit?: number; + prefix?: string; + cursor?: string; + delimiter?: string; + startAfter?: string; + include?: ("httpMetadata" | "customMetadata")[]; +} +declare abstract class R2Bucket { + head(key: string): Promise; + get(key: string, options: R2GetOptions & { + onlyIf: R2Conditional | Headers; + }): Promise; + get(key: string, options?: R2GetOptions): Promise; + put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions & { + onlyIf: R2Conditional | Headers; + }): Promise; + put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions): Promise; + createMultipartUpload(key: string, options?: R2MultipartOptions): Promise; + resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload; + delete(keys: string | string[]): Promise; + list(options?: R2ListOptions): Promise; +} +interface R2MultipartUpload { + readonly key: string; + readonly uploadId: string; + uploadPart(partNumber: number, value: ReadableStream | (ArrayBuffer | ArrayBufferView) | string | Blob, options?: R2UploadPartOptions): Promise; + abort(): Promise; + complete(uploadedParts: R2UploadedPart[]): Promise; +} +interface R2UploadedPart { + partNumber: number; + etag: string; +} +declare abstract class R2Object { + readonly key: string; + readonly version: string; + readonly size: number; + readonly etag: string; + readonly httpEtag: string; + readonly checksums: R2Checksums; + readonly uploaded: Date; + readonly httpMetadata?: R2HTTPMetadata; + readonly customMetadata?: Record; + readonly range?: R2Range; + readonly storageClass: string; + readonly ssecKeyMd5?: string; + writeHttpMetadata(headers: Headers): void; +} +interface R2ObjectBody extends R2Object { + get body(): ReadableStream; + get bodyUsed(): boolean; + arrayBuffer(): Promise; + bytes(): Promise; + text(): Promise; + json(): Promise; + blob(): Promise; +} +type R2Range = { + offset: number; + length?: number; +} | { + offset?: number; + length: number; +} | { + suffix: number; +}; +interface R2Conditional { + etagMatches?: string; + etagDoesNotMatch?: string; + uploadedBefore?: Date; + uploadedAfter?: Date; + secondsGranularity?: boolean; +} +interface R2GetOptions { + onlyIf?: (R2Conditional | Headers); + range?: (R2Range | Headers); + ssecKey?: (ArrayBuffer | string); +} +interface R2PutOptions { + onlyIf?: (R2Conditional | Headers); + httpMetadata?: (R2HTTPMetadata | Headers); + customMetadata?: Record; + md5?: ((ArrayBuffer | ArrayBufferView) | string); + sha1?: ((ArrayBuffer | ArrayBufferView) | string); + sha256?: ((ArrayBuffer | ArrayBufferView) | string); + sha384?: ((ArrayBuffer | ArrayBufferView) | string); + sha512?: ((ArrayBuffer | ArrayBufferView) | string); + storageClass?: string; + ssecKey?: (ArrayBuffer | string); +} +interface R2MultipartOptions { + httpMetadata?: (R2HTTPMetadata | Headers); + customMetadata?: Record; + storageClass?: string; + ssecKey?: (ArrayBuffer | string); +} +interface R2Checksums { + readonly md5?: ArrayBuffer; + readonly sha1?: ArrayBuffer; + readonly sha256?: ArrayBuffer; + readonly sha384?: ArrayBuffer; + readonly sha512?: ArrayBuffer; + toJSON(): R2StringChecksums; +} +interface R2StringChecksums { + md5?: string; + sha1?: string; + sha256?: string; + sha384?: string; + sha512?: string; +} +interface R2HTTPMetadata { + contentType?: string; + contentLanguage?: string; + contentDisposition?: string; + contentEncoding?: string; + cacheControl?: string; + cacheExpiry?: Date; +} +type R2Objects = { + objects: R2Object[]; + delimitedPrefixes: string[]; +} & ({ + truncated: true; + cursor: string; +} | { + truncated: false; +}); +interface R2UploadPartOptions { + ssecKey?: (ArrayBuffer | string); +} +declare abstract class ScheduledEvent extends ExtendableEvent { + readonly scheduledTime: number; + readonly cron: string; + noRetry(): void; +} +interface ScheduledController { + readonly scheduledTime: number; + readonly cron: string; + noRetry(): void; +} +interface QueuingStrategy { + highWaterMark?: (number | bigint); + size?: (chunk: T) => number | bigint; +} +interface UnderlyingSink { + type?: string; + start?: (controller: WritableStreamDefaultController) => void | Promise; + write?: (chunk: W, controller: WritableStreamDefaultController) => void | Promise; + abort?: (reason: any) => void | Promise; + close?: () => void | Promise; +} +interface UnderlyingByteSource { + type: "bytes"; + autoAllocateChunkSize?: number; + start?: (controller: ReadableByteStreamController) => void | Promise; + pull?: (controller: ReadableByteStreamController) => void | Promise; + cancel?: (reason: any) => void | Promise; +} +interface UnderlyingSource { + type?: "" | undefined; + start?: (controller: ReadableStreamDefaultController) => void | Promise; + pull?: (controller: ReadableStreamDefaultController) => void | Promise; + cancel?: (reason: any) => void | Promise; + expectedLength?: (number | bigint); +} +interface Transformer { + readableType?: string; + writableType?: string; + start?: (controller: TransformStreamDefaultController) => void | Promise; + transform?: (chunk: I, controller: TransformStreamDefaultController) => void | Promise; + flush?: (controller: TransformStreamDefaultController) => void | Promise; + cancel?: (reason: any) => void | Promise; + expectedLength?: number; +} +interface StreamPipeOptions { + /** + * Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + * + * Errors and closures of the source and destination streams propagate as follows: + * + * An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination. + * + * An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source. + * + * When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error. + * + * If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source. + * + * The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set. + */ + preventClose?: boolean; + preventAbort?: boolean; + preventCancel?: boolean; + signal?: AbortSignal; +} +type ReadableStreamReadResult = { + done: false; + value: R; +} | { + done: true; + value?: undefined; +}; +/** + * This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream) + */ +interface ReadableStream { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/locked) */ + get locked(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/cancel) */ + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader) */ + getReader(): ReadableStreamDefaultReader; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader) */ + getReader(options: ReadableStreamGetReaderOptions): ReadableStreamBYOBReader; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeThrough) */ + pipeThrough(transform: ReadableWritablePair, options?: StreamPipeOptions): ReadableStream; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeTo) */ + pipeTo(destination: WritableStream, options?: StreamPipeOptions): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/tee) */ + tee(): [ + ReadableStream, + ReadableStream + ]; + values(options?: ReadableStreamValuesOptions): AsyncIterableIterator; + [Symbol.asyncIterator](options?: ReadableStreamValuesOptions): AsyncIterableIterator; +} +/** + * This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream) + */ +declare const ReadableStream: { + prototype: ReadableStream; + new (underlyingSource: UnderlyingByteSource, strategy?: QueuingStrategy): ReadableStream; + new (underlyingSource?: UnderlyingSource, strategy?: QueuingStrategy): ReadableStream; +}; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader) */ +declare class ReadableStreamDefaultReader { + constructor(stream: ReadableStream); + get closed(): Promise; + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/read) */ + read(): Promise>; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/releaseLock) */ + releaseLock(): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader) */ +declare class ReadableStreamBYOBReader { + constructor(stream: ReadableStream); + get closed(): Promise; + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/read) */ + read(view: T): Promise>; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/releaseLock) */ + releaseLock(): void; + readAtLeast(minElements: number, view: T): Promise>; +} +interface ReadableStreamBYOBReaderReadableStreamBYOBReaderReadOptions { + min?: number; +} +interface ReadableStreamGetReaderOptions { + /** + * Creates a ReadableStreamBYOBReader and locks the stream to the new reader. + * + * This call behaves the same way as the no-argument variant, except that it only works on readable byte streams, i.e. streams which were constructed specifically with the ability to handle "bring your own buffer" reading. The returned BYOB reader provides the ability to directly read individual chunks from the stream via its read() method, into developer-supplied buffers, allowing more precise control over allocation. + */ + mode: "byob"; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest) */ +declare abstract class ReadableStreamBYOBRequest { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/view) */ + get view(): Uint8Array | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respond) */ + respond(bytesWritten: number): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respondWithNewView) */ + respondWithNewView(view: ArrayBuffer | ArrayBufferView): void; + get atLeast(): number | null; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController) */ +declare abstract class ReadableStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/close) */ + close(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/enqueue) */ + enqueue(chunk?: R): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/error) */ + error(reason: any): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController) */ +declare abstract class ReadableByteStreamController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/byobRequest) */ + get byobRequest(): ReadableStreamBYOBRequest | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/close) */ + close(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/enqueue) */ + enqueue(chunk: ArrayBuffer | ArrayBufferView): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/error) */ + error(reason: any): void; +} +/** + * This Streams API interface represents a controller allowing control of a WritableStream's state. When constructing a WritableStream, the underlying sink is given a corresponding WritableStreamDefaultController instance to manipulate. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController) + */ +declare abstract class WritableStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/signal) */ + get signal(): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/error) */ + error(reason?: any): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController) */ +declare abstract class TransformStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/enqueue) */ + enqueue(chunk?: O): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/error) */ + error(reason: any): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/terminate) */ + terminate(): void; +} +interface ReadableWritablePair { + /** + * Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + */ + writable: WritableStream; + readable: ReadableStream; +} +/** + * This Streams API interface provides a standard abstraction for writing streaming data to a destination, known as a sink. This object comes with built-in backpressure and queuing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream) + */ +declare class WritableStream { + constructor(underlyingSink?: UnderlyingSink, queuingStrategy?: QueuingStrategy); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/locked) */ + get locked(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/abort) */ + abort(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/close) */ + close(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/getWriter) */ + getWriter(): WritableStreamDefaultWriter; +} +/** + * This Streams API interface is the object returned by WritableStream.getWriter() and once created locks the < writer to the WritableStream ensuring that no other streams can write to the underlying sink. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter) + */ +declare class WritableStreamDefaultWriter { + constructor(stream: WritableStream); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/closed) */ + get closed(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/ready) */ + get ready(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/abort) */ + abort(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/close) */ + close(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/write) */ + write(chunk?: W): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/releaseLock) */ + releaseLock(): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream) */ +declare class TransformStream { + constructor(transformer?: Transformer, writableStrategy?: QueuingStrategy, readableStrategy?: QueuingStrategy); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/readable) */ + get readable(): ReadableStream; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/writable) */ + get writable(): WritableStream; +} +declare class FixedLengthStream extends IdentityTransformStream { + constructor(expectedLength: number | bigint, queuingStrategy?: IdentityTransformStreamQueuingStrategy); +} +declare class IdentityTransformStream extends TransformStream { + constructor(queuingStrategy?: IdentityTransformStreamQueuingStrategy); +} +interface IdentityTransformStreamQueuingStrategy { + highWaterMark?: (number | bigint); +} +interface ReadableStreamValuesOptions { + preventCancel?: boolean; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CompressionStream) */ +declare class CompressionStream extends TransformStream { + constructor(format: "gzip" | "deflate" | "deflate-raw"); +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DecompressionStream) */ +declare class DecompressionStream extends TransformStream { + constructor(format: "gzip" | "deflate" | "deflate-raw"); +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoderStream) */ +declare class TextEncoderStream extends TransformStream { + constructor(); + get encoding(): string; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoderStream) */ +declare class TextDecoderStream extends TransformStream { + constructor(label?: string, options?: TextDecoderStreamTextDecoderStreamInit); + get encoding(): string; + get fatal(): boolean; + get ignoreBOM(): boolean; +} +interface TextDecoderStreamTextDecoderStreamInit { + fatal?: boolean; + ignoreBOM?: boolean; +} +/** + * This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy) + */ +declare class ByteLengthQueuingStrategy implements QueuingStrategy { + constructor(init: QueuingStrategyInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/highWaterMark) */ + get highWaterMark(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/size) */ + get size(): (chunk?: any) => number; +} +/** + * This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy) + */ +declare class CountQueuingStrategy implements QueuingStrategy { + constructor(init: QueuingStrategyInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/highWaterMark) */ + get highWaterMark(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/size) */ + get size(): (chunk?: any) => number; +} +interface QueuingStrategyInit { + /** + * Creates a new ByteLengthQueuingStrategy with the provided high water mark. + * + * Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw. + */ + highWaterMark: number; +} +interface ScriptVersion { + id?: string; + tag?: string; + message?: string; +} +declare abstract class TailEvent extends ExtendableEvent { + readonly events: TraceItem[]; + readonly traces: TraceItem[]; +} +interface TraceItem { + readonly event: (TraceItemFetchEventInfo | TraceItemJsRpcEventInfo | TraceItemScheduledEventInfo | TraceItemAlarmEventInfo | TraceItemQueueEventInfo | TraceItemEmailEventInfo | TraceItemTailEventInfo | TraceItemCustomEventInfo | TraceItemHibernatableWebSocketEventInfo) | null; + readonly eventTimestamp: number | null; + readonly logs: TraceLog[]; + readonly exceptions: TraceException[]; + readonly diagnosticsChannelEvents: TraceDiagnosticChannelEvent[]; + readonly scriptName: string | null; + readonly entrypoint?: string; + readonly scriptVersion?: ScriptVersion; + readonly dispatchNamespace?: string; + readonly scriptTags?: string[]; + readonly outcome: string; + readonly executionModel: string; + readonly truncated: boolean; + readonly cpuTime: number; + readonly wallTime: number; +} +interface TraceItemAlarmEventInfo { + readonly scheduledTime: Date; +} +interface TraceItemCustomEventInfo { +} +interface TraceItemScheduledEventInfo { + readonly scheduledTime: number; + readonly cron: string; +} +interface TraceItemQueueEventInfo { + readonly queue: string; + readonly batchSize: number; +} +interface TraceItemEmailEventInfo { + readonly mailFrom: string; + readonly rcptTo: string; + readonly rawSize: number; +} +interface TraceItemTailEventInfo { + readonly consumedEvents: TraceItemTailEventInfoTailItem[]; +} +interface TraceItemTailEventInfoTailItem { + readonly scriptName: string | null; +} +interface TraceItemFetchEventInfo { + readonly response?: TraceItemFetchEventInfoResponse; + readonly request: TraceItemFetchEventInfoRequest; +} +interface TraceItemFetchEventInfoRequest { + readonly cf?: any; + readonly headers: Record; + readonly method: string; + readonly url: string; + getUnredacted(): TraceItemFetchEventInfoRequest; +} +interface TraceItemFetchEventInfoResponse { + readonly status: number; +} +interface TraceItemJsRpcEventInfo { + readonly rpcMethod: string; +} +interface TraceItemHibernatableWebSocketEventInfo { + readonly getWebSocketEvent: TraceItemHibernatableWebSocketEventInfoMessage | TraceItemHibernatableWebSocketEventInfoClose | TraceItemHibernatableWebSocketEventInfoError; +} +interface TraceItemHibernatableWebSocketEventInfoMessage { + readonly webSocketEventType: string; +} +interface TraceItemHibernatableWebSocketEventInfoClose { + readonly webSocketEventType: string; + readonly code: number; + readonly wasClean: boolean; +} +interface TraceItemHibernatableWebSocketEventInfoError { + readonly webSocketEventType: string; +} +interface TraceLog { + readonly timestamp: number; + readonly level: string; + readonly message: any; +} +interface TraceException { + readonly timestamp: number; + readonly message: string; + readonly name: string; + readonly stack?: string; +} +interface TraceDiagnosticChannelEvent { + readonly timestamp: number; + readonly channel: string; + readonly message: any; +} +interface TraceMetrics { + readonly cpuTime: number; + readonly wallTime: number; +} +interface UnsafeTraceMetrics { + fromTrace(item: TraceItem): TraceMetrics; +} +/** + * The URL interface represents an object providing static methods used for creating object URLs. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL) + */ +declare class URL { + constructor(url: string | URL, base?: string | URL); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/origin) */ + get origin(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href) */ + get href(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href) */ + set href(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol) */ + get protocol(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol) */ + set protocol(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username) */ + get username(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username) */ + set username(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password) */ + get password(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password) */ + set password(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host) */ + get host(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host) */ + set host(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname) */ + get hostname(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname) */ + set hostname(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port) */ + get port(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port) */ + set port(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname) */ + get pathname(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname) */ + set pathname(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search) */ + get search(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search) */ + set search(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash) */ + get hash(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash) */ + set hash(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/searchParams) */ + get searchParams(): URLSearchParams; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/toJSON) */ + toJSON(): string; + /*function toString() { [native code] }*/ + toString(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/canParse_static) */ + static canParse(url: string, base?: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/parse_static) */ + static parse(url: string, base?: string): URL | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/createObjectURL_static) */ + static createObjectURL(object: File | Blob): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL_static) */ + static revokeObjectURL(object_url: string): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams) */ +declare class URLSearchParams { + constructor(init?: (Iterable> | Record | string)); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/size) */ + get size(): number; + /** + * Appends a specified key/value pair as a new search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/append) + */ + append(name: string, value: string): void; + /** + * Deletes the given search parameter, and its associated value, from the list of all search parameters. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/delete) + */ + delete(name: string, value?: string): void; + /** + * Returns the first value associated to the given search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/get) + */ + get(name: string): string | null; + /** + * Returns all the values association with a given search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/getAll) + */ + getAll(name: string): string[]; + /** + * Returns a Boolean indicating if such a search parameter exists. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/has) + */ + has(name: string, value?: string): boolean; + /** + * Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/set) + */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/sort) */ + sort(): void; + /* Returns an array of key, value pairs for every entry in the search params. */ + entries(): IterableIterator<[ + key: string, + value: string + ]>; + /* Returns a list of keys in the search params. */ + keys(): IterableIterator; + /* Returns a list of values in the search params. */ + values(): IterableIterator; + forEach(callback: (this: This, value: string, key: string, parent: URLSearchParams) => void, thisArg?: This): void; + /*function toString() { [native code] } Returns a string containing a query string suitable for use in a URL. Does not include the question mark. */ + toString(): string; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: string + ]>; +} +declare class URLPattern { + constructor(input?: (string | URLPatternInit), baseURL?: (string | URLPatternOptions), patternOptions?: URLPatternOptions); + get protocol(): string; + get username(): string; + get password(): string; + get hostname(): string; + get port(): string; + get pathname(): string; + get search(): string; + get hash(): string; + test(input?: (string | URLPatternInit), baseURL?: string): boolean; + exec(input?: (string | URLPatternInit), baseURL?: string): URLPatternResult | null; +} +interface URLPatternInit { + protocol?: string; + username?: string; + password?: string; + hostname?: string; + port?: string; + pathname?: string; + search?: string; + hash?: string; + baseURL?: string; +} +interface URLPatternComponentResult { + input: string; + groups: Record; +} +interface URLPatternResult { + inputs: (string | URLPatternInit)[]; + protocol: URLPatternComponentResult; + username: URLPatternComponentResult; + password: URLPatternComponentResult; + hostname: URLPatternComponentResult; + port: URLPatternComponentResult; + pathname: URLPatternComponentResult; + search: URLPatternComponentResult; + hash: URLPatternComponentResult; +} +interface URLPatternOptions { + ignoreCase?: boolean; +} +/** + * A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent) + */ +declare class CloseEvent extends Event { + constructor(type: string, initializer?: CloseEventInit); + /** + * Returns the WebSocket connection close code provided by the server. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/code) + */ + readonly code: number; + /** + * Returns the WebSocket connection close reason provided by the server. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/reason) + */ + readonly reason: string; + /** + * Returns true if the connection closed cleanly; false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/wasClean) + */ + readonly wasClean: boolean; +} +interface CloseEventInit { + code?: number; + reason?: string; + wasClean?: boolean; +} +/** + * A message received by a target object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent) + */ +declare class MessageEvent extends Event { + constructor(type: string, initializer: MessageEventInit); + /** + * Returns the data of the message. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/data) + */ + readonly data: ArrayBuffer | string; +} +interface MessageEventInit { + data: ArrayBuffer | string; +} +type WebSocketEventMap = { + close: CloseEvent; + message: MessageEvent; + open: Event; + error: ErrorEvent; +}; +/** + * Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket) + */ +declare var WebSocket: { + prototype: WebSocket; + new (url: string, protocols?: (string[] | string)): WebSocket; + readonly READY_STATE_CONNECTING: number; + readonly CONNECTING: number; + readonly READY_STATE_OPEN: number; + readonly OPEN: number; + readonly READY_STATE_CLOSING: number; + readonly CLOSING: number; + readonly READY_STATE_CLOSED: number; + readonly CLOSED: number; +}; +/** + * Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket) + */ +interface WebSocket extends EventTarget { + accept(): void; + /** + * Transmits data using the WebSocket connection. data can be a string, a Blob, an ArrayBuffer, or an ArrayBufferView. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/send) + */ + send(message: (ArrayBuffer | ArrayBufferView) | string): void; + /** + * Closes the WebSocket connection, optionally using code as the the WebSocket connection close code and reason as the the WebSocket connection close reason. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/close) + */ + close(code?: number, reason?: string): void; + serializeAttachment(attachment: any): void; + deserializeAttachment(): any | null; + /** + * Returns the state of the WebSocket object's connection. It can have the values described below. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/readyState) + */ + readyState: number; + /** + * Returns the URL that was used to establish the WebSocket connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/url) + */ + url: string | null; + /** + * Returns the subprotocol selected by the server, if any. It can be used in conjunction with the array form of the constructor's second argument to perform subprotocol negotiation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/protocol) + */ + protocol: string | null; + /** + * Returns the extensions selected by the server, if any. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/extensions) + */ + extensions: string | null; +} +declare const WebSocketPair: { + new (): { + 0: WebSocket; + 1: WebSocket; + }; +}; +interface SqlStorage { + exec>(query: string, ...bindings: any[]): SqlStorageCursor; + get databaseSize(): number; + Cursor: typeof SqlStorageCursor; + Statement: typeof SqlStorageStatement; +} +declare abstract class SqlStorageStatement { +} +type SqlStorageValue = ArrayBuffer | string | number | null; +declare abstract class SqlStorageCursor> { + next(): { + done?: false; + value: T; + } | { + done: true; + value?: never; + }; + toArray(): T[]; + one(): T; + raw(): IterableIterator; + columnNames: string[]; + get rowsRead(): number; + get rowsWritten(): number; + [Symbol.iterator](): IterableIterator; +} +interface Socket { + get readable(): ReadableStream; + get writable(): WritableStream; + get closed(): Promise; + get opened(): Promise; + get upgraded(): boolean; + get secureTransport(): "on" | "off" | "starttls"; + close(): Promise; + startTls(options?: TlsOptions): Socket; +} +interface SocketOptions { + secureTransport?: string; + allowHalfOpen: boolean; + highWaterMark?: (number | bigint); +} +interface SocketAddress { + hostname: string; + port: number; +} +interface TlsOptions { + expectedServerHostname?: string; +} +interface SocketInfo { + remoteAddress?: string; + localAddress?: string; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource) */ +declare class EventSource extends EventTarget { + constructor(url: string, init?: EventSourceEventSourceInit); + /** + * Aborts any instances of the fetch algorithm started for this EventSource object, and sets the readyState attribute to CLOSED. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close) + */ + close(): void; + /** + * Returns the URL providing the event stream. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/url) + */ + get url(): string; + /** + * Returns true if the credentials mode for connection requests to the URL providing the event stream is set to "include", and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/withCredentials) + */ + get withCredentials(): boolean; + /** + * Returns the state of this EventSource object's connection. It can have the values described below. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState) + */ + get readyState(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */ + get onopen(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */ + set onopen(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */ + get onmessage(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */ + set onmessage(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */ + get onerror(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */ + set onerror(value: any | null); + static readonly CONNECTING: number; + static readonly OPEN: number; + static readonly CLOSED: number; + static from(stream: ReadableStream): EventSource; +} +interface EventSourceEventSourceInit { + withCredentials?: boolean; + fetcher?: Fetcher; +} +interface Container { + get running(): boolean; + start(options?: ContainerStartupOptions): void; + monitor(): Promise; + destroy(error?: any): Promise; + signal(signo: number): void; + getTcpPort(port: number): Fetcher; +} +interface ContainerStartupOptions { + entrypoint?: string[]; + enableInternet: boolean; + env?: Record; +} +type AiImageClassificationInput = { + image: number[]; +}; +type AiImageClassificationOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiImageClassification { + inputs: AiImageClassificationInput; + postProcessedOutputs: AiImageClassificationOutput; +} +type AiImageToTextInput = { + image: number[]; + prompt?: string; + max_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + raw?: boolean; + messages?: RoleScopedChatInput[]; +}; +type AiImageToTextOutput = { + description: string; +}; +declare abstract class BaseAiImageToText { + inputs: AiImageToTextInput; + postProcessedOutputs: AiImageToTextOutput; +} +type AiImageTextToTextInput = { + image: string; + prompt?: string; + max_tokens?: number; + temperature?: number; + ignore_eos?: boolean; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + raw?: boolean; + messages?: RoleScopedChatInput[]; +}; +type AiImageTextToTextOutput = { + description: string; +}; +declare abstract class BaseAiImageTextToText { + inputs: AiImageTextToTextInput; + postProcessedOutputs: AiImageTextToTextOutput; +} +type AiObjectDetectionInput = { + image: number[]; +}; +type AiObjectDetectionOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiObjectDetection { + inputs: AiObjectDetectionInput; + postProcessedOutputs: AiObjectDetectionOutput; +} +type AiSentenceSimilarityInput = { + source: string; + sentences: string[]; +}; +type AiSentenceSimilarityOutput = number[]; +declare abstract class BaseAiSentenceSimilarity { + inputs: AiSentenceSimilarityInput; + postProcessedOutputs: AiSentenceSimilarityOutput; +} +type AiAutomaticSpeechRecognitionInput = { + audio: number[]; +}; +type AiAutomaticSpeechRecognitionOutput = { + text?: string; + words?: { + word: string; + start: number; + end: number; + }[]; + vtt?: string; +}; +declare abstract class BaseAiAutomaticSpeechRecognition { + inputs: AiAutomaticSpeechRecognitionInput; + postProcessedOutputs: AiAutomaticSpeechRecognitionOutput; +} +type AiSummarizationInput = { + input_text: string; + max_length?: number; +}; +type AiSummarizationOutput = { + summary: string; +}; +declare abstract class BaseAiSummarization { + inputs: AiSummarizationInput; + postProcessedOutputs: AiSummarizationOutput; +} +type AiTextClassificationInput = { + text: string; +}; +type AiTextClassificationOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiTextClassification { + inputs: AiTextClassificationInput; + postProcessedOutputs: AiTextClassificationOutput; +} +type AiTextEmbeddingsInput = { + text: string | string[]; +}; +type AiTextEmbeddingsOutput = { + shape: number[]; + data: number[][]; +}; +declare abstract class BaseAiTextEmbeddings { + inputs: AiTextEmbeddingsInput; + postProcessedOutputs: AiTextEmbeddingsOutput; +} +type RoleScopedChatInput = { + role: "user" | "assistant" | "system" | "tool" | (string & NonNullable); + content: string; + name?: string; +}; +type AiTextGenerationToolLegacyInput = { + name: string; + description: string; + parameters?: { + type: "object" | (string & NonNullable); + properties: { + [key: string]: { + type: string; + description?: string; + }; + }; + required: string[]; + }; +}; +type AiTextGenerationToolInput = { + type: "function" | (string & NonNullable); + function: { + name: string; + description: string; + parameters?: { + type: "object" | (string & NonNullable); + properties: { + [key: string]: { + type: string; + description?: string; + }; + }; + required: string[]; + }; + }; +}; +type AiTextGenerationFunctionsInput = { + name: string; + code: string; +}; +type AiTextGenerationResponseFormat = { + type: string; + json_schema?: any; +}; +type AiTextGenerationInput = { + prompt?: string; + raw?: boolean; + stream?: boolean; + max_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + messages?: RoleScopedChatInput[]; + response_format?: AiTextGenerationResponseFormat; + tools?: AiTextGenerationToolInput[] | AiTextGenerationToolLegacyInput[] | (object & NonNullable); + functions?: AiTextGenerationFunctionsInput[]; +}; +type AiTextGenerationOutput = { + response?: string; + tool_calls?: { + name: string; + arguments: unknown; + }[]; +}; +declare abstract class BaseAiTextGeneration { + inputs: AiTextGenerationInput; + postProcessedOutputs: AiTextGenerationOutput; +} +type AiTextToSpeechInput = { + prompt: string; + lang?: string; +}; +type AiTextToSpeechOutput = Uint8Array | { + audio: string; +}; +declare abstract class BaseAiTextToSpeech { + inputs: AiTextToSpeechInput; + postProcessedOutputs: AiTextToSpeechOutput; +} +type AiTextToImageInput = { + prompt: string; + negative_prompt?: string; + height?: number; + width?: number; + image?: number[]; + image_b64?: string; + mask?: number[]; + num_steps?: number; + strength?: number; + guidance?: number; + seed?: number; +}; +type AiTextToImageOutput = ReadableStream; +declare abstract class BaseAiTextToImage { + inputs: AiTextToImageInput; + postProcessedOutputs: AiTextToImageOutput; +} +type AiTranslationInput = { + text: string; + target_lang: string; + source_lang?: string; +}; +type AiTranslationOutput = { + translated_text?: string; +}; +declare abstract class BaseAiTranslation { + inputs: AiTranslationInput; + postProcessedOutputs: AiTranslationOutput; +} +type Ai_Cf_Baai_Bge_Base_En_V1_5_Input = { + text: string | string[]; + /** + * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy. + */ + pooling?: "mean" | "cls"; +} | { + /** + * Batch of the embeddings requests to run using async-queue + */ + requests: { + text: string | string[]; + /** + * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy. + */ + pooling?: "mean" | "cls"; + }[]; +}; +type Ai_Cf_Baai_Bge_Base_En_V1_5_Output = { + shape?: number[]; + /** + * Embeddings of the requested text values + */ + data?: number[][]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} | AsyncResponse; +interface AsyncResponse { + /** + * The async request id that can be used to obtain the results. + */ + request_id?: string; +} +declare abstract class Base_Ai_Cf_Baai_Bge_Base_En_V1_5 { + inputs: Ai_Cf_Baai_Bge_Base_En_V1_5_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_Base_En_V1_5_Output; +} +type Ai_Cf_Openai_Whisper_Input = string | { + /** + * An array of integers that represent the audio data constrained to 8-bit unsigned integer values + */ + audio: number[]; +}; +interface Ai_Cf_Openai_Whisper_Output { + /** + * The transcription + */ + text: string; + word_count?: number; + words?: { + word?: string; + /** + * The second this word begins in the recording + */ + start?: number; + /** + * The ending second when the word completes + */ + end?: number; + }[]; + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper { + inputs: Ai_Cf_Openai_Whisper_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Output; +} +type Ai_Cf_Meta_M2M100_1_2B_Input = { + /** + * The text to be translated + */ + text: string; + /** + * The language code of the source text (e.g., 'en' for English). Defaults to 'en' if not specified + */ + source_lang?: string; + /** + * The language code to translate the text into (e.g., 'es' for Spanish) + */ + target_lang: string; +} | { + /** + * Batch of the embeddings requests to run using async-queue + */ + requests: { + /** + * The text to be translated + */ + text: string; + /** + * The language code of the source text (e.g., 'en' for English). Defaults to 'en' if not specified + */ + source_lang?: string; + /** + * The language code to translate the text into (e.g., 'es' for Spanish) + */ + target_lang: string; + }[]; +}; +type Ai_Cf_Meta_M2M100_1_2B_Output = { + /** + * The translated text in the target language + */ + translated_text?: string; +} | AsyncResponse; +declare abstract class Base_Ai_Cf_Meta_M2M100_1_2B { + inputs: Ai_Cf_Meta_M2M100_1_2B_Input; + postProcessedOutputs: Ai_Cf_Meta_M2M100_1_2B_Output; +} +type Ai_Cf_Baai_Bge_Small_En_V1_5_Input = { + text: string | string[]; + /** + * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy. + */ + pooling?: "mean" | "cls"; +} | { + /** + * Batch of the embeddings requests to run using async-queue + */ + requests: { + text: string | string[]; + /** + * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy. + */ + pooling?: "mean" | "cls"; + }[]; +}; +type Ai_Cf_Baai_Bge_Small_En_V1_5_Output = { + shape?: number[]; + /** + * Embeddings of the requested text values + */ + data?: number[][]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} | AsyncResponse; +declare abstract class Base_Ai_Cf_Baai_Bge_Small_En_V1_5 { + inputs: Ai_Cf_Baai_Bge_Small_En_V1_5_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_Small_En_V1_5_Output; +} +type Ai_Cf_Baai_Bge_Large_En_V1_5_Input = { + text: string | string[]; + /** + * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy. + */ + pooling?: "mean" | "cls"; +} | { + /** + * Batch of the embeddings requests to run using async-queue + */ + requests: { + text: string | string[]; + /** + * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy. + */ + pooling?: "mean" | "cls"; + }[]; +}; +type Ai_Cf_Baai_Bge_Large_En_V1_5_Output = { + shape?: number[]; + /** + * Embeddings of the requested text values + */ + data?: number[][]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} | AsyncResponse; +declare abstract class Base_Ai_Cf_Baai_Bge_Large_En_V1_5 { + inputs: Ai_Cf_Baai_Bge_Large_En_V1_5_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_Large_En_V1_5_Output; +} +type Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input = string | { + /** + * The input text prompt for the model to generate a response. + */ + prompt?: string; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + image: number[] | (string & NonNullable); + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; +}; +interface Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output { + description?: string; +} +declare abstract class Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M { + inputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input; + postProcessedOutputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output; +} +type Ai_Cf_Openai_Whisper_Tiny_En_Input = string | { + /** + * An array of integers that represent the audio data constrained to 8-bit unsigned integer values + */ + audio: number[]; +}; +interface Ai_Cf_Openai_Whisper_Tiny_En_Output { + /** + * The transcription + */ + text: string; + word_count?: number; + words?: { + word?: string; + /** + * The second this word begins in the recording + */ + start?: number; + /** + * The ending second when the word completes + */ + end?: number; + }[]; + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper_Tiny_En { + inputs: Ai_Cf_Openai_Whisper_Tiny_En_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Tiny_En_Output; +} +interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input { + /** + * Base64 encoded value of the audio data. + */ + audio: string; + /** + * Supported tasks are 'translate' or 'transcribe'. + */ + task?: string; + /** + * The language of the audio being transcribed or translated. + */ + language?: string; + /** + * Preprocess the audio with a voice activity detection model. + */ + vad_filter?: boolean; + /** + * A text prompt to help provide context to the model on the contents of the audio. + */ + initial_prompt?: string; + /** + * The prefix it appended the the beginning of the output of the transcription and can guide the transcription result. + */ + prefix?: string; +} +interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output { + transcription_info?: { + /** + * The language of the audio being transcribed or translated. + */ + language?: string; + /** + * The confidence level or probability of the detected language being accurate, represented as a decimal between 0 and 1. + */ + language_probability?: number; + /** + * The total duration of the original audio file, in seconds. + */ + duration?: number; + /** + * The duration of the audio after applying Voice Activity Detection (VAD) to remove silent or irrelevant sections, in seconds. + */ + duration_after_vad?: number; + }; + /** + * The complete transcription of the audio. + */ + text: string; + /** + * The total number of words in the transcription. + */ + word_count?: number; + segments?: { + /** + * The starting time of the segment within the audio, in seconds. + */ + start?: number; + /** + * The ending time of the segment within the audio, in seconds. + */ + end?: number; + /** + * The transcription of the segment. + */ + text?: string; + /** + * The temperature used in the decoding process, controlling randomness in predictions. Lower values result in more deterministic outputs. + */ + temperature?: number; + /** + * The average log probability of the predictions for the words in this segment, indicating overall confidence. + */ + avg_logprob?: number; + /** + * The compression ratio of the input to the output, measuring how much the text was compressed during the transcription process. + */ + compression_ratio?: number; + /** + * The probability that the segment contains no speech, represented as a decimal between 0 and 1. + */ + no_speech_prob?: number; + words?: { + /** + * The individual word transcribed from the audio. + */ + word?: string; + /** + * The starting time of the word within the audio, in seconds. + */ + start?: number; + /** + * The ending time of the word within the audio, in seconds. + */ + end?: number; + }[]; + }[]; + /** + * The transcription in WebVTT format, which includes timing and text information for use in subtitles. + */ + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo { + inputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output; +} +type Ai_Cf_Baai_Bge_M3_Input = BGEM3InputQueryAndContexts | BGEM3InputEmbedding | { + /** + * Batch of the embeddings requests to run using async-queue + */ + requests: (BGEM3InputQueryAndContexts1 | BGEM3InputEmbedding1)[]; +}; +interface BGEM3InputQueryAndContexts { + /** + * A query you wish to perform against the provided contexts. If no query is provided the model with respond with embeddings for contexts + */ + query?: string; + /** + * List of provided contexts. Note that the index in this array is important, as the response will refer to it. + */ + contexts: { + /** + * One of the provided context content + */ + text?: string; + }[]; + /** + * When provided with too long context should the model error out or truncate the context to fit? + */ + truncate_inputs?: boolean; +} +interface BGEM3InputEmbedding { + text: string | string[]; + /** + * When provided with too long context should the model error out or truncate the context to fit? + */ + truncate_inputs?: boolean; +} +interface BGEM3InputQueryAndContexts1 { + /** + * A query you wish to perform against the provided contexts. If no query is provided the model with respond with embeddings for contexts + */ + query?: string; + /** + * List of provided contexts. Note that the index in this array is important, as the response will refer to it. + */ + contexts: { + /** + * One of the provided context content + */ + text?: string; + }[]; + /** + * When provided with too long context should the model error out or truncate the context to fit? + */ + truncate_inputs?: boolean; +} +interface BGEM3InputEmbedding1 { + text: string | string[]; + /** + * When provided with too long context should the model error out or truncate the context to fit? + */ + truncate_inputs?: boolean; +} +type Ai_Cf_Baai_Bge_M3_Output = BGEM3OuputQuery | BGEM3OutputEmbeddingForContexts | BGEM3OuputEmbedding | AsyncResponse; +interface BGEM3OuputQuery { + response?: { + /** + * Index of the context in the request + */ + id?: number; + /** + * Score of the context under the index. + */ + score?: number; + }[]; +} +interface BGEM3OutputEmbeddingForContexts { + response?: number[][]; + shape?: number[]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} +interface BGEM3OuputEmbedding { + shape?: number[]; + /** + * Embeddings of the requested text values + */ + data?: number[][]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} +declare abstract class Base_Ai_Cf_Baai_Bge_M3 { + inputs: Ai_Cf_Baai_Bge_M3_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_M3_Output; +} +interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input { + /** + * A text description of the image you want to generate. + */ + prompt: string; + /** + * The number of diffusion steps; higher values can improve quality but take longer. + */ + steps?: number; +} +interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output { + /** + * The generated image in Base64 format. + */ + image?: string; +} +declare abstract class Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell { + inputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input; + postProcessedOutputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output; +} +type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input = Prompt | Messages; +interface Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + image?: number[] | (string & NonNullable); + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + /** + * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model. + */ + lora?: string; +} +interface Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role?: string; + /** + * The tool call id. Must be supplied for tool calls for Mistral-3. If you don't know what to put here you can fall back to 000000001 + */ + tool_call_id?: string; + content?: string | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }[] | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }; + }[]; + image?: number[] | (string & NonNullable); + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * If true, the response will be streamed back incrementally. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output = { + /** + * The generated text response from the model + */ + response?: string; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +}; +declare abstract class Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct { + inputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output; +} +type Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Input = Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Prompt | Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Messages | AsyncBatch; +interface Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + /** + * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model. + */ + lora?: string; + response_format?: JSONMode; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +interface JSONMode { + type?: "json_object" | "json_schema"; + json_schema?: unknown; +} +interface Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role: string; + /** + * The content of the message as a string. + */ + content: string; + }[]; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + response_format?: JSONMode; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +interface AsyncBatch { + requests?: { + /** + * User-supplied reference. This field will be present in the response as well it can be used to reference the request and response. It's NOT validated to be unique. + */ + external_reference?: string; + /** + * Prompt for the text generation model + */ + prompt?: string; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + response_format?: JSONMode; + }[]; +} +type Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Output = { + /** + * The generated text response from the model + */ + response: string; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +} | AsyncResponse; +declare abstract class Base_Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast { + inputs: Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Output; +} +interface Ai_Cf_Meta_Llama_Guard_3_8B_Input { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender must alternate between 'user' and 'assistant'. + */ + role: "user" | "assistant"; + /** + * The content of the message as a string. + */ + content: string; + }[]; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Dictate the output format of the generated response. + */ + response_format?: { + /** + * Set to json_object to process and output generated text as JSON. + */ + type?: string; + }; +} +interface Ai_Cf_Meta_Llama_Guard_3_8B_Output { + response?: string | { + /** + * Whether the conversation is safe or not. + */ + safe?: boolean; + /** + * A list of what hazard categories predicted for the conversation, if the conversation is deemed unsafe. + */ + categories?: string[]; + }; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; +} +declare abstract class Base_Ai_Cf_Meta_Llama_Guard_3_8B { + inputs: Ai_Cf_Meta_Llama_Guard_3_8B_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_Guard_3_8B_Output; +} +interface Ai_Cf_Baai_Bge_Reranker_Base_Input { + /** + * A query you wish to perform against the provided contexts. + */ + query: string; + /** + * Number of returned results starting with the best score. + */ + top_k?: number; + /** + * List of provided contexts. Note that the index in this array is important, as the response will refer to it. + */ + contexts: { + /** + * One of the provided context content + */ + text?: string; + }[]; +} +interface Ai_Cf_Baai_Bge_Reranker_Base_Output { + response?: { + /** + * Index of the context in the request + */ + id?: number; + /** + * Score of the context under the index. + */ + score?: number; + }[]; +} +declare abstract class Base_Ai_Cf_Baai_Bge_Reranker_Base { + inputs: Ai_Cf_Baai_Bge_Reranker_Base_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_Reranker_Base_Output; +} +type Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Input = Qwen2_5_Coder_32B_Instruct_Prompt | Qwen2_5_Coder_32B_Instruct_Messages; +interface Qwen2_5_Coder_32B_Instruct_Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + /** + * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model. + */ + lora?: string; + response_format?: JSONMode; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +interface Qwen2_5_Coder_32B_Instruct_Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role: string; + /** + * The content of the message as a string. + */ + content: string; + }[]; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + response_format?: JSONMode; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Output = { + /** + * The generated text response from the model + */ + response: string; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +}; +declare abstract class Base_Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct { + inputs: Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Input; + postProcessedOutputs: Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Output; +} +type Ai_Cf_Qwen_Qwq_32B_Input = Qwen_Qwq_32B_Prompt | Qwen_Qwq_32B_Messages; +interface Qwen_Qwq_32B_Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + /** + * JSON schema that should be fulfilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +interface Qwen_Qwq_32B_Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role?: string; + /** + * The tool call id. Must be supplied for tool calls for Mistral-3. If you don't know what to put here you can fall back to 000000001 + */ + tool_call_id?: string; + content?: string | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }[] | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }; + }[]; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * JSON schema that should be fufilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Qwen_Qwq_32B_Output = { + /** + * The generated text response from the model + */ + response: string; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +}; +declare abstract class Base_Ai_Cf_Qwen_Qwq_32B { + inputs: Ai_Cf_Qwen_Qwq_32B_Input; + postProcessedOutputs: Ai_Cf_Qwen_Qwq_32B_Output; +} +type Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Input = Mistral_Small_3_1_24B_Instruct_Prompt | Mistral_Small_3_1_24B_Instruct_Messages; +interface Mistral_Small_3_1_24B_Instruct_Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + /** + * JSON schema that should be fulfilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +interface Mistral_Small_3_1_24B_Instruct_Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role?: string; + /** + * The tool call id. Must be supplied for tool calls for Mistral-3. If you don't know what to put here you can fall back to 000000001 + */ + tool_call_id?: string; + content?: string | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }[] | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }; + }[]; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * JSON schema that should be fufilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Output = { + /** + * The generated text response from the model + */ + response: string; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +}; +declare abstract class Base_Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct { + inputs: Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Input; + postProcessedOutputs: Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Output; +} +type Ai_Cf_Google_Gemma_3_12B_It_Input = Google_Gemma_3_12B_It_Prompt | Google_Gemma_3_12B_It_Messages; +interface Google_Gemma_3_12B_It_Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + /** + * JSON schema that should be fufilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +interface Google_Gemma_3_12B_It_Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role?: string; + content?: string | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }[] | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }; + }[]; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * JSON schema that should be fufilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Google_Gemma_3_12B_It_Output = { + /** + * The generated text response from the model + */ + response: string; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +}; +declare abstract class Base_Ai_Cf_Google_Gemma_3_12B_It { + inputs: Ai_Cf_Google_Gemma_3_12B_It_Input; + postProcessedOutputs: Ai_Cf_Google_Gemma_3_12B_It_Output; +} +type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input = Ai_Cf_Meta_Llama_4_Prompt | Ai_Cf_Meta_Llama_4_Messages; +interface Ai_Cf_Meta_Llama_4_Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + /** + * JSON schema that should be fulfilled for the response. + */ + guided_json?: object; + response_format?: JSONMode; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +interface Ai_Cf_Meta_Llama_4_Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role?: string; + /** + * The tool call id. If you don't know what to put here you can fall back to 000000001 + */ + tool_call_id?: string; + content?: string | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }[] | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }; + }[]; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + response_format?: JSONMode; + /** + * JSON schema that should be fufilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output = { + /** + * The generated text response from the model + */ + response: string; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The tool call id. + */ + id?: string; + /** + * Specifies the type of tool (e.g., 'function'). + */ + type?: string; + /** + * Details of the function tool. + */ + function?: { + /** + * The name of the tool to be called + */ + name?: string; + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + }; + }[]; +}; +declare abstract class Base_Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct { + inputs: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output; +} +interface AiModels { + "@cf/huggingface/distilbert-sst-2-int8": BaseAiTextClassification; + "@cf/stabilityai/stable-diffusion-xl-base-1.0": BaseAiTextToImage; + "@cf/runwayml/stable-diffusion-v1-5-inpainting": BaseAiTextToImage; + "@cf/runwayml/stable-diffusion-v1-5-img2img": BaseAiTextToImage; + "@cf/lykon/dreamshaper-8-lcm": BaseAiTextToImage; + "@cf/bytedance/stable-diffusion-xl-lightning": BaseAiTextToImage; + "@cf/myshell-ai/melotts": BaseAiTextToSpeech; + "@cf/microsoft/resnet-50": BaseAiImageClassification; + "@cf/facebook/detr-resnet-50": BaseAiObjectDetection; + "@cf/meta/llama-2-7b-chat-int8": BaseAiTextGeneration; + "@cf/mistral/mistral-7b-instruct-v0.1": BaseAiTextGeneration; + "@cf/meta/llama-2-7b-chat-fp16": BaseAiTextGeneration; + "@hf/thebloke/llama-2-13b-chat-awq": BaseAiTextGeneration; + "@hf/thebloke/mistral-7b-instruct-v0.1-awq": BaseAiTextGeneration; + "@hf/thebloke/zephyr-7b-beta-awq": BaseAiTextGeneration; + "@hf/thebloke/openhermes-2.5-mistral-7b-awq": BaseAiTextGeneration; + "@hf/thebloke/neural-chat-7b-v3-1-awq": BaseAiTextGeneration; + "@hf/thebloke/llamaguard-7b-awq": BaseAiTextGeneration; + "@hf/thebloke/deepseek-coder-6.7b-base-awq": BaseAiTextGeneration; + "@hf/thebloke/deepseek-coder-6.7b-instruct-awq": BaseAiTextGeneration; + "@cf/deepseek-ai/deepseek-math-7b-instruct": BaseAiTextGeneration; + "@cf/defog/sqlcoder-7b-2": BaseAiTextGeneration; + "@cf/openchat/openchat-3.5-0106": BaseAiTextGeneration; + "@cf/tiiuae/falcon-7b-instruct": BaseAiTextGeneration; + "@cf/thebloke/discolm-german-7b-v1-awq": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-0.5b-chat": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-7b-chat-awq": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-14b-chat-awq": BaseAiTextGeneration; + "@cf/tinyllama/tinyllama-1.1b-chat-v1.0": BaseAiTextGeneration; + "@cf/microsoft/phi-2": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-1.8b-chat": BaseAiTextGeneration; + "@cf/mistral/mistral-7b-instruct-v0.2-lora": BaseAiTextGeneration; + "@hf/nousresearch/hermes-2-pro-mistral-7b": BaseAiTextGeneration; + "@hf/nexusflow/starling-lm-7b-beta": BaseAiTextGeneration; + "@hf/google/gemma-7b-it": BaseAiTextGeneration; + "@cf/meta-llama/llama-2-7b-chat-hf-lora": BaseAiTextGeneration; + "@cf/google/gemma-2b-it-lora": BaseAiTextGeneration; + "@cf/google/gemma-7b-it-lora": BaseAiTextGeneration; + "@hf/mistral/mistral-7b-instruct-v0.2": BaseAiTextGeneration; + "@cf/meta/llama-3-8b-instruct": BaseAiTextGeneration; + "@cf/fblgit/una-cybertron-7b-v2-bf16": BaseAiTextGeneration; + "@cf/meta/llama-3-8b-instruct-awq": BaseAiTextGeneration; + "@hf/meta-llama/meta-llama-3-8b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct-fp8": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct-awq": BaseAiTextGeneration; + "@cf/meta/llama-3.2-3b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.2-1b-instruct": BaseAiTextGeneration; + "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": BaseAiTextGeneration; + "@cf/facebook/bart-large-cnn": BaseAiSummarization; + "@cf/llava-hf/llava-1.5-7b-hf": BaseAiImageToText; + "@cf/baai/bge-base-en-v1.5": Base_Ai_Cf_Baai_Bge_Base_En_V1_5; + "@cf/openai/whisper": Base_Ai_Cf_Openai_Whisper; + "@cf/meta/m2m100-1.2b": Base_Ai_Cf_Meta_M2M100_1_2B; + "@cf/baai/bge-small-en-v1.5": Base_Ai_Cf_Baai_Bge_Small_En_V1_5; + "@cf/baai/bge-large-en-v1.5": Base_Ai_Cf_Baai_Bge_Large_En_V1_5; + "@cf/unum/uform-gen2-qwen-500m": Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M; + "@cf/openai/whisper-tiny-en": Base_Ai_Cf_Openai_Whisper_Tiny_En; + "@cf/openai/whisper-large-v3-turbo": Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo; + "@cf/baai/bge-m3": Base_Ai_Cf_Baai_Bge_M3; + "@cf/black-forest-labs/flux-1-schnell": Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell; + "@cf/meta/llama-3.2-11b-vision-instruct": Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct; + "@cf/meta/llama-3.3-70b-instruct-fp8-fast": Base_Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast; + "@cf/meta/llama-guard-3-8b": Base_Ai_Cf_Meta_Llama_Guard_3_8B; + "@cf/baai/bge-reranker-base": Base_Ai_Cf_Baai_Bge_Reranker_Base; + "@cf/qwen/qwen2.5-coder-32b-instruct": Base_Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct; + "@cf/qwen/qwq-32b": Base_Ai_Cf_Qwen_Qwq_32B; + "@cf/mistralai/mistral-small-3.1-24b-instruct": Base_Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct; + "@cf/google/gemma-3-12b-it": Base_Ai_Cf_Google_Gemma_3_12B_It; + "@cf/meta/llama-4-scout-17b-16e-instruct": Base_Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct; +} +type AiOptions = { + /** + * Send requests as an asynchronous batch job, only works for supported models + * https://developers.cloudflare.com/workers-ai/features/batch-api + */ + queueRequest?: boolean; + gateway?: GatewayOptions; + returnRawResponse?: boolean; + prefix?: string; + extraHeaders?: object; +}; +type ConversionResponse = { + name: string; + mimeType: string; + format: "markdown"; + tokens: number; + data: string; +}; +type AiModelsSearchParams = { + author?: string; + hide_experimental?: boolean; + page?: number; + per_page?: number; + search?: string; + source?: number; + task?: string; +}; +type AiModelsSearchObject = { + id: string; + source: number; + name: string; + description: string; + task: { + id: string; + name: string; + description: string; + }; + tags: string[]; + properties: { + property_id: string; + value: string; + }[]; +}; +interface InferenceUpstreamError extends Error { +} +interface AiInternalError extends Error { +} +type AiModelListType = Record; +declare abstract class Ai { + aiGatewayLogId: string | null; + gateway(gatewayId: string): AiGateway; + autorag(autoragId: string): AutoRAG; + run(model: Name, inputs: InputOptions, options?: Options): Promise; + models(params?: AiModelsSearchParams): Promise; + toMarkdown(files: { + name: string; + blob: Blob; + }[], options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; + toMarkdown(files: { + name: string; + blob: Blob; + }, options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; +} +type GatewayRetries = { + maxAttempts?: 1 | 2 | 3 | 4 | 5; + retryDelayMs?: number; + backoff?: 'constant' | 'linear' | 'exponential'; +}; +type GatewayOptions = { + id: string; + cacheKey?: string; + cacheTtl?: number; + skipCache?: boolean; + metadata?: Record; + collectLog?: boolean; + eventId?: string; + requestTimeoutMs?: number; + retries?: GatewayRetries; +}; +type AiGatewayPatchLog = { + score?: number | null; + feedback?: -1 | 1 | null; + metadata?: Record | null; +}; +type AiGatewayLog = { + id: string; + provider: string; + model: string; + model_type?: string; + path: string; + duration: number; + request_type?: string; + request_content_type?: string; + status_code: number; + response_content_type?: string; + success: boolean; + cached: boolean; + tokens_in?: number; + tokens_out?: number; + metadata?: Record; + step?: number; + cost?: number; + custom_cost?: boolean; + request_size: number; + request_head?: string; + request_head_complete: boolean; + response_size: number; + response_head?: string; + response_head_complete: boolean; + created_at: Date; +}; +type AIGatewayProviders = 'workers-ai' | 'anthropic' | 'aws-bedrock' | 'azure-openai' | 'google-vertex-ai' | 'huggingface' | 'openai' | 'perplexity-ai' | 'replicate' | 'groq' | 'cohere' | 'google-ai-studio' | 'mistral' | 'grok' | 'openrouter' | 'deepseek' | 'cerebras' | 'cartesia' | 'elevenlabs' | 'adobe-firefly'; +type AIGatewayHeaders = { + 'cf-aig-metadata': Record | string; + 'cf-aig-custom-cost': { + per_token_in?: number; + per_token_out?: number; + } | { + total_cost?: number; + } | string; + 'cf-aig-cache-ttl': number | string; + 'cf-aig-skip-cache': boolean | string; + 'cf-aig-cache-key': string; + 'cf-aig-event-id': string; + 'cf-aig-request-timeout': number | string; + 'cf-aig-max-attempts': number | string; + 'cf-aig-retry-delay': number | string; + 'cf-aig-backoff': string; + 'cf-aig-collect-log': boolean | string; + Authorization: string; + 'Content-Type': string; + [key: string]: string | number | boolean | object; +}; +type AIGatewayUniversalRequest = { + provider: AIGatewayProviders | string; // eslint-disable-line + endpoint: string; + headers: Partial; + query: unknown; +}; +interface AiGatewayInternalError extends Error { +} +interface AiGatewayLogNotFound extends Error { +} +declare abstract class AiGateway { + patchLog(logId: string, data: AiGatewayPatchLog): Promise; + getLog(logId: string): Promise; + run(data: AIGatewayUniversalRequest | AIGatewayUniversalRequest[], options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; + getUrl(provider?: AIGatewayProviders | string): Promise; // eslint-disable-line +} +interface AutoRAGInternalError extends Error { +} +interface AutoRAGNotFoundError extends Error { +} +interface AutoRAGUnauthorizedError extends Error { +} +interface AutoRAGNameNotSetError extends Error { +} +type ComparisonFilter = { + key: string; + type: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte'; + value: string | number | boolean; +}; +type CompoundFilter = { + type: 'and' | 'or'; + filters: ComparisonFilter[]; +}; +type AutoRagSearchRequest = { + query: string; + filters?: CompoundFilter | ComparisonFilter; + max_num_results?: number; + ranking_options?: { + ranker?: string; + score_threshold?: number; + }; + rewrite_query?: boolean; +}; +type AutoRagAiSearchRequest = AutoRagSearchRequest & { + stream?: boolean; +}; +type AutoRagAiSearchRequestStreaming = Omit & { + stream: true; +}; +type AutoRagSearchResponse = { + object: 'vector_store.search_results.page'; + search_query: string; + data: { + file_id: string; + filename: string; + score: number; + attributes: Record; + content: { + type: 'text'; + text: string; + }[]; + }[]; + has_more: boolean; + next_page: string | null; +}; +type AutoRagListResponse = { + id: string; + enable: boolean; + type: string; + source: string; + vectorize_name: string; + paused: boolean; + status: string; +}[]; +type AutoRagAiSearchResponse = AutoRagSearchResponse & { + response: string; +}; +declare abstract class AutoRAG { + list(): Promise; + search(params: AutoRagSearchRequest): Promise; + aiSearch(params: AutoRagAiSearchRequestStreaming): Promise; + aiSearch(params: AutoRagAiSearchRequest): Promise; + aiSearch(params: AutoRagAiSearchRequest): Promise; +} +interface BasicImageTransformations { + /** + * Maximum width in image pixels. The value must be an integer. + */ + width?: number; + /** + * Maximum height in image pixels. The value must be an integer. + */ + height?: number; + /** + * Resizing mode as a string. It affects interpretation of width and height + * options: + * - scale-down: Similar to contain, but the image is never enlarged. If + * the image is larger than given width or height, it will be resized. + * Otherwise its original size will be kept. + * - contain: Resizes to maximum size that fits within the given width and + * height. If only a single dimension is given (e.g. only width), the + * image will be shrunk or enlarged to exactly match that dimension. + * Aspect ratio is always preserved. + * - cover: Resizes (shrinks or enlarges) to fill the entire area of width + * and height. If the image has an aspect ratio different from the ratio + * of width and height, it will be cropped to fit. + * - crop: The image will be shrunk and cropped to fit within the area + * specified by width and height. The image will not be enlarged. For images + * smaller than the given dimensions it's the same as scale-down. For + * images larger than the given dimensions, it's the same as cover. + * See also trim. + * - pad: Resizes to the maximum size that fits within the given width and + * height, and then fills the remaining area with a background color + * (white by default). Use of this mode is not recommended, as the same + * effect can be more efficiently achieved with the contain mode and the + * CSS object-fit: contain property. + * - squeeze: Stretches and deforms to the width and height given, even if it + * breaks aspect ratio + */ + fit?: "scale-down" | "contain" | "cover" | "crop" | "pad" | "squeeze"; + /** + * When cropping with fit: "cover", this defines the side or point that should + * be left uncropped. The value is either a string + * "left", "right", "top", "bottom", "auto", or "center" (the default), + * or an object {x, y} containing focal point coordinates in the original + * image expressed as fractions ranging from 0.0 (top or left) to 1.0 + * (bottom or right), 0.5 being the center. {fit: "cover", gravity: "top"} will + * crop bottom or left and right sides as necessary, but won’t crop anything + * from the top. {fit: "cover", gravity: {x:0.5, y:0.2}} will crop each side to + * preserve as much as possible around a point at 20% of the height of the + * source image. + */ + gravity?: 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | BasicImageTransformationsGravityCoordinates; + /** + * Background color to add underneath the image. Applies only to images with + * transparency (such as PNG). Accepts any CSS color (#RRGGBB, rgba(…), + * hsl(…), etc.) + */ + background?: string; + /** + * Number of degrees (90, 180, 270) to rotate the image by. width and height + * options refer to axes after rotation. + */ + rotate?: 0 | 90 | 180 | 270 | 360; +} +interface BasicImageTransformationsGravityCoordinates { + x?: number; + y?: number; + mode?: 'remainder' | 'box-center'; +} +/** + * In addition to the properties you can set in the RequestInit dict + * that you pass as an argument to the Request constructor, you can + * set certain properties of a `cf` object to control how Cloudflare + * features are applied to that new Request. + * + * Note: Currently, these properties cannot be tested in the + * playground. + */ +interface RequestInitCfProperties extends Record { + cacheEverything?: boolean; + /** + * A request's cache key is what determines if two requests are + * "the same" for caching purposes. If a request has the same cache key + * as some previous request, then we can serve the same cached response for + * both. (e.g. 'some-key') + * + * Only available for Enterprise customers. + */ + cacheKey?: string; + /** + * This allows you to append additional Cache-Tag response headers + * to the origin response without modifications to the origin server. + * This will allow for greater control over the Purge by Cache Tag feature + * utilizing changes only in the Workers process. + * + * Only available for Enterprise customers. + */ + cacheTags?: string[]; + /** + * Force response to be cached for a given number of seconds. (e.g. 300) + */ + cacheTtl?: number; + /** + * Force response to be cached for a given number of seconds based on the Origin status code. + * (e.g. { '200-299': 86400, '404': 1, '500-599': 0 }) + */ + cacheTtlByStatus?: Record; + scrapeShield?: boolean; + apps?: boolean; + image?: RequestInitCfPropertiesImage; + minify?: RequestInitCfPropertiesImageMinify; + mirage?: boolean; + polish?: "lossy" | "lossless" | "off"; + r2?: RequestInitCfPropertiesR2; + /** + * Redirects the request to an alternate origin server. You can use this, + * for example, to implement load balancing across several origins. + * (e.g.us-east.example.com) + * + * Note - For security reasons, the hostname set in resolveOverride must + * be proxied on the same Cloudflare zone of the incoming request. + * Otherwise, the setting is ignored. CNAME hosts are allowed, so to + * resolve to a host under a different domain or a DNS only domain first + * declare a CNAME record within your own zone’s DNS mapping to the + * external hostname, set proxy on Cloudflare, then set resolveOverride + * to point to that CNAME record. + */ + resolveOverride?: string; +} +interface RequestInitCfPropertiesImageDraw extends BasicImageTransformations { + /** + * Absolute URL of the image file to use for the drawing. It can be any of + * the supported file formats. For drawing of watermarks or non-rectangular + * overlays we recommend using PNG or WebP images. + */ + url: string; + /** + * Floating-point number between 0 (transparent) and 1 (opaque). + * For example, opacity: 0.5 makes overlay semitransparent. + */ + opacity?: number; + /** + * - If set to true, the overlay image will be tiled to cover the entire + * area. This is useful for stock-photo-like watermarks. + * - If set to "x", the overlay image will be tiled horizontally only + * (form a line). + * - If set to "y", the overlay image will be tiled vertically only + * (form a line). + */ + repeat?: true | "x" | "y"; + /** + * Position of the overlay image relative to a given edge. Each property is + * an offset in pixels. 0 aligns exactly to the edge. For example, left: 10 + * positions left side of the overlay 10 pixels from the left edge of the + * image it's drawn over. bottom: 0 aligns bottom of the overlay with bottom + * of the background image. + * + * Setting both left & right, or both top & bottom is an error. + * + * If no position is specified, the image will be centered. + */ + top?: number; + left?: number; + bottom?: number; + right?: number; +} +interface RequestInitCfPropertiesImage extends BasicImageTransformations { + /** + * Device Pixel Ratio. Default 1. Multiplier for width/height that makes it + * easier to specify higher-DPI sizes in . + */ + dpr?: number; + /** + * Allows you to trim your image. Takes dpr into account and is performed before + * resizing or rotation. + * + * It can be used as: + * - left, top, right, bottom - it will specify the number of pixels to cut + * off each side + * - width, height - the width/height you'd like to end up with - can be used + * in combination with the properties above + * - border - this will automatically trim the surroundings of an image based on + * it's color. It consists of three properties: + * - color: rgb or hex representation of the color you wish to trim (todo: verify the rgba bit) + * - tolerance: difference from color to treat as color + * - keep: the number of pixels of border to keep + */ + trim?: "border" | { + top?: number; + bottom?: number; + left?: number; + right?: number; + width?: number; + height?: number; + border?: boolean | { + color?: string; + tolerance?: number; + keep?: number; + }; + }; + /** + * Quality setting from 1-100 (useful values are in 60-90 range). Lower values + * make images look worse, but load faster. The default is 85. It applies only + * to JPEG and WebP images. It doesn’t have any effect on PNG. + */ + quality?: number | "low" | "medium-low" | "medium-high" | "high"; + /** + * Output format to generate. It can be: + * - avif: generate images in AVIF format. + * - webp: generate images in Google WebP format. Set quality to 100 to get + * the WebP-lossless format. + * - json: instead of generating an image, outputs information about the + * image, in JSON format. The JSON object will contain image size + * (before and after resizing), source image’s MIME type, file size, etc. + * - jpeg: generate images in JPEG format. + * - png: generate images in PNG format. + */ + format?: "avif" | "webp" | "json" | "jpeg" | "png" | "baseline-jpeg" | "png-force" | "svg"; + /** + * Whether to preserve animation frames from input files. Default is true. + * Setting it to false reduces animations to still images. This setting is + * recommended when enlarging images or processing arbitrary user content, + * because large GIF animations can weigh tens or even hundreds of megabytes. + * It is also useful to set anim:false when using format:"json" to get the + * response quicker without the number of frames. + */ + anim?: boolean; + /** + * What EXIF data should be preserved in the output image. Note that EXIF + * rotation and embedded color profiles are always applied ("baked in" into + * the image), and aren't affected by this option. Note that if the Polish + * feature is enabled, all metadata may have been removed already and this + * option may have no effect. + * - keep: Preserve most of EXIF metadata, including GPS location if there's + * any. + * - copyright: Only keep the copyright tag, and discard everything else. + * This is the default behavior for JPEG files. + * - none: Discard all invisible EXIF metadata. Currently WebP and PNG + * output formats always discard metadata. + */ + metadata?: "keep" | "copyright" | "none"; + /** + * Strength of sharpening filter to apply to the image. Floating-point + * number between 0 (no sharpening, default) and 10 (maximum). 1.0 is a + * recommended value for downscaled images. + */ + sharpen?: number; + /** + * Radius of a blur filter (approximate gaussian). Maximum supported radius + * is 250. + */ + blur?: number; + /** + * Overlays are drawn in the order they appear in the array (last array + * entry is the topmost layer). + */ + draw?: RequestInitCfPropertiesImageDraw[]; + /** + * Fetching image from authenticated origin. Setting this property will + * pass authentication headers (Authorization, Cookie, etc.) through to + * the origin. + */ + "origin-auth"?: "share-publicly"; + /** + * Adds a border around the image. The border is added after resizing. Border + * width takes dpr into account, and can be specified either using a single + * width property, or individually for each side. + */ + border?: { + color: string; + width: number; + } | { + color: string; + top: number; + right: number; + bottom: number; + left: number; + }; + /** + * Increase brightness by a factor. A value of 1.0 equals no change, a value + * of 0.5 equals half brightness, and a value of 2.0 equals twice as bright. + * 0 is ignored. + */ + brightness?: number; + /** + * Increase contrast by a factor. A value of 1.0 equals no change, a value of + * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is + * ignored. + */ + contrast?: number; + /** + * Increase exposure by a factor. A value of 1.0 equals no change, a value of + * 0.5 darkens the image, and a value of 2.0 lightens the image. 0 is ignored. + */ + gamma?: number; + /** + * Increase contrast by a factor. A value of 1.0 equals no change, a value of + * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is + * ignored. + */ + saturation?: number; + /** + * Flips the images horizontally, vertically, or both. Flipping is applied before + * rotation, so if you apply flip=h,rotate=90 then the image will be flipped + * horizontally, then rotated by 90 degrees. + */ + flip?: 'h' | 'v' | 'hv'; + /** + * Slightly reduces latency on a cache miss by selecting a + * quickest-to-compress file format, at a cost of increased file size and + * lower image quality. It will usually override the format option and choose + * JPEG over WebP or AVIF. We do not recommend using this option, except in + * unusual circumstances like resizing uncacheable dynamically-generated + * images. + */ + compression?: "fast"; +} +interface RequestInitCfPropertiesImageMinify { + javascript?: boolean; + css?: boolean; + html?: boolean; +} +interface RequestInitCfPropertiesR2 { + /** + * Colo id of bucket that an object is stored in + */ + bucketColoId?: number; +} +/** + * Request metadata provided by Cloudflare's edge. + */ +type IncomingRequestCfProperties = IncomingRequestCfPropertiesBase & IncomingRequestCfPropertiesBotManagementEnterprise & IncomingRequestCfPropertiesCloudflareForSaaSEnterprise & IncomingRequestCfPropertiesGeographicInformation & IncomingRequestCfPropertiesCloudflareAccessOrApiShield; +interface IncomingRequestCfPropertiesBase extends Record { + /** + * [ASN](https://www.iana.org/assignments/as-numbers/as-numbers.xhtml) of the incoming request. + * + * @example 395747 + */ + asn: number; + /** + * The organization which owns the ASN of the incoming request. + * + * @example "Google Cloud" + */ + asOrganization: string; + /** + * The original value of the `Accept-Encoding` header if Cloudflare modified it. + * + * @example "gzip, deflate, br" + */ + clientAcceptEncoding?: string; + /** + * The number of milliseconds it took for the request to reach your worker. + * + * @example 22 + */ + clientTcpRtt?: number; + /** + * The three-letter [IATA](https://en.wikipedia.org/wiki/IATA_airport_code) + * airport code of the data center that the request hit. + * + * @example "DFW" + */ + colo: string; + /** + * Represents the upstream's response to a + * [TCP `keepalive` message](https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) + * from cloudflare. + * + * For workers with no upstream, this will always be `1`. + * + * @example 3 + */ + edgeRequestKeepAliveStatus: IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus; + /** + * The HTTP Protocol the request used. + * + * @example "HTTP/2" + */ + httpProtocol: string; + /** + * The browser-requested prioritization information in the request object. + * + * If no information was set, defaults to the empty string `""` + * + * @example "weight=192;exclusive=0;group=3;group-weight=127" + * @default "" + */ + requestPriority: string; + /** + * The TLS version of the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "TLSv1.3" + */ + tlsVersion: string; + /** + * The cipher for the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "AEAD-AES128-GCM-SHA256" + */ + tlsCipher: string; + /** + * Metadata containing the [`HELLO`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2) and [`FINISHED`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9) messages from this request's TLS handshake. + * + * If the incoming request was served over plaintext (without TLS) this field is undefined. + */ + tlsExportedAuthenticator?: IncomingRequestCfPropertiesExportedAuthenticatorMetadata; +} +interface IncomingRequestCfPropertiesBotManagementBase { + /** + * Cloudflare’s [level of certainty](https://developers.cloudflare.com/bots/concepts/bot-score/) that a request comes from a bot, + * represented as an integer percentage between `1` (almost certainly a bot) and `99` (almost certainly human). + * + * @example 54 + */ + score: number; + /** + * A boolean value that is true if the request comes from a good bot, like Google or Bing. + * Most customers choose to allow this traffic. For more details, see [Traffic from known bots](https://developers.cloudflare.com/firewall/known-issues-and-faq/#how-does-firewall-rules-handle-traffic-from-known-bots). + */ + verifiedBot: boolean; + /** + * A boolean value that is true if the request originates from a + * Cloudflare-verified proxy service. + */ + corporateProxy: boolean; + /** + * A boolean value that's true if the request matches [file extensions](https://developers.cloudflare.com/bots/reference/static-resources/) for many types of static resources. + */ + staticResource: boolean; + /** + * List of IDs that correlate to the Bot Management heuristic detections made on a request (you can have multiple heuristic detections on the same request). + */ + detectionIds: number[]; +} +interface IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase; + /** + * Duplicate of `botManagement.score`. + * + * @deprecated + */ + clientTrustScore: number; +} +interface IncomingRequestCfPropertiesBotManagementEnterprise extends IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase & { + /** + * A [JA3 Fingerprint](https://developers.cloudflare.com/bots/concepts/ja3-fingerprint/) to help profile specific SSL/TLS clients + * across different destination IPs, Ports, and X509 certificates. + */ + ja3Hash: string; + }; +} +interface IncomingRequestCfPropertiesCloudflareForSaaSEnterprise { + /** + * Custom metadata set per-host in [Cloudflare for SaaS](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/). + * + * This field is only present if you have Cloudflare for SaaS enabled on your account + * and you have followed the [required steps to enable it]((https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/)). + */ + hostMetadata: HostMetadata; +} +interface IncomingRequestCfPropertiesCloudflareAccessOrApiShield { + /** + * Information about the client certificate presented to Cloudflare. + * + * This is populated when the incoming request is served over TLS using + * either Cloudflare Access or API Shield (mTLS) + * and the presented SSL certificate has a valid + * [Certificate Serial Number](https://ldapwiki.com/wiki/Certificate%20Serial%20Number) + * (i.e., not `null` or `""`). + * + * Otherwise, a set of placeholder values are used. + * + * The property `certPresented` will be set to `"1"` when + * the object is populated (i.e. the above conditions were met). + */ + tlsClientAuth: IncomingRequestCfPropertiesTLSClientAuth | IncomingRequestCfPropertiesTLSClientAuthPlaceholder; +} +/** + * Metadata about the request's TLS handshake + */ +interface IncomingRequestCfPropertiesExportedAuthenticatorMetadata { + /** + * The client's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + clientHandshake: string; + /** + * The server's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + serverHandshake: string; + /** + * The client's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + clientFinished: string; + /** + * The server's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + serverFinished: string; +} +/** + * Geographic data about the request's origin. + */ +interface IncomingRequestCfPropertiesGeographicInformation { + /** + * The [ISO 3166-1 Alpha 2](https://www.iso.org/iso-3166-country-codes.html) country code the request originated from. + * + * If your worker is [configured to accept TOR connections](https://support.cloudflare.com/hc/en-us/articles/203306930-Understanding-Cloudflare-Tor-support-and-Onion-Routing), this may also be `"T1"`, indicating a request that originated over TOR. + * + * If Cloudflare is unable to determine where the request originated this property is omitted. + * + * The country code `"T1"` is used for requests originating on TOR. + * + * @example "GB" + */ + country?: Iso3166Alpha2Code | "T1"; + /** + * If present, this property indicates that the request originated in the EU + * + * @example "1" + */ + isEUCountry?: "1"; + /** + * A two-letter code indicating the continent the request originated from. + * + * @example "AN" + */ + continent?: ContinentCode; + /** + * The city the request originated from + * + * @example "Austin" + */ + city?: string; + /** + * Postal code of the incoming request + * + * @example "78701" + */ + postalCode?: string; + /** + * Latitude of the incoming request + * + * @example "30.27130" + */ + latitude?: string; + /** + * Longitude of the incoming request + * + * @example "-97.74260" + */ + longitude?: string; + /** + * Timezone of the incoming request + * + * @example "America/Chicago" + */ + timezone?: string; + /** + * If known, the ISO 3166-2 name for the first level region associated with + * the IP address of the incoming request + * + * @example "Texas" + */ + region?: string; + /** + * If known, the ISO 3166-2 code for the first-level region associated with + * the IP address of the incoming request + * + * @example "TX" + */ + regionCode?: string; + /** + * Metro code (DMA) of the incoming request + * + * @example "635" + */ + metroCode?: string; +} +/** Data about the incoming request's TLS certificate */ +interface IncomingRequestCfPropertiesTLSClientAuth { + /** Always `"1"`, indicating that the certificate was presented */ + certPresented: "1"; + /** + * Result of certificate verification. + * + * @example "FAILED:self signed certificate" + */ + certVerified: Exclude; + /** The presented certificate's revokation status. + * + * - A value of `"1"` indicates the certificate has been revoked + * - A value of `"0"` indicates the certificate has not been revoked + */ + certRevoked: "1" | "0"; + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDN: string; + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certSubjectDN: string; + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDNRFC2253: string; + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certSubjectDNRFC2253: string; + /** The certificate issuer's distinguished name (legacy policies) */ + certIssuerDNLegacy: string; + /** The certificate subject's distinguished name (legacy policies) */ + certSubjectDNLegacy: string; + /** + * The certificate's serial number + * + * @example "00936EACBE07F201DF" + */ + certSerial: string; + /** + * The certificate issuer's serial number + * + * @example "2489002934BDFEA34" + */ + certIssuerSerial: string; + /** + * The certificate's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certSKI: string; + /** + * The certificate issuer's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certIssuerSKI: string; + /** + * The certificate's SHA-1 fingerprint + * + * @example "6b9109f323999e52259cda7373ff0b4d26bd232e" + */ + certFingerprintSHA1: string; + /** + * The certificate's SHA-256 fingerprint + * + * @example "acf77cf37b4156a2708e34c4eb755f9b5dbbe5ebb55adfec8f11493438d19e6ad3f157f81fa3b98278453d5652b0c1fd1d71e5695ae4d709803a4d3f39de9dea" + */ + certFingerprintSHA256: string; + /** + * The effective starting date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotBefore: string; + /** + * The effective expiration date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotAfter: string; +} +/** Placeholder values for TLS Client Authorization */ +interface IncomingRequestCfPropertiesTLSClientAuthPlaceholder { + certPresented: "0"; + certVerified: "NONE"; + certRevoked: "0"; + certIssuerDN: ""; + certSubjectDN: ""; + certIssuerDNRFC2253: ""; + certSubjectDNRFC2253: ""; + certIssuerDNLegacy: ""; + certSubjectDNLegacy: ""; + certSerial: ""; + certIssuerSerial: ""; + certSKI: ""; + certIssuerSKI: ""; + certFingerprintSHA1: ""; + certFingerprintSHA256: ""; + certNotBefore: ""; + certNotAfter: ""; +} +/** Possible outcomes of TLS verification */ +declare type CertVerificationStatus = +/** Authentication succeeded */ +"SUCCESS" +/** No certificate was presented */ + | "NONE" +/** Failed because the certificate was self-signed */ + | "FAILED:self signed certificate" +/** Failed because the certificate failed a trust chain check */ + | "FAILED:unable to verify the first certificate" +/** Failed because the certificate not yet valid */ + | "FAILED:certificate is not yet valid" +/** Failed because the certificate is expired */ + | "FAILED:certificate has expired" +/** Failed for another unspecified reason */ + | "FAILED"; +/** + * An upstream endpoint's response to a TCP `keepalive` message from Cloudflare. + */ +declare type IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus = 0 /** Unknown */ | 1 /** no keepalives (not found) */ | 2 /** no connection re-use, opening keepalive connection failed */ | 3 /** no connection re-use, keepalive accepted and saved */ | 4 /** connection re-use, refused by the origin server (`TCP FIN`) */ | 5; /** connection re-use, accepted by the origin server */ +/** ISO 3166-1 Alpha-2 codes */ +declare type Iso3166Alpha2Code = "AD" | "AE" | "AF" | "AG" | "AI" | "AL" | "AM" | "AO" | "AQ" | "AR" | "AS" | "AT" | "AU" | "AW" | "AX" | "AZ" | "BA" | "BB" | "BD" | "BE" | "BF" | "BG" | "BH" | "BI" | "BJ" | "BL" | "BM" | "BN" | "BO" | "BQ" | "BR" | "BS" | "BT" | "BV" | "BW" | "BY" | "BZ" | "CA" | "CC" | "CD" | "CF" | "CG" | "CH" | "CI" | "CK" | "CL" | "CM" | "CN" | "CO" | "CR" | "CU" | "CV" | "CW" | "CX" | "CY" | "CZ" | "DE" | "DJ" | "DK" | "DM" | "DO" | "DZ" | "EC" | "EE" | "EG" | "EH" | "ER" | "ES" | "ET" | "FI" | "FJ" | "FK" | "FM" | "FO" | "FR" | "GA" | "GB" | "GD" | "GE" | "GF" | "GG" | "GH" | "GI" | "GL" | "GM" | "GN" | "GP" | "GQ" | "GR" | "GS" | "GT" | "GU" | "GW" | "GY" | "HK" | "HM" | "HN" | "HR" | "HT" | "HU" | "ID" | "IE" | "IL" | "IM" | "IN" | "IO" | "IQ" | "IR" | "IS" | "IT" | "JE" | "JM" | "JO" | "JP" | "KE" | "KG" | "KH" | "KI" | "KM" | "KN" | "KP" | "KR" | "KW" | "KY" | "KZ" | "LA" | "LB" | "LC" | "LI" | "LK" | "LR" | "LS" | "LT" | "LU" | "LV" | "LY" | "MA" | "MC" | "MD" | "ME" | "MF" | "MG" | "MH" | "MK" | "ML" | "MM" | "MN" | "MO" | "MP" | "MQ" | "MR" | "MS" | "MT" | "MU" | "MV" | "MW" | "MX" | "MY" | "MZ" | "NA" | "NC" | "NE" | "NF" | "NG" | "NI" | "NL" | "NO" | "NP" | "NR" | "NU" | "NZ" | "OM" | "PA" | "PE" | "PF" | "PG" | "PH" | "PK" | "PL" | "PM" | "PN" | "PR" | "PS" | "PT" | "PW" | "PY" | "QA" | "RE" | "RO" | "RS" | "RU" | "RW" | "SA" | "SB" | "SC" | "SD" | "SE" | "SG" | "SH" | "SI" | "SJ" | "SK" | "SL" | "SM" | "SN" | "SO" | "SR" | "SS" | "ST" | "SV" | "SX" | "SY" | "SZ" | "TC" | "TD" | "TF" | "TG" | "TH" | "TJ" | "TK" | "TL" | "TM" | "TN" | "TO" | "TR" | "TT" | "TV" | "TW" | "TZ" | "UA" | "UG" | "UM" | "US" | "UY" | "UZ" | "VA" | "VC" | "VE" | "VG" | "VI" | "VN" | "VU" | "WF" | "WS" | "YE" | "YT" | "ZA" | "ZM" | "ZW"; +/** The 2-letter continent codes Cloudflare uses */ +declare type ContinentCode = "AF" | "AN" | "AS" | "EU" | "NA" | "OC" | "SA"; +type CfProperties = IncomingRequestCfProperties | RequestInitCfProperties; +interface D1Meta { + duration: number; + size_after: number; + rows_read: number; + rows_written: number; + last_row_id: number; + changed_db: boolean; + changes: number; + /** + * The region of the database instance that executed the query. + */ + served_by_region?: string; + /** + * True if-and-only-if the database instance that executed the query was the primary. + */ + served_by_primary?: boolean; + timings?: { + /** + * The duration of the SQL query execution by the database instance. It doesn't include any network time. + */ + sql_duration_ms: number; + }; +} +interface D1Response { + success: true; + meta: D1Meta & Record; + error?: never; +} +type D1Result = D1Response & { + results: T[]; +}; +interface D1ExecResult { + count: number; + duration: number; +} +type D1SessionConstraint = +// Indicates that the first query should go to the primary, and the rest queries +// using the same D1DatabaseSession will go to any replica that is consistent with +// the bookmark maintained by the session (returned by the first query). +"first-primary" +// Indicates that the first query can go anywhere (primary or replica), and the rest queries +// using the same D1DatabaseSession will go to any replica that is consistent with +// the bookmark maintained by the session (returned by the first query). + | "first-unconstrained"; +type D1SessionBookmark = string; +declare abstract class D1Database { + prepare(query: string): D1PreparedStatement; + batch(statements: D1PreparedStatement[]): Promise[]>; + exec(query: string): Promise; + /** + * Creates a new D1 Session anchored at the given constraint or the bookmark. + * All queries executed using the created session will have sequential consistency, + * meaning that all writes done through the session will be visible in subsequent reads. + * + * @param constraintOrBookmark Either the session constraint or the explicit bookmark to anchor the created session. + */ + withSession(constraintOrBookmark?: D1SessionBookmark | D1SessionConstraint): D1DatabaseSession; + /** + * @deprecated dump() will be removed soon, only applies to deprecated alpha v1 databases. + */ + dump(): Promise; +} +declare abstract class D1DatabaseSession { + prepare(query: string): D1PreparedStatement; + batch(statements: D1PreparedStatement[]): Promise[]>; + /** + * @returns The latest session bookmark across all executed queries on the session. + * If no query has been executed yet, `null` is returned. + */ + getBookmark(): D1SessionBookmark | null; +} +declare abstract class D1PreparedStatement { + bind(...values: unknown[]): D1PreparedStatement; + first(colName: string): Promise; + first>(): Promise; + run>(): Promise>; + all>(): Promise>; + raw(options: { + columnNames: true; + }): Promise<[ + string[], + ...T[] + ]>; + raw(options?: { + columnNames?: false; + }): Promise; +} +// `Disposable` was added to TypeScript's standard lib types in version 5.2. +// To support older TypeScript versions, define an empty `Disposable` interface. +// Users won't be able to use `using`/`Symbol.dispose` without upgrading to 5.2, +// but this will ensure type checking on older versions still passes. +// TypeScript's interface merging will ensure our empty interface is effectively +// ignored when `Disposable` is included in the standard lib. +interface Disposable { +} +/** + * An email message that can be sent from a Worker. + */ +interface EmailMessage { + /** + * Envelope From attribute of the email message. + */ + readonly from: string; + /** + * Envelope To attribute of the email message. + */ + readonly to: string; +} +/** + * An email message that is sent to a consumer Worker and can be rejected/forwarded. + */ +interface ForwardableEmailMessage extends EmailMessage { + /** + * Stream of the email message content. + */ + readonly raw: ReadableStream; + /** + * An [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers). + */ + readonly headers: Headers; + /** + * Size of the email message content. + */ + readonly rawSize: number; + /** + * Reject this email message by returning a permanent SMTP error back to the connecting client including the given reason. + * @param reason The reject reason. + * @returns void + */ + setReject(reason: string): void; + /** + * Forward this email message to a verified destination address of the account. + * @param rcptTo Verified destination address. + * @param headers A [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers). + * @returns A promise that resolves when the email message is forwarded. + */ + forward(rcptTo: string, headers?: Headers): Promise; + /** + * Reply to the sender of this email message with a new EmailMessage object. + * @param message The reply message. + * @returns A promise that resolves when the email message is replied. + */ + reply(message: EmailMessage): Promise; +} +/** + * A binding that allows a Worker to send email messages. + */ +interface SendEmail { + send(message: EmailMessage): Promise; +} +declare abstract class EmailEvent extends ExtendableEvent { + readonly message: ForwardableEmailMessage; +} +declare type EmailExportedHandler = (message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext) => void | Promise; +declare module "cloudflare:email" { + let _EmailMessage: { + prototype: EmailMessage; + new (from: string, to: string, raw: ReadableStream | string): EmailMessage; + }; + export { _EmailMessage as EmailMessage }; +} +interface Hyperdrive { + /** + * Connect directly to Hyperdrive as if it's your database, returning a TCP socket. + * + * Calling this method returns an idential socket to if you call + * `connect("host:port")` using the `host` and `port` fields from this object. + * Pick whichever approach works better with your preferred DB client library. + * + * Note that this socket is not yet authenticated -- it's expected that your + * code (or preferably, the client library of your choice) will authenticate + * using the information in this class's readonly fields. + */ + connect(): Socket; + /** + * A valid DB connection string that can be passed straight into the typical + * client library/driver/ORM. This will typically be the easiest way to use + * Hyperdrive. + */ + readonly connectionString: string; + /* + * A randomly generated hostname that is only valid within the context of the + * currently running Worker which, when passed into `connect()` function from + * the "cloudflare:sockets" module, will connect to the Hyperdrive instance + * for your database. + */ + readonly host: string; + /* + * The port that must be paired the the host field when connecting. + */ + readonly port: number; + /* + * The username to use when authenticating to your database via Hyperdrive. + * Unlike the host and password, this will be the same every time + */ + readonly user: string; + /* + * The randomly generated password to use when authenticating to your + * database via Hyperdrive. Like the host field, this password is only valid + * within the context of the currently running Worker instance from which + * it's read. + */ + readonly password: string; + /* + * The name of the database to connect to. + */ + readonly database: string; +} +// Copyright (c) 2024 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +type ImageInfoResponse = { + format: 'image/svg+xml'; +} | { + format: string; + fileSize: number; + width: number; + height: number; +}; +type ImageTransform = { + width?: number; + height?: number; + background?: string; + blur?: number; + border?: { + color?: string; + width?: number; + } | { + top?: number; + bottom?: number; + left?: number; + right?: number; + }; + brightness?: number; + contrast?: number; + fit?: 'scale-down' | 'contain' | 'pad' | 'squeeze' | 'cover' | 'crop'; + flip?: 'h' | 'v' | 'hv'; + gamma?: number; + gravity?: 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | { + x?: number; + y?: number; + mode: 'remainder' | 'box-center'; + }; + rotate?: 0 | 90 | 180 | 270; + saturation?: number; + sharpen?: number; + trim?: "border" | { + top?: number; + bottom?: number; + left?: number; + right?: number; + width?: number; + height?: number; + border?: boolean | { + color?: string; + tolerance?: number; + keep?: number; + }; + }; +}; +type ImageDrawOptions = { + opacity?: number; + repeat?: boolean | string; + top?: number; + left?: number; + bottom?: number; + right?: number; +}; +type ImageOutputOptions = { + format: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' | 'image/avif' | 'rgb' | 'rgba'; + quality?: number; + background?: string; +}; +interface ImagesBinding { + /** + * Get image metadata (type, width and height) + * @throws {@link ImagesError} with code 9412 if input is not an image + * @param stream The image bytes + */ + info(stream: ReadableStream): Promise; + /** + * Begin applying a series of transformations to an image + * @param stream The image bytes + * @returns A transform handle + */ + input(stream: ReadableStream): ImageTransformer; +} +interface ImageTransformer { + /** + * Apply transform next, returning a transform handle. + * You can then apply more transformations, draw, or retrieve the output. + * @param transform + */ + transform(transform: ImageTransform): ImageTransformer; + /** + * Draw an image on this transformer, returning a transform handle. + * You can then apply more transformations, draw, or retrieve the output. + * @param image The image (or transformer that will give the image) to draw + * @param options The options configuring how to draw the image + */ + draw(image: ReadableStream | ImageTransformer, options?: ImageDrawOptions): ImageTransformer; + /** + * Retrieve the image that results from applying the transforms to the + * provided input + * @param options Options that apply to the output e.g. output format + */ + output(options: ImageOutputOptions): Promise; +} +interface ImageTransformationResult { + /** + * The image as a response, ready to store in cache or return to users + */ + response(): Response; + /** + * The content type of the returned image + */ + contentType(): string; + /** + * The bytes of the response + */ + image(): ReadableStream; +} +interface ImagesError extends Error { + readonly code: number; + readonly message: string; + readonly stack?: string; +} +type Params

= Record; +type EventContext = { + request: Request>; + functionPath: string; + waitUntil: (promise: Promise) => void; + passThroughOnException: () => void; + next: (input?: Request | string, init?: RequestInit) => Promise; + env: Env & { + ASSETS: { + fetch: typeof fetch; + }; + }; + params: Params

; + data: Data; +}; +type PagesFunction = Record> = (context: EventContext) => Response | Promise; +type EventPluginContext = { + request: Request>; + functionPath: string; + waitUntil: (promise: Promise) => void; + passThroughOnException: () => void; + next: (input?: Request | string, init?: RequestInit) => Promise; + env: Env & { + ASSETS: { + fetch: typeof fetch; + }; + }; + params: Params

; + data: Data; + pluginArgs: PluginArgs; +}; +type PagesPluginFunction = Record, PluginArgs = unknown> = (context: EventPluginContext) => Response | Promise; +declare module "assets:*" { + export const onRequest: PagesFunction; +} +// Copyright (c) 2022-2023 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +declare module "cloudflare:pipelines" { + export abstract class PipelineTransformationEntrypoint { + protected env: Env; + protected ctx: ExecutionContext; + constructor(ctx: ExecutionContext, env: Env); + /** + * run recieves an array of PipelineRecord which can be + * transformed and returned to the pipeline + * @param records Incoming records from the pipeline to be transformed + * @param metadata Information about the specific pipeline calling the transformation entrypoint + * @returns A promise containing the transformed PipelineRecord array + */ + public run(records: I[], metadata: PipelineBatchMetadata): Promise; + } + export type PipelineRecord = Record; + export type PipelineBatchMetadata = { + pipelineId: string; + pipelineName: string; + }; + export interface Pipeline { + /** + * The Pipeline interface represents the type of a binding to a Pipeline + * + * @param records The records to send to the pipeline + */ + send(records: T[]): Promise; + } +} +// PubSubMessage represents an incoming PubSub message. +// The message includes metadata about the broker, the client, and the payload +// itself. +// https://developers.cloudflare.com/pub-sub/ +interface PubSubMessage { + // Message ID + readonly mid: number; + // MQTT broker FQDN in the form mqtts://BROKER.NAMESPACE.cloudflarepubsub.com:PORT + readonly broker: string; + // The MQTT topic the message was sent on. + readonly topic: string; + // The client ID of the client that published this message. + readonly clientId: string; + // The unique identifier (JWT ID) used by the client to authenticate, if token + // auth was used. + readonly jti?: string; + // A Unix timestamp (seconds from Jan 1, 1970), set when the Pub/Sub Broker + // received the message from the client. + readonly receivedAt: number; + // An (optional) string with the MIME type of the payload, if set by the + // client. + readonly contentType: string; + // Set to 1 when the payload is a UTF-8 string + // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901063 + readonly payloadFormatIndicator: number; + // Pub/Sub (MQTT) payloads can be UTF-8 strings, or byte arrays. + // You can use payloadFormatIndicator to inspect this before decoding. + payload: string | Uint8Array; +} +// JsonWebKey extended by kid parameter +interface JsonWebKeyWithKid extends JsonWebKey { + // Key Identifier of the JWK + readonly kid: string; +} +interface RateLimitOptions { + key: string; +} +interface RateLimitOutcome { + success: boolean; +} +interface RateLimit { + /** + * Rate limit a request based on the provided options. + * @see https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/ + * @returns A promise that resolves with the outcome of the rate limit. + */ + limit(options: RateLimitOptions): Promise; +} +// Namespace for RPC utility types. Unfortunately, we can't use a `module` here as these types need +// to referenced by `Fetcher`. This is included in the "importable" version of the types which +// strips all `module` blocks. +declare namespace Rpc { + // Branded types for identifying `WorkerEntrypoint`/`DurableObject`/`Target`s. + // TypeScript uses *structural* typing meaning anything with the same shape as type `T` is a `T`. + // For the classes exported by `cloudflare:workers` we want *nominal* typing (i.e. we only want to + // accept `WorkerEntrypoint` from `cloudflare:workers`, not any other class with the same shape) + export const __RPC_STUB_BRAND: '__RPC_STUB_BRAND'; + export const __RPC_TARGET_BRAND: '__RPC_TARGET_BRAND'; + export const __WORKER_ENTRYPOINT_BRAND: '__WORKER_ENTRYPOINT_BRAND'; + export const __DURABLE_OBJECT_BRAND: '__DURABLE_OBJECT_BRAND'; + export const __WORKFLOW_ENTRYPOINT_BRAND: '__WORKFLOW_ENTRYPOINT_BRAND'; + export interface RpcTargetBranded { + [__RPC_TARGET_BRAND]: never; + } + export interface WorkerEntrypointBranded { + [__WORKER_ENTRYPOINT_BRAND]: never; + } + export interface DurableObjectBranded { + [__DURABLE_OBJECT_BRAND]: never; + } + export interface WorkflowEntrypointBranded { + [__WORKFLOW_ENTRYPOINT_BRAND]: never; + } + export type EntrypointBranded = WorkerEntrypointBranded | DurableObjectBranded | WorkflowEntrypointBranded; + // Types that can be used through `Stub`s + export type Stubable = RpcTargetBranded | ((...args: any[]) => any); + // Types that can be passed over RPC + // The reason for using a generic type here is to build a serializable subset of structured + // cloneable composite types. This allows types defined with the "interface" keyword to pass the + // serializable check as well. Otherwise, only types defined with the "type" keyword would pass. + type Serializable = + // Structured cloneables + BaseType + // Structured cloneable composites + | Map ? Serializable : never, T extends Map ? Serializable : never> | Set ? Serializable : never> | ReadonlyArray ? Serializable : never> | { + [K in keyof T]: K extends number | string ? Serializable : never; + } + // Special types + | Stub + // Serialized as stubs, see `Stubify` + | Stubable; + // Base type for all RPC stubs, including common memory management methods. + // `T` is used as a marker type for unwrapping `Stub`s later. + interface StubBase extends Disposable { + [__RPC_STUB_BRAND]: T; + dup(): this; + } + export type Stub = Provider & StubBase; + // This represents all the types that can be sent as-is over an RPC boundary + type BaseType = void | undefined | null | boolean | number | bigint | string | TypedArray | ArrayBuffer | DataView | Date | Error | RegExp | ReadableStream | WritableStream | Request | Response | Headers; + // Recursively rewrite all `Stubable` types with `Stub`s + // prettier-ignore + type Stubify = T extends Stubable ? Stub : T extends Map ? Map, Stubify> : T extends Set ? Set> : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends BaseType ? T : T extends { + [key: string | number]: any; + } ? { + [K in keyof T]: Stubify; + } : T; + // Recursively rewrite all `Stub`s with the corresponding `T`s. + // Note we use `StubBase` instead of `Stub` here to avoid circular dependencies: + // `Stub` depends on `Provider`, which depends on `Unstubify`, which would depend on `Stub`. + // prettier-ignore + type Unstubify = T extends StubBase ? V : T extends Map ? Map, Unstubify> : T extends Set ? Set> : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends BaseType ? T : T extends { + [key: string | number]: unknown; + } ? { + [K in keyof T]: Unstubify; + } : T; + type UnstubifyAll = { + [I in keyof A]: Unstubify; + }; + // Utility type for adding `Provider`/`Disposable`s to `object` types only. + // Note `unknown & T` is equivalent to `T`. + type MaybeProvider = T extends object ? Provider : unknown; + type MaybeDisposable = T extends object ? Disposable : unknown; + // Type for method return or property on an RPC interface. + // - Stubable types are replaced by stubs. + // - Serializable types are passed by value, with stubable types replaced by stubs + // and a top-level `Disposer`. + // Everything else can't be passed over PRC. + // Technically, we use custom thenables here, but they quack like `Promise`s. + // Intersecting with `(Maybe)Provider` allows pipelining. + // prettier-ignore + type Result = R extends Stubable ? Promise> & Provider : R extends Serializable ? Promise & MaybeDisposable> & MaybeProvider : never; + // Type for method or property on an RPC interface. + // For methods, unwrap `Stub`s in parameters, and rewrite returns to be `Result`s. + // Unwrapping `Stub`s allows calling with `Stubable` arguments. + // For properties, rewrite types to be `Result`s. + // In each case, unwrap `Promise`s. + type MethodOrProperty = V extends (...args: infer P) => infer R ? (...args: UnstubifyAll

) => Result> : Result>; + // Type for the callable part of an `Provider` if `T` is callable. + // This is intersected with methods/properties. + type MaybeCallableProvider = T extends (...args: any[]) => any ? MethodOrProperty : unknown; + // Base type for all other types providing RPC-like interfaces. + // Rewrites all methods/properties to be `MethodOrProperty`s, while preserving callable types. + // `Reserved` names (e.g. stub method names like `dup()`) and symbols can't be accessed over RPC. + export type Provider = MaybeCallableProvider & { + [K in Exclude>]: MethodOrProperty; + }; +} +declare namespace Cloudflare { + interface Env { + } +} +declare module 'cloudflare:workers' { + export type RpcStub = Rpc.Stub; + export const RpcStub: { + new (value: T): Rpc.Stub; + }; + export abstract class RpcTarget implements Rpc.RpcTargetBranded { + [Rpc.__RPC_TARGET_BRAND]: never; + } + // `protected` fields don't appear in `keyof`s, so can't be accessed over RPC + export abstract class WorkerEntrypoint implements Rpc.WorkerEntrypointBranded { + [Rpc.__WORKER_ENTRYPOINT_BRAND]: never; + protected ctx: ExecutionContext; + protected env: Env; + constructor(ctx: ExecutionContext, env: Env); + fetch?(request: Request): Response | Promise; + tail?(events: TraceItem[]): void | Promise; + trace?(traces: TraceItem[]): void | Promise; + scheduled?(controller: ScheduledController): void | Promise; + queue?(batch: MessageBatch): void | Promise; + test?(controller: TestController): void | Promise; + } + export abstract class DurableObject implements Rpc.DurableObjectBranded { + [Rpc.__DURABLE_OBJECT_BRAND]: never; + protected ctx: DurableObjectState; + protected env: Env; + constructor(ctx: DurableObjectState, env: Env); + fetch?(request: Request): Response | Promise; + alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise; + webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise; + webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise; + webSocketError?(ws: WebSocket, error: unknown): void | Promise; + } + export type WorkflowDurationLabel = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; + export type WorkflowSleepDuration = `${number} ${WorkflowDurationLabel}${'s' | ''}` | number; + export type WorkflowDelayDuration = WorkflowSleepDuration; + export type WorkflowTimeoutDuration = WorkflowSleepDuration; + export type WorkflowRetentionDuration = WorkflowSleepDuration; + export type WorkflowBackoff = 'constant' | 'linear' | 'exponential'; + export type WorkflowStepConfig = { + retries?: { + limit: number; + delay: WorkflowDelayDuration | number; + backoff?: WorkflowBackoff; + }; + timeout?: WorkflowTimeoutDuration | number; + }; + export type WorkflowEvent = { + payload: Readonly; + timestamp: Date; + instanceId: string; + }; + export type WorkflowStepEvent = { + payload: Readonly; + timestamp: Date; + type: string; + }; + export abstract class WorkflowStep { + do>(name: string, callback: () => Promise): Promise; + do>(name: string, config: WorkflowStepConfig, callback: () => Promise): Promise; + sleep: (name: string, duration: WorkflowSleepDuration) => Promise; + sleepUntil: (name: string, timestamp: Date | number) => Promise; + waitForEvent>(name: string, options: { + type: string; + timeout?: WorkflowTimeoutDuration | number; + }): Promise>; + } + export abstract class WorkflowEntrypoint | unknown = unknown> implements Rpc.WorkflowEntrypointBranded { + [Rpc.__WORKFLOW_ENTRYPOINT_BRAND]: never; + protected ctx: ExecutionContext; + protected env: Env; + constructor(ctx: ExecutionContext, env: Env); + run(event: Readonly>, step: WorkflowStep): Promise; + } + export const env: Cloudflare.Env; +} +interface SecretsStoreSecret { + /** + * Get a secret from the Secrets Store, returning a string of the secret value + * if it exists, or throws an error if it does not exist + */ + get(): Promise; +} +declare module "cloudflare:sockets" { + function _connect(address: string | SocketAddress, options?: SocketOptions): Socket; + export { _connect as connect }; +} +declare namespace TailStream { + interface Header { + readonly name: string; + readonly value: string; + } + interface FetchEventInfo { + readonly type: "fetch"; + readonly method: string; + readonly url: string; + readonly cfJson: string; + readonly headers: Header[]; + } + interface JsRpcEventInfo { + readonly type: "jsrpc"; + readonly methodName: string; + } + interface ScheduledEventInfo { + readonly type: "scheduled"; + readonly scheduledTime: Date; + readonly cron: string; + } + interface AlarmEventInfo { + readonly type: "alarm"; + readonly scheduledTime: Date; + } + interface QueueEventInfo { + readonly type: "queue"; + readonly queueName: string; + readonly batchSize: number; + } + interface EmailEventInfo { + readonly type: "email"; + readonly mailFrom: string; + readonly rcptTo: string; + readonly rawSize: number; + } + interface TraceEventInfo { + readonly type: "trace"; + readonly traces: (string | null)[]; + } + interface HibernatableWebSocketEventInfoMessage { + readonly type: "message"; + } + interface HibernatableWebSocketEventInfoError { + readonly type: "error"; + } + interface HibernatableWebSocketEventInfoClose { + readonly type: "close"; + readonly code: number; + readonly wasClean: boolean; + } + interface HibernatableWebSocketEventInfo { + readonly type: "hibernatableWebSocket"; + readonly info: HibernatableWebSocketEventInfoClose | HibernatableWebSocketEventInfoError | HibernatableWebSocketEventInfoMessage; + } + interface Resume { + readonly type: "resume"; + readonly attachment?: any; + } + interface CustomEventInfo { + readonly type: "custom"; + } + interface FetchResponseInfo { + readonly type: "fetch"; + readonly statusCode: number; + } + type EventOutcome = "ok" | "canceled" | "exception" | "unknown" | "killSwitch" | "daemonDown" | "exceededCpu" | "exceededMemory" | "loadShed" | "responseStreamDisconnected" | "scriptNotFound"; + interface ScriptVersion { + readonly id: string; + readonly tag?: string; + readonly message?: string; + } + interface Trigger { + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + } + interface Onset { + readonly type: "onset"; + readonly dispatchNamespace?: string; + readonly entrypoint?: string; + readonly executionModel: string; + readonly scriptName?: string; + readonly scriptTags?: string[]; + readonly scriptVersion?: ScriptVersion; + readonly trigger?: Trigger; + readonly info: FetchEventInfo | JsRpcEventInfo | ScheduledEventInfo | AlarmEventInfo | QueueEventInfo | EmailEventInfo | TraceEventInfo | HibernatableWebSocketEventInfo | Resume | CustomEventInfo; + } + interface Outcome { + readonly type: "outcome"; + readonly outcome: EventOutcome; + readonly cpuTime: number; + readonly wallTime: number; + } + interface Hibernate { + readonly type: "hibernate"; + } + interface SpanOpen { + readonly type: "spanOpen"; + readonly name: string; + readonly info?: FetchEventInfo | JsRpcEventInfo | Attributes; + } + interface SpanClose { + readonly type: "spanClose"; + readonly outcome: EventOutcome; + } + interface DiagnosticChannelEvent { + readonly type: "diagnosticChannel"; + readonly channel: string; + readonly message: any; + } + interface Exception { + readonly type: "exception"; + readonly name: string; + readonly message: string; + readonly stack?: string; + } + interface Log { + readonly type: "log"; + readonly level: "debug" | "error" | "info" | "log" | "warn"; + readonly message: string; + } + interface Return { + readonly type: "return"; + readonly info?: FetchResponseInfo; + } + interface Link { + readonly type: "link"; + readonly label?: string; + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + } + interface Attribute { + readonly name: string; + readonly value: string | string[] | boolean | boolean[] | number | number[] | bigint | bigint[]; + } + interface Attributes { + readonly type: "attributes"; + readonly info: Attribute[]; + } + interface TailEvent { + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + readonly timestamp: Date; + readonly sequence: number; + readonly event: Onset | Outcome | Hibernate | SpanOpen | SpanClose | DiagnosticChannelEvent | Exception | Log | Return | Link | Attributes; + } + type TailEventHandler = (event: TailEvent) => void | Promise; + type TailEventHandlerName = "outcome" | "hibernate" | "spanOpen" | "spanClose" | "diagnosticChannel" | "exception" | "log" | "return" | "link" | "attributes"; + type TailEventHandlerObject = Record; + type TailEventHandlerType = TailEventHandler | TailEventHandlerObject; +} +// Copyright (c) 2022-2023 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +/** + * Data types supported for holding vector metadata. + */ +type VectorizeVectorMetadataValue = string | number | boolean | string[]; +/** + * Additional information to associate with a vector. + */ +type VectorizeVectorMetadata = VectorizeVectorMetadataValue | Record; +type VectorFloatArray = Float32Array | Float64Array; +interface VectorizeError { + code?: number; + error: string; +} +/** + * Comparison logic/operation to use for metadata filtering. + * + * This list is expected to grow as support for more operations are released. + */ +type VectorizeVectorMetadataFilterOp = "$eq" | "$ne"; +/** + * Filter criteria for vector metadata used to limit the retrieved query result set. + */ +type VectorizeVectorMetadataFilter = { + [field: string]: Exclude | null | { + [Op in VectorizeVectorMetadataFilterOp]?: Exclude | null; + }; +}; +/** + * Supported distance metrics for an index. + * Distance metrics determine how other "similar" vectors are determined. + */ +type VectorizeDistanceMetric = "euclidean" | "cosine" | "dot-product"; +/** + * Metadata return levels for a Vectorize query. + * + * Default to "none". + * + * @property all Full metadata for the vector return set, including all fields (including those un-indexed) without truncation. This is a more expensive retrieval, as it requires additional fetching & reading of un-indexed data. + * @property indexed Return all metadata fields configured for indexing in the vector return set. This level of retrieval is "free" in that no additional overhead is incurred returning this data. However, note that indexed metadata is subject to truncation (especially for larger strings). + * @property none No indexed metadata will be returned. + */ +type VectorizeMetadataRetrievalLevel = "all" | "indexed" | "none"; +interface VectorizeQueryOptions { + topK?: number; + namespace?: string; + returnValues?: boolean; + returnMetadata?: boolean | VectorizeMetadataRetrievalLevel; + filter?: VectorizeVectorMetadataFilter; +} +/** + * Information about the configuration of an index. + */ +type VectorizeIndexConfig = { + dimensions: number; + metric: VectorizeDistanceMetric; +} | { + preset: string; // keep this generic, as we'll be adding more presets in the future and this is only in a read capacity +}; +/** + * Metadata about an existing index. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link VectorizeIndexInfo} for its post-beta equivalent. + */ +interface VectorizeIndexDetails { + /** The unique ID of the index */ + readonly id: string; + /** The name of the index. */ + name: string; + /** (optional) A human readable description for the index. */ + description?: string; + /** The index configuration, including the dimension size and distance metric. */ + config: VectorizeIndexConfig; + /** The number of records containing vectors within the index. */ + vectorsCount: number; +} +/** + * Metadata about an existing index. + */ +interface VectorizeIndexInfo { + /** The number of records containing vectors within the index. */ + vectorCount: number; + /** Number of dimensions the index has been configured for. */ + dimensions: number; + /** ISO 8601 datetime of the last processed mutation on in the index. All changes before this mutation will be reflected in the index state. */ + processedUpToDatetime: number; + /** UUIDv4 of the last mutation processed by the index. All changes before this mutation will be reflected in the index state. */ + processedUpToMutation: number; +} +/** + * Represents a single vector value set along with its associated metadata. + */ +interface VectorizeVector { + /** The ID for the vector. This can be user-defined, and must be unique. It should uniquely identify the object, and is best set based on the ID of what the vector represents. */ + id: string; + /** The vector values */ + values: VectorFloatArray | number[]; + /** The namespace this vector belongs to. */ + namespace?: string; + /** Metadata associated with the vector. Includes the values of other fields and potentially additional details. */ + metadata?: Record; +} +/** + * Represents a matched vector for a query along with its score and (if specified) the matching vector information. + */ +type VectorizeMatch = Pick, "values"> & Omit & { + /** The score or rank for similarity, when returned as a result */ + score: number; +}; +/** + * A set of matching {@link VectorizeMatch} for a particular query. + */ +interface VectorizeMatches { + matches: VectorizeMatch[]; + count: number; +} +/** + * Results of an operation that performed a mutation on a set of vectors. + * Here, `ids` is a list of vectors that were successfully processed. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link VectorizeAsyncMutation} for its post-beta equivalent. + */ +interface VectorizeVectorMutation { + /* List of ids of vectors that were successfully processed. */ + ids: string[]; + /* Total count of the number of processed vectors. */ + count: number; +} +/** + * Result type indicating a mutation on the Vectorize Index. + * Actual mutations are processed async where the `mutationId` is the unique identifier for the operation. + */ +interface VectorizeAsyncMutation { + /** The unique identifier for the async mutation operation containing the changeset. */ + mutationId: string; +} +/** + * A Vectorize Vector Search Index for querying vectors/embeddings. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link Vectorize} for its new implementation. + */ +declare abstract class VectorizeIndex { + /** + * Get information about the currently bound index. + * @returns A promise that resolves with information about the current index. + */ + public describe(): Promise; + /** + * Use the provided vector to perform a similarity search across the index. + * @param vector Input vector that will be used to drive the similarity search. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise; + /** + * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown. + * @param vectors List of vectors that will be inserted. + * @returns A promise that resolves with the ids & count of records that were successfully processed. + */ + public insert(vectors: VectorizeVector[]): Promise; + /** + * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values. + * @param vectors List of vectors that will be upserted. + * @returns A promise that resolves with the ids & count of records that were successfully processed. + */ + public upsert(vectors: VectorizeVector[]): Promise; + /** + * Delete a list of vectors with a matching id. + * @param ids List of vector ids that should be deleted. + * @returns A promise that resolves with the ids & count of records that were successfully processed (and thus deleted). + */ + public deleteByIds(ids: string[]): Promise; + /** + * Get a list of vectors with a matching id. + * @param ids List of vector ids that should be returned. + * @returns A promise that resolves with the raw unscored vectors matching the id set. + */ + public getByIds(ids: string[]): Promise; +} +/** + * A Vectorize Vector Search Index for querying vectors/embeddings. + * + * Mutations in this version are async, returning a mutation id. + */ +declare abstract class Vectorize { + /** + * Get information about the currently bound index. + * @returns A promise that resolves with information about the current index. + */ + public describe(): Promise; + /** + * Use the provided vector to perform a similarity search across the index. + * @param vector Input vector that will be used to drive the similarity search. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise; + /** + * Use the provided vector-id to perform a similarity search across the index. + * @param vectorId Id for a vector in the index against which the index should be queried. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public queryById(vectorId: string, options?: VectorizeQueryOptions): Promise; + /** + * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown. + * @param vectors List of vectors that will be inserted. + * @returns A promise that resolves with a unique identifier of a mutation containing the insert changeset. + */ + public insert(vectors: VectorizeVector[]): Promise; + /** + * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values. + * @param vectors List of vectors that will be upserted. + * @returns A promise that resolves with a unique identifier of a mutation containing the upsert changeset. + */ + public upsert(vectors: VectorizeVector[]): Promise; + /** + * Delete a list of vectors with a matching id. + * @param ids List of vector ids that should be deleted. + * @returns A promise that resolves with a unique identifier of a mutation containing the delete changeset. + */ + public deleteByIds(ids: string[]): Promise; + /** + * Get a list of vectors with a matching id. + * @param ids List of vector ids that should be returned. + * @returns A promise that resolves with the raw unscored vectors matching the id set. + */ + public getByIds(ids: string[]): Promise; +} +/** + * The interface for "version_metadata" binding + * providing metadata about the Worker Version using this binding. + */ +type WorkerVersionMetadata = { + /** The ID of the Worker Version using this binding */ + id: string; + /** The tag of the Worker Version using this binding */ + tag: string; + /** The timestamp of when the Worker Version was uploaded */ + timestamp: string; +}; +interface DynamicDispatchLimits { + /** + * Limit CPU time in milliseconds. + */ + cpuMs?: number; + /** + * Limit number of subrequests. + */ + subRequests?: number; +} +interface DynamicDispatchOptions { + /** + * Limit resources of invoked Worker script. + */ + limits?: DynamicDispatchLimits; + /** + * Arguments for outbound Worker script, if configured. + */ + outbound?: { + [key: string]: any; + }; +} +interface DispatchNamespace { + /** + * @param name Name of the Worker script. + * @param args Arguments to Worker script. + * @param options Options for Dynamic Dispatch invocation. + * @returns A Fetcher object that allows you to send requests to the Worker script. + * @throws If the Worker script does not exist in this dispatch namespace, an error will be thrown. + */ + get(name: string, args?: { + [key: string]: any; + }, options?: DynamicDispatchOptions): Fetcher; +} +declare module 'cloudflare:workflows' { + /** + * NonRetryableError allows for a user to throw a fatal error + * that makes a Workflow instance fail immediately without triggering a retry + */ + export class NonRetryableError extends Error { + public constructor(message: string, name?: string); + } +} +declare abstract class Workflow { + /** + * Get a handle to an existing instance of the Workflow. + * @param id Id for the instance of this Workflow + * @returns A promise that resolves with a handle for the Instance + */ + public get(id: string): Promise; + /** + * Create a new instance and return a handle to it. If a provided id exists, an error will be thrown. + * @param options Options when creating an instance including id and params + * @returns A promise that resolves with a handle for the Instance + */ + public create(options?: WorkflowInstanceCreateOptions): Promise; + /** + * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached. + * @param batch List of Options when creating an instance including name and params + * @returns A promise that resolves with a list of handles for the created instances. + */ + public createBatch(batch: WorkflowInstanceCreateOptions[]): Promise; +} +type WorkflowDurationLabel = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; +type WorkflowSleepDuration = `${number} ${WorkflowDurationLabel}${'s' | ''}` | number; +type WorkflowRetentionDuration = WorkflowSleepDuration; +interface WorkflowInstanceCreateOptions { + /** + * An id for your Workflow instance. Must be unique within the Workflow. + */ + id?: string; + /** + * The event payload the Workflow instance is triggered with + */ + params?: PARAMS; + /** + * The retention policy for Workflow instance. + * Defaults to the maximum retention period available for the owner's account. + */ + retention?: { + successRetention?: WorkflowRetentionDuration; + errorRetention?: WorkflowRetentionDuration; + }; +} +type InstanceStatus = { + status: 'queued' // means that instance is waiting to be started (see concurrency limits) + | 'running' | 'paused' | 'errored' | 'terminated' // user terminated the instance while it was running + | 'complete' | 'waiting' // instance is hibernating and waiting for sleep or event to finish + | 'waitingForPause' // instance is finishing the current work to pause + | 'unknown'; + error?: string; + output?: object; +}; +interface WorkflowError { + code?: number; + message: string; +} +declare abstract class WorkflowInstance { + public id: string; + /** + * Pause the instance. + */ + public pause(): Promise; + /** + * Resume the instance. If it is already running, an error will be thrown. + */ + public resume(): Promise; + /** + * Terminate the instance. If it is errored, terminated or complete, an error will be thrown. + */ + public terminate(): Promise; + /** + * Restart the instance. + */ + public restart(): Promise; + /** + * Returns the current status of the instance. + */ + public status(): Promise; + /** + * Send an event to this instance. + */ + public sendEvent({ type, payload, }: { + type: string; + payload: unknown; + }): Promise; +} diff --git a/apps/daily-ai-trigger/wrangler.jsonc b/apps/daily-ai-trigger/wrangler.jsonc new file mode 100644 index 0000000..c3e6ecd --- /dev/null +++ b/apps/daily-ai-trigger/wrangler.jsonc @@ -0,0 +1,53 @@ +/** + * For more details on how to configure Wrangler, refer to: + * https://developers.cloudflare.com/workers/wrangler/configuration/ + */ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "daily-ai-trigger", + "main": "src/index.ts", + "compatibility_date": "2025-03-10", + "observability": { + "enabled": true + }, + "triggers": { + "crons": [ + "0 1 * * *", + "0 2 * * *" + ] + } + /** + * Smart Placement + * Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement + */ + // "placement": { "mode": "smart" }, + + /** + * Bindings + * Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including + * databases, object storage, AI inference, real-time communication and more. + * https://developers.cloudflare.com/workers/runtime-apis/bindings/ + */ + + /** + * Environment Variables + * https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables + */ + // "vars": { "MY_VARIABLE": "production_value" }, + /** + * Note: Use secrets to store sensitive data. + * https://developers.cloudflare.com/workers/configuration/secrets/ + */ + + /** + * Static Assets + * https://developers.cloudflare.com/workers/static-assets/binding/ + */ + // "assets": { "directory": "./public/", "binding": "ASSETS" }, + + /** + * Service Bindings (communicate between multiple Workers) + * https://developers.cloudflare.com/workers/wrangler/configuration/#service-bindings + */ + // "services": [{ "binding": "MY_SERVICE", "service": "my-service" }] +} diff --git a/apps/mcp-server/.gitignore b/apps/mcp-server/.gitignore new file mode 100644 index 0000000..2754949 --- /dev/null +++ b/apps/mcp-server/.gitignore @@ -0,0 +1,136 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# vitepress build output +**/.vitepress/dist + +# vitepress cache directory +**/.vitepress/cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* \ No newline at end of file diff --git a/apps/mcp-server/.vscode/launch.json b/apps/mcp-server/.vscode/launch.json new file mode 100644 index 0000000..83ab764 --- /dev/null +++ b/apps/mcp-server/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug MCP Server", + "skipFiles": ["/**"], + "outFiles": ["${workspaceFolder}/dist/**/*.js"], + "runtimeExecutable": "npx", + "runtimeArgs": [ + "-y", + "@modelcontextprotocol/inspector", + "node", + "dist/index.js" + ], + "console": "integratedTerminal", + "preLaunchTask": "npm: watch", + "serverReadyAction": { + "action": "openExternally", + "pattern": "running at (https?://\\S+)", + "uriFormat": "%s?timeout=60000" + } + } + ] +} diff --git a/apps/mcp-server/.vscode/mcp.json b/apps/mcp-server/.vscode/mcp.json new file mode 100644 index 0000000..a50f51e --- /dev/null +++ b/apps/mcp-server/.vscode/mcp.json @@ -0,0 +1,14 @@ +{ + "servers": { + "tianji-mcp-server": { + "type": "stdio", + "command": "node", + "args": ["${workspaceFolder}/dist/index.js"], + "env": { + "TIANJI_BASE_URL": "https://tianji.example.com", + "TIANJI_API_KEY": "", + "TIANJI_WORKSPACE_ID": "" + } + } + } +} diff --git a/apps/mcp-server/.vscode/tasks.json b/apps/mcp-server/.vscode/tasks.json new file mode 100644 index 0000000..95d8256 --- /dev/null +++ b/apps/mcp-server/.vscode/tasks.json @@ -0,0 +1,16 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": ["$tsc-watch"], + "isBackground": true, + "label": "npm: watch", + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/apps/mcp-server/README.md b/apps/mcp-server/README.md new file mode 100644 index 0000000..f429add --- /dev/null +++ b/apps/mcp-server/README.md @@ -0,0 +1,52 @@ +# Tianji MCP Server + +A server based on Model Context Protocol (MCP) that provides tools for interacting with the Tianji platform. + +## Introduction + +Tianji MCP Server is a bridge connecting AI assistants with the Tianji platform, exposing survey functionality from the Tianji platform to AI assistants through the MCP protocol. This server provides the following core features: + +- Query survey results +- Get detailed survey information +- Get list of all surveys in workspace + +## Install + +```json +{ + "mcpServers": { + "tianji": { + "type": "stdio", + "command": "npx", + "args": [ + "-y", + "tianji-mcp-server" + ], + "env": { + "TIANJI_BASE_URL": "https://tianji.example.com", + "TIANJI_API_KEY": "", + "TIANJI_WORKSPACE_ID": "" + } + } + } +} +``` + +## Environment Variables + +Before using, you need to set the following environment variables: + +```bash +# Tianji Platform API Base URL +TIANJI_BASE_URL=https://tianji.example.com + +# Tianji Platform API Key +TIANJI_API_KEY=your_api_key_here + +# Tianji Platform Workspace ID +TIANJI_WORKSPACE_ID=your_workspace_id_here +``` + +## License + +[MIT](LICENSE) diff --git a/apps/mcp-server/package.json b/apps/mcp-server/package.json new file mode 100644 index 0000000..c036d2e --- /dev/null +++ b/apps/mcp-server/package.json @@ -0,0 +1,43 @@ +{ + "name": "tianji-mcp-server", + "version": "0.2.0", + "type": "module", + "bin": { + "tianji-mcp-server": "dist/index.js" + }, + "scripts": { + "build": "tsc && shx chmod +x dist/index.js", + "watch": "tsc --watch", + "start": "node ./dist/index.js", + "prepare": "pnpm build", + "inspector": "mcp-inspector node ./dist/index.js" + }, + "files": [ + "dist" + ], + "keywords": [ + "tianji", + "modelcontextprotocol", + "tianji-mcp", + "tianji-mcp-server", + "mcp" + ], + "repository": { + "type": "git", + "url": "" + }, + "author": "moonrailgun ", + "license": "MIT", + "description": "Tianji MCP Server", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.18.0", + "tianji-client-sdk": "workspace:^", + "zod": "^3.24.2" + }, + "devDependencies": { + "@modelcontextprotocol/inspector": "^0.16.7", + "@types/node": "^22.13.10", + "shx": "^0.3.4", + "typescript": "^5.8.2" + } +} diff --git a/apps/mcp-server/src/index.ts b/apps/mcp-server/src/index.ts new file mode 100644 index 0000000..494018e --- /dev/null +++ b/apps/mcp-server/src/index.ts @@ -0,0 +1,17 @@ +#!/usr/bin/env node +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; + +import { createServer } from './server.js'; + +async function main() { + const server: McpServer = createServer(); + const transport = new StdioServerTransport(); + await server.connect(transport); + // console.debug("Tianji MCP Server running on stdio"); +} + +main().catch((error) => { + console.error('Fatal error in main():', error); + process.exit(1); +}); diff --git a/apps/mcp-server/src/server.ts b/apps/mcp-server/src/server.ts new file mode 100644 index 0000000..a065c8b --- /dev/null +++ b/apps/mcp-server/src/server.ts @@ -0,0 +1,492 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { initOpenapiSDK, openApiClient } from 'tianji-client-sdk'; +import { z } from 'zod'; +import { fetchAllPaginatedData } from './utils/pagination.js'; + +export function createServer(): McpServer { + const server = new McpServer({ + name: 'Tianji MCP Server', + version: '0.1.0', + }); + + const tianjiBaseUrl = process.env.TIANJI_BASE_URL; + if (!tianjiBaseUrl) { + throw new Error('TIANJI_BASE_URL is required'); + } + const apiKey = process.env.TIANJI_API_KEY; + if (!apiKey) { + throw new Error('TIANJI_API_KEY is required'); + } + const workspaceId = process.env.TIANJI_WORKSPACE_ID; + if (!workspaceId) { + throw new Error('TIANJI_WORKSPACE_ID is required'); + } + + const now = new Date().toISOString(); + + initOpenapiSDK(tianjiBaseUrl, { + apiKey: apiKey, + }); + + // Add service to query survey results + server.tool( + 'tianji_get_survey_results', + 'Query survey questionnaire result data', + { + workspaceId: z + .string() + .default(workspaceId) + .describe('Tianji workspace ID'), + surveyId: z.string().describe('Survey questionnaire ID'), + limit: z + .number() + .default(20) + .describe('Limit the number of records returned'), + cursor: z.string().optional().describe('Pagination cursor'), + startAt: z + .string() + .describe( + `Start time, ISO format, example: 2023-10-01T00:00:00Z, current time is: ${now}` + ), + endAt: z + .string() + .describe( + `End time, ISO format, example: 2023-10-31T23:59:59Z, current time is: ${now}` + ), + filter: z.string().optional().describe('Filter conditions'), + }, + async ( + params, + extra + ): Promise<{ + content: Array<{ + type: 'text'; + text: string; + }>; + }> => { + try { + const results = await fetchAllPaginatedData( + (queryParams: { cursor?: string }) => + openApiClient.SurveyService.surveyResultList({ + workspaceId: params.workspaceId, + surveyId: params.surveyId, + limit: params.limit, + cursor: queryParams.cursor, + startAt: new Date(params.startAt).valueOf(), + endAt: new Date(params.endAt).valueOf(), + filter: params.filter, + }), + { cursor: params.cursor } + ); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(results), + }, + ], + }; + } catch (error) { + console.error('Failed to get survey results:', error); + return { + content: [ + { + type: 'text', + text: 'Error: Failed to get survey results', + }, + ], + }; + } + } + ); + + // Add service to get individual survey information + server.tool( + 'tianji_get_survey_info', + 'Get basic information of a specified survey questionnaire', + { + workspaceId: z + .string() + .default(workspaceId) + .describe('Tianji workspace ID'), + surveyId: z.string().describe('Survey questionnaire ID'), + }, + async ( + params, + extra + ): Promise<{ + content: Array<{ + type: 'text'; + text: string; + }>; + }> => { + try { + const surveyInfo = await openApiClient.SurveyService.surveyGet({ + workspaceId: params.workspaceId, + surveyId: params.surveyId, + }); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(surveyInfo), + }, + ], + }; + } catch (error) { + console.error('Failed to get survey information:', error); + return { + content: [ + { + type: 'text', + text: 'Error: Failed to get survey info', + }, + ], + }; + } + } + ); + + // Add service to get survey list + server.tool( + 'tianji_get_all_survey_list', + 'Get all survey list in the workspace', + { + workspaceId: z + .string() + .describe(`Tianji workspace ID, default: ${workspaceId}`), + }, + async ( + params, + extra + ): Promise<{ + content: Array<{ + type: 'text'; + text: string; + }>; + }> => { + try { + const surveys = await openApiClient.SurveyService.surveyAll({ + workspaceId: params.workspaceId, + }); + + return { + content: surveys.map((survey) => ({ + type: 'text', + text: JSON.stringify({ + id: survey.id, + name: survey.name, + }), + })), + }; + } catch (error) { + console.error('Failed to get survey list:', error); + return { + content: [ + { + type: 'text', + text: 'Error: Failed to get survey list', + }, + ], + }; + } + } + ); + + // Add service to get website list + server.tool( + 'tianji_get_website_list', + 'Get all websites in the workspace', + { + workspaceId: z + .string() + .default(workspaceId) + .describe('Tianji workspace ID'), + }, + async ( + params, + extra + ): Promise<{ + content: Array<{ + type: 'text'; + text: string; + }>; + }> => { + try { + const websites = await openApiClient.WebsiteService.websiteAll({ + workspaceId: params.workspaceId, + }); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(websites), + }, + ], + }; + } catch (error) { + console.error('Failed to get website list:', error); + return { + content: [ + { + type: 'text', + text: 'Error: Failed to get website list', + }, + ], + }; + } + } + ); + + // Add service to get website info + server.tool( + 'tianji_get_website_info', + 'Get detailed information about a specific website', + { + workspaceId: z + .string() + .default(workspaceId) + .describe('Tianji workspace ID'), + websiteId: z.string().describe('Website ID'), + }, + async ( + params, + extra + ): Promise<{ + content: Array<{ + type: 'text'; + text: string; + }>; + }> => { + try { + const websiteInfo = await openApiClient.WebsiteService.websiteInfo({ + workspaceId: params.workspaceId, + websiteId: params.websiteId, + }); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(websiteInfo), + }, + ], + }; + } catch (error) { + console.error('Failed to get website information:', error); + return { + content: [ + { + type: 'text', + text: 'Error: Failed to get website info', + }, + ], + }; + } + } + ); + + // Add service to get website statistics + server.tool( + 'tianji_get_website_stats', + 'Get statistics data for a website', + { + workspaceId: z + .string() + .default(workspaceId) + .describe('Tianji workspace ID'), + websiteId: z.string().describe('Website ID'), + startAt: z + .string() + .describe( + `Start time, ISO format, example: 2023-10-01T00:00:00Z, current time is: ${now}` + ), + endAt: z + .string() + .describe( + `End time, ISO format, example: 2023-10-31T23:59:59Z, current time is: ${now}` + ), + unit: z.string().optional().describe('Time unit for statistics grouping'), + }, + async ( + params, + extra + ): Promise<{ + content: Array<{ + type: 'text'; + text: string; + }>; + }> => { + try { + const stats = await openApiClient.WebsiteService.websiteStats({ + workspaceId: params.workspaceId, + websiteId: params.websiteId, + startAt: new Date(params.startAt).valueOf(), + endAt: new Date(params.endAt).valueOf(), + unit: params.unit, + }); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(stats), + }, + ], + }; + } catch (error) { + console.error('Failed to get website statistics:', error); + return { + content: [ + { + type: 'text', + text: 'Error: Failed to get website statistics', + }, + ], + }; + } + } + ); + + // Add service to get website pageviews + server.tool( + 'tianji_get_website_pageviews', + 'Get pageview and session data for a website', + { + workspaceId: z + .string() + .default(workspaceId) + .describe('Tianji workspace ID'), + websiteId: z.string().describe('Website ID'), + startAt: z + .string() + .describe( + `Start time, ISO format, example: 2023-10-01T00:00:00Z, current time is: ${now}` + ), + endAt: z + .string() + .describe( + `End time, ISO format, example: 2023-10-31T23:59:59Z, current time is: ${now}` + ), + unit: z.string().optional().describe('Time unit for data grouping'), + }, + async ( + params, + extra + ): Promise<{ + content: Array<{ + type: 'text'; + text: string; + }>; + }> => { + try { + const pageviewData = + await openApiClient.WebsiteService.websitePageviews({ + workspaceId: params.workspaceId, + websiteId: params.websiteId, + startAt: new Date(params.startAt).valueOf(), + endAt: new Date(params.endAt).valueOf(), + unit: params.unit, + }); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(pageviewData), + }, + ], + }; + } catch (error) { + console.error('Failed to get website pageviews:', error); + return { + content: [ + { + type: 'text', + text: 'Error: Failed to get website pageview data', + }, + ], + }; + } + } + ); + + // Add service to get website metrics + server.tool( + 'tianji_get_website_metrics', + 'Get specific metrics for a website (url, referrer, browser, os, etc.)', + { + workspaceId: z + .string() + .default(workspaceId) + .describe('Tianji workspace ID'), + websiteId: z.string().describe('Website ID'), + type: z + .enum([ + 'url', + 'language', + 'referrer', + 'title', + 'browser', + 'os', + 'device', + 'country', + 'event', + ]) + .describe('Type of metrics to retrieve'), + startAt: z + .string() + .describe( + `Start time, ISO format, example: 2023-10-01T00:00:00Z, current time is: ${now}` + ), + endAt: z + .string() + .describe( + `End time, ISO format, example: 2023-10-31T23:59:59Z, current time is: ${now}` + ), + }, + async ( + params, + extra + ): Promise<{ + content: Array<{ + type: 'text'; + text: string; + }>; + }> => { + try { + const metricsData = await openApiClient.WebsiteService.websiteMetrics({ + workspaceId: params.workspaceId, + websiteId: params.websiteId, + type: params.type, + startAt: new Date(params.startAt).valueOf(), + endAt: new Date(params.endAt).valueOf(), + }); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(metricsData), + }, + ], + }; + } catch (error) { + console.error('Failed to get website metrics:', error); + return { + content: [ + { + type: 'text', + text: 'Error: Failed to get website metrics data', + }, + ], + }; + } + } + ); + + return server; +} diff --git a/apps/mcp-server/src/utils/pagination.ts b/apps/mcp-server/src/utils/pagination.ts new file mode 100644 index 0000000..d4bab72 --- /dev/null +++ b/apps/mcp-server/src/utils/pagination.ts @@ -0,0 +1,51 @@ +/** + * Generic paginated data fetcher + * Automatically fetches all pages of data using a cursor-based pagination approach + */ + +export interface PaginationParams { + cursor?: string; + [key: string]: any; +} + +export interface PaginatedResponse { + items: T[]; + nextCursor?: string; + [key: string]: any; +} + +export async function fetchAllPaginatedData( + fetchFunction: (params: P) => Promise>, + params: P +): Promise<{ total: number; items: T[] }> { + const allResults: T[] = []; + let currentCursor = params.cursor; + let hasMore = true; + + while (hasMore) { + // Create a new params object with the current cursor + const currentParams = { + ...params, + cursor: currentCursor, + } as P; + + const results = await fetchFunction(currentParams); + + // Add current page results to the total results array + if (results.items && Array.isArray(results.items)) { + allResults.push(...results.items); + } + + // Check if there are more pages + if (results.nextCursor) { + currentCursor = results.nextCursor; + } else { + hasMore = false; + } + } + + return { + total: allResults.length, + items: allResults, + }; +} diff --git a/apps/mcp-server/tsconfig.json b/apps/mcp-server/tsconfig.json new file mode 100644 index 0000000..faca33f --- /dev/null +++ b/apps/mcp-server/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..bcc6990 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3' +services: + tianji: + image: moonrailgun/tianji + build: + context: ./ + dockerfile: ./Dockerfile + ports: + - "12345:12345" + environment: + DATABASE_URL: postgresql://tianji:tianji@postgres:5432/tianji + JWT_SECRET: replace-me-with-a-random-string + ALLOW_REGISTER: "false" + ALLOW_OPENAPI: "true" + PUBLIC_URL: "" # for example: https://app.tianji.dev + depends_on: + - postgres + restart: always + postgres: + image: postgres:15.4-alpine + environment: + POSTGRES_DB: tianji + POSTGRES_USER: tianji + POSTGRES_PASSWORD: tianji + volumes: + - tianji-db-data:/var/lib/postgresql/data + restart: always + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 5 +volumes: + tianji-db-data: diff --git a/docker/clickhouse/docker-compose.yml b/docker/clickhouse/docker-compose.yml new file mode 100644 index 0000000..7d13b0f --- /dev/null +++ b/docker/clickhouse/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3.8' + +services: + clickhouse: + image: clickhouse/clickhouse-server:latest + container_name: tianji-clickhouse + ports: + - "127.0.0.1:8123:8123" # HTTP endpoint + # - "9000:9000" # Native endpoint + - "127.0.0.1:9004:9004" # Mysql endpoint + environment: + - CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 + - CLICKHOUSE_DB=tianji + - CLICKHOUSE_USER=tianji + - CLICKHOUSE_PASSWORD=tianji + # volumes: + # - clickhouse_data:/var/lib/clickhouse + # - ./config.xml:/etc/clickhouse-server/config.xml + restart: unless-stopped + ch-ui: + image: ghcr.io/caioricciuti/ch-ui:latest + restart: always + ports: + - "5521:5521" + environment: + VITE_CLICKHOUSE_URL: "http://127.0.0.1:8123" + VITE_CLICKHOUSE_USER: "tianji" + VITE_CLICKHOUSE_PASS: "tianji" + +# volumes: +# clickhouse_data: +# driver: local +# clickhouse_config: +# driver: local diff --git a/docker/k8s/helm/.gitignore b/docker/k8s/helm/.gitignore new file mode 100644 index 0000000..ebf1d3d --- /dev/null +++ b/docker/k8s/helm/.gitignore @@ -0,0 +1 @@ +charts diff --git a/docker/k8s/helm/.helmignore b/docker/k8s/helm/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/docker/k8s/helm/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/docker/k8s/helm/Chart.lock b/docker/k8s/helm/Chart.lock new file mode 100644 index 0000000..597c1d0 --- /dev/null +++ b/docker/k8s/helm/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: postgresql + repository: https://charts.bitnami.com/bitnami + version: 15.4.2 +digest: sha256:57308af2821726255fe0c52374260c2ea98f9091c03d82cf82b9c6d499e6f214 +generated: "2024-06-09T14:04:36.881437+08:00" diff --git a/docker/k8s/helm/Chart.yaml b/docker/k8s/helm/Chart.yaml new file mode 100644 index 0000000..47e9776 --- /dev/null +++ b/docker/k8s/helm/Chart.yaml @@ -0,0 +1,42 @@ +apiVersion: v2 +name: tianji +description: All-in-One Insight Hub +icon: https://tianji.dev/img/logo.svg +maintainers: + - name: moonrailgun + email: moonrailgun@gmail.com +keywords: + - tianji + - uptime + - umami + - server status + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.17 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.11.2" + +sources: + - https://github.com/msgbyte/tianji + +dependencies: + - name: postgresql + version: 15.4.2 + repository: "https://charts.bitnami.com/bitnami" + condition: postgresql.enabled diff --git a/docker/k8s/helm/templates/NOTES.txt b/docker/k8s/helm/templates/NOTES.txt new file mode 100644 index 0000000..ea2e056 --- /dev/null +++ b/docker/k8s/helm/templates/NOTES.txt @@ -0,0 +1,26 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tianji.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tianji.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tianji.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tianji.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} + +2. Tianji's default account: +Username: admin +Password: admin diff --git a/docker/k8s/helm/templates/_helpers.tpl b/docker/k8s/helm/templates/_helpers.tpl new file mode 100644 index 0000000..a7a3a80 --- /dev/null +++ b/docker/k8s/helm/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "tianji.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "tianji.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "tianji.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "tianji.labels" -}} +helm.sh/chart: {{ include "tianji.chart" . }} +{{ include "tianji.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "tianji.selectorLabels" -}} +app.kubernetes.io/name: {{ include "tianji.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "tianji.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "tianji.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/docker/k8s/helm/templates/configmap.yaml b/docker/k8s/helm/templates/configmap.yaml new file mode 100644 index 0000000..c7c206d --- /dev/null +++ b/docker/k8s/helm/templates/configmap.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "tianji.fullname" . }} + labels: + {{- include "tianji.labels" . | nindent 4 }} +data: + DATABASE_URL: postgresql://{{ .Values.postgresql.auth.username }}:{{ .Values.postgresql.auth.password }}@{{ .Release.Name }}-postgresql:5432/{{ .Values.postgresql.auth.database }} + {{/* + Rather than maintain a comprehensive ConfigMap, we map all sub-keys of the "env" value here. + This allows for more flexibility and less Chart churn as Drone evolves. + */}} +{{- range $envKey, $envVal := .Values.env }} + {{ $envKey | upper }}: {{ $envVal | quote }} +{{- end }} diff --git a/docker/k8s/helm/templates/deployment.yaml b/docker/k8s/helm/templates/deployment.yaml new file mode 100644 index 0000000..2488ac7 --- /dev/null +++ b/docker/k8s/helm/templates/deployment.yaml @@ -0,0 +1,71 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "tianji.fullname" . }} + labels: + {{- include "tianji.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "tianji.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "tianji.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "tianji.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + envFrom: + - configMapRef: + name: {{ include "tianji.fullname" . }} + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/docker/k8s/helm/templates/hpa.yaml b/docker/k8s/helm/templates/hpa.yaml new file mode 100644 index 0000000..a09258a --- /dev/null +++ b/docker/k8s/helm/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "tianji.fullname" . }} + labels: + {{- include "tianji.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "tianji.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/docker/k8s/helm/templates/ingress.yaml b/docker/k8s/helm/templates/ingress.yaml new file mode 100644 index 0000000..16d274d --- /dev/null +++ b/docker/k8s/helm/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "tianji.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "tianji.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/docker/k8s/helm/templates/service.yaml b/docker/k8s/helm/templates/service.yaml new file mode 100644 index 0000000..4133920 --- /dev/null +++ b/docker/k8s/helm/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "tianji.fullname" . }} + labels: + {{- include "tianji.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "tianji.selectorLabels" . | nindent 4 }} diff --git a/docker/k8s/helm/templates/serviceaccount.yaml b/docker/k8s/helm/templates/serviceaccount.yaml new file mode 100644 index 0000000..c9b70aa --- /dev/null +++ b/docker/k8s/helm/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "tianji.serviceAccountName" . }} + labels: + {{- include "tianji.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/docker/k8s/helm/templates/tests/test-connection.yaml b/docker/k8s/helm/templates/tests/test-connection.yaml new file mode 100644 index 0000000..2c0cb5d --- /dev/null +++ b/docker/k8s/helm/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "tianji.fullname" . }}-test-connection" + labels: + {{- include "tianji.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "tianji.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/docker/k8s/helm/values.yaml b/docker/k8s/helm/values.yaml new file mode 100644 index 0000000..20c1c13 --- /dev/null +++ b/docker/k8s/helm/values.yaml @@ -0,0 +1,121 @@ +# Default values for tianji. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: moonrailgun/tianji + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +postgresql: + enabled: true + auth: + username: "tianji" + password: "tianji" + database: "tianji" + postgresPassword: "tianji" + +service: + type: ClusterIP + port: 12345 + +env: + # DATABASE_URL: postgresql://tianji:tianji@postgresql:5432/tianji + JWT_SECRET: replace-me-with-a-random-string + ALLOW_REGISTER: "false" + ALLOW_OPENAPI: "true" + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +livenessProbe: + httpGet: + path: / + port: http +readinessProbe: + httpGet: + path: / + port: http + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/docker/k8s/reporter-daemonset.yaml b/docker/k8s/reporter-daemonset.yaml new file mode 100644 index 0000000..6eeb0fb --- /dev/null +++ b/docker/k8s/reporter-daemonset.yaml @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: tianji-reporter + labels: + app: tianji-reporter +spec: + selector: + matchLabels: + app: tianji-reporter + template: + metadata: + labels: + app: tianji-reporter + spec: + containers: + - name: reporter + image: moonrailgun/tianji:latest + args: + - /usr/local/bin/tianji-reporter + - --url=$(TIANJI_SERVER_URL) + - --workspace=$(TIANJI_WORKSPACE_ID) + - --name=$(NODE_NAME) + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: TIANJI_SERVER_URL + value: "http://tianji.example.com" + - name: TIANJI_WORKSPACE_ID + value: "" + volumeMounts: + - name: proc + mountPath: /proc + readOnly: true + - name: sys + mountPath: /sys + readOnly: true + - name: rootfs + mountPath: /rootfs + readOnly: true + securityContext: + privileged: true + hostNetwork: true + hostPID: true + volumes: + - name: proc + hostPath: + path: /proc + - name: sys + hostPath: + path: /sys + - name: rootfs + hostPath: + path: / diff --git a/docker/k8s/sealos/tianji.yaml b/docker/k8s/sealos/tianji.yaml new file mode 100644 index 0000000..679063b --- /dev/null +++ b/docker/k8s/sealos/tianji.yaml @@ -0,0 +1,322 @@ +apiVersion: app.sealos.io/v1 +kind: Template +metadata: + name: tianji +spec: + title: 'Tianji' + url: 'https://tianji.dev/' + gitRepo: 'https://github.com/msgbyte/tianji' + author: 'moonrailgun' + description: 'Tianji: Insight into everything, Website Analytics + Uptime Monitor + Server Status. not only another GA alternatives' + readme: 'https://raw.githubusercontent.com/msgbyte/tianji/master/README.md' + icon: 'https://tianji.dev/img/logo.svg' + templateType: inline + defaults: + app_host: + # number or string.. + type: string + value: tianji-${{ random(8) }} + app_name: + type: string + value: tianji-${{ random(8) }} + inputs: + JWT_SECRET: + description: 'replace me with a random string' + type: string + default: 'replace-me-with-a-random-string' + required: true + ALLOW_REGISTER: + description: 'whether allow register account' + type: string + default: "false" + required: false + ALLOW_OPENAPI: + description: 'whether allow open openapi' + type: string + default: "true" + required: false + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ${{ defaults.app_name }} + annotations: + originImageName: moonrailgun/tianji + deploy.cloud.sealos.io/minReplicas: '1' + deploy.cloud.sealos.io/maxReplicas: '1' + labels: + cloud.sealos.io/app-deploy-manager: ${{ defaults.app_name }} + app: ${{ defaults.app_name }} +spec: + replicas: 1 + revisionHistoryLimit: 1 + selector: + matchLabels: + app: ${{ defaults.app_name }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 0 + template: + metadata: + labels: + app: ${{ defaults.app_name }} + spec: + containers: + - name: ${{ defaults.app_name }} + image: moonrailgun/tianji + env: + - name: PG_PASSWORD + valueFrom: + secretKeyRef: + name: ${{ defaults.app_name }}-pg-conn-credential + key: password + - name: DATABASE_URL + value: postgresql://postgres:$(PG_PASSWORD)@${{ defaults.app_name }}-pg-postgresql.${{ SEALOS_NAMESPACE }}.svc:5432/tianji + - name: JWT_SECRET + value: ${{ inputs.JWT_SECRET }} + - name: ALLOW_REGISTER + value: ${{ inputs.ALLOW_REGISTER }} + - name: ALLOW_OPENAPI + value: ${{ inputs.ALLOW_OPENAPI }} + resources: + requests: + cpu: 100m + memory: 102Mi + limits: + cpu: 200m + memory: 512Mi + command: [] + args: [] + ports: + - containerPort: 12345 + imagePullPolicy: Always + volumeMounts: [] + volumes: [] +--- +apiVersion: v1 +kind: Service +metadata: + name: ${{ defaults.app_name }} + labels: + cloud.sealos.io/app-deploy-manager: ${{ defaults.app_name }} +spec: + ports: + - port: 12345 + selector: + app: ${{ defaults.app_name }} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ${{ defaults.app_name }} + labels: + cloud.sealos.io/app-deploy-manager: ${{ defaults.app_name }} + cloud.sealos.io/app-deploy-manager-domain: ${{ defaults.app_host }} + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/proxy-body-size: 32m + nginx.ingress.kubernetes.io/server-snippet: | + client_header_buffer_size 64k; + large_client_header_buffers 4 128k; + nginx.ingress.kubernetes.io/ssl-redirect: 'false' + nginx.ingress.kubernetes.io/backend-protocol: HTTP + nginx.ingress.kubernetes.io/rewrite-target: /$2 + nginx.ingress.kubernetes.io/client-body-buffer-size: 64k + nginx.ingress.kubernetes.io/proxy-buffer-size: 64k + nginx.ingress.kubernetes.io/proxy-send-timeout: '300' + nginx.ingress.kubernetes.io/proxy-read-timeout: '300' + nginx.ingress.kubernetes.io/configuration-snippet: | + if ($request_uri ~* \.(js|css|gif|jpe?g|png)) { + expires 30d; + add_header Cache-Control "public"; + } +spec: + rules: + - host: ${{ defaults.app_host }}.${{ SEALOS_CLOUD_DOMAIN }} + http: + paths: + - pathType: Prefix + path: /()(.*) + backend: + service: + name: ${{ defaults.app_name }} + port: + number: 12345 + tls: + - hosts: + - ${{ defaults.app_host }}.${{ SEALOS_CLOUD_DOMAIN }} + secretName: ${{ SEALOS_CERT_SECRET_NAME }} +--- +apiVersion: apps.kubeblocks.io/v1alpha1 +kind: Cluster +metadata: + finalizers: + - cluster.kubeblocks.io/finalizer + labels: + clusterdefinition.kubeblocks.io/name: postgresql + clusterversion.kubeblocks.io/name: postgresql-14.8.0 + sealos-db-provider-cr: ${{ defaults.app_name }}-pg + annotations: {} + name: ${{ defaults.app_name }}-pg +spec: + affinity: + nodeLabels: {} + podAntiAffinity: Preferred + tenancy: SharedNode + topologyKeys: [] + clusterDefinitionRef: postgresql + clusterVersionRef: postgresql-14.8.0 + componentSpecs: + - componentDefRef: postgresql + monitor: true + name: postgresql + replicas: 1 + resources: + limits: + cpu: 1000m + memory: 1024Mi + requests: + cpu: 100m + memory: 102Mi + serviceAccountName: ${{ defaults.app_name }}-pg + switchPolicy: + type: Noop + volumeClaimTemplates: + - name: data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi + storageClassName: openebs-backup + terminationPolicy: Delete + tolerations: [] + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + sealos-db-provider-cr: ${{ defaults.app_name }}-pg + app.kubernetes.io/instance: ${{ defaults.app_name }}-pg + app.kubernetes.io/managed-by: kbcli + name: ${{ defaults.app_name }}-pg + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + sealos-db-provider-cr: ${{ defaults.app_name }}-pg + app.kubernetes.io/instance: ${{ defaults.app_name }}-pg + app.kubernetes.io/managed-by: kbcli + name: ${{ defaults.app_name }}-pg +rules: + - apiGroups: + - '' + resources: + - events + verbs: + - create + - apiGroups: + - '' + resources: + - configmaps + verbs: + - create + - get + - list + - patch + - update + - watch + - delete + - apiGroups: + - '' + resources: + - endpoints + verbs: + - create + - get + - list + - patch + - update + - watch + - delete + - apiGroups: + - '' + resources: + - pods + verbs: + - get + - list + - patch + - update + - watch + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + sealos-db-provider-cr: ${{ defaults.app_name }}-pg + app.kubernetes.io/instance: ${{ defaults.app_name }}-pg + app.kubernetes.io/managed-by: kbcli + name: ${{ defaults.app_name }}-pg +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: ${{ defaults.app_name }}-pg +subjects: + - kind: ServiceAccount + name: ${{ defaults.app_name }}-pg + namespace: ${{ SEALOS_NAMESPACE }} + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: ${{ defaults.app_name }}-init +spec: + completions: 1 + template: + spec: + containers: + - name: pgsql-init + image: senzing/postgresql-client:latest + env: + - name: PG_PASSWORD + valueFrom: + secretKeyRef: + name: ${{ defaults.app_name }}-pg-conn-credential + key: password + - name: DATABASE_URL + value: postgresql://postgres:$(PG_PASSWORD)@${{ defaults.app_name }}-pg-postgresql.${{ SEALOS_NAMESPACE }}.svc:5432 + command: + - /bin/sh + - -c + - | + until psql ${DATABASE_URL} -c 'CREATE DATABASE tianji;' &>/dev/null; do sleep 1; done + restartPolicy: Never + backoffLimit: 0 + ttlSecondsAfterFinished: 300 + +--- +apiVersion: app.sealos.io/v1 +kind: App +metadata: + name: ${{ defaults.app_name }} + labels: + cloud.sealos.io/app-deploy-manager: ${{ defaults.app_name }} +spec: + data: + url: https://${{ defaults.app_host }}.${{ SEALOS_CLOUD_DOMAIN }} + displayType: normal + icon: "https://tianji.dev/img/logo.svg" + menuData: + nameColor: text-black + name: ${{ defaults.app_name }} + type: iframe diff --git a/example/expo/.gitignore b/example/expo/.gitignore new file mode 100644 index 0000000..c9d575d --- /dev/null +++ b/example/expo/.gitignore @@ -0,0 +1,38 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ +expo-env.d.ts + +# Native +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo + +app-example diff --git a/example/expo/.npmrc b/example/expo/.npmrc new file mode 100644 index 0000000..d67f374 --- /dev/null +++ b/example/expo/.npmrc @@ -0,0 +1 @@ +node-linker=hoisted diff --git a/example/expo/README.md b/example/expo/README.md new file mode 100644 index 0000000..cd4feb8 --- /dev/null +++ b/example/expo/README.md @@ -0,0 +1,50 @@ +# Welcome to your Expo app 👋 + +This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). + +## Get started + +1. Install dependencies + + ```bash + npm install + ``` + +2. Start the app + + ```bash + npx expo start + ``` + +In the output, you'll find options to open the app in a + +- [development build](https://docs.expo.dev/develop/development-builds/introduction/) +- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) +- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) +- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo + +You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). + +## Get a fresh project + +When you're ready, run: + +```bash +npm run reset-project +``` + +This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing. + +## Learn more + +To learn more about developing your project with Expo, look at the following resources: + +- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). +- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. + +## Join the community + +Join our community of developers creating universal apps. + +- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. +- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. diff --git a/example/expo/app.json b/example/expo/app.json new file mode 100644 index 0000000..aca8297 --- /dev/null +++ b/example/expo/app.json @@ -0,0 +1,41 @@ +{ + "expo": { + "name": "tianji-expo-example", + "slug": "tianji-expo-example", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/images/icon.png", + "scheme": "myapp", + "userInterfaceStyle": "automatic", + "newArchEnabled": true, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/images/adaptive-icon.png", + "backgroundColor": "#ffffff" + } + }, + "web": { + "bundler": "metro", + "output": "static", + "favicon": "./assets/images/favicon.png" + }, + "plugins": [ + "expo-router", + [ + "expo-splash-screen", + { + "image": "./assets/images/splash-icon.png", + "imageWidth": 200, + "resizeMode": "contain", + "backgroundColor": "#ffffff" + } + ] + ], + "experiments": { + "typedRoutes": true + } + } +} diff --git a/example/expo/app/(tabs)/_layout.tsx b/example/expo/app/(tabs)/_layout.tsx new file mode 100644 index 0000000..851c47c --- /dev/null +++ b/example/expo/app/(tabs)/_layout.tsx @@ -0,0 +1,59 @@ +import { Tabs } from 'expo-router'; +import React from 'react'; +import { Platform } from 'react-native'; + +import { HapticTab } from '@/components/HapticTab'; +import { IconSymbol } from '@/components/ui/IconSymbol'; +import TabBarBackground from '@/components/ui/TabBarBackground'; +import { Colors } from '@/constants/Colors'; +import { useColorScheme } from '@/hooks/useColorScheme'; + +export default function TabLayout() { + const colorScheme = useColorScheme(); + + return ( + + ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), + }} + /> + + ); +} diff --git a/example/expo/app/(tabs)/application-test.tsx b/example/expo/app/(tabs)/application-test.tsx new file mode 100644 index 0000000..090de57 --- /dev/null +++ b/example/expo/app/(tabs)/application-test.tsx @@ -0,0 +1,362 @@ +import { useState } from 'react'; +import { StyleSheet, TextInput, Alert } from 'react-native'; +import { ScrollView } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { + reportApplicationEvent, + identifyApplicationUser, + ApplicationTrackingOptions, +} from 'tianji-react-native'; + +import { Collapsible } from '@/components/Collapsible'; +import { ThemedText } from '@/components/ThemedText'; +import { ThemedView } from '@/components/ThemedView'; +import { IconSymbol } from '@/components/ui/IconSymbol'; +import { useColorScheme } from '@/hooks/useColorScheme'; +import { Colors } from '@/constants/Colors'; +import { VStack } from '@/components/ui/vstack'; +import { + Toast, + ToastDescription, + ToastTitle, + useToast, +} from '@/components/ui/toast'; +import { + FormControl, + FormControlLabel, + FormControlLabelText, +} from '@/components/ui/form-control'; +import { Input, InputField } from '@/components/ui/input'; +import { Button, ButtonText } from '@/components/ui/button'; +import { HStack } from '@/components/ui/hstack'; +import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'; + +interface EventResult { + success: boolean; + message: string; + timestamp: string; +} + +export default function ApplicationTestScreen() { + const [serverUrl, setServerUrl] = useState('http://localhost:12345'); + const [applicationId, setApplicationId] = useState( + 'cm8aepds100qrge0xiv982zj4' + ); + const [eventName, setEventName] = useState('test_event'); + const [eventData, setEventData] = useState('{"value": 1, "action": "click"}'); + const [results, setResults] = useState([]); + const [loading, setLoading] = useState(false); + const toast = useToast(); + const insets = useSafeAreaInsets(); + const buttonTabbarHeight = useBottomTabBarHeight(); + + const colorScheme = useColorScheme() ?? 'light'; + + const sendApplicationEvent = async () => { + if (!applicationId) { + Alert.alert('Error', 'Please enter application ID'); + return; + } + + try { + setLoading(true); + const timestamp = new Date().toISOString(); + + // Parse event data from JSON string + let parsedEventData; + try { + parsedEventData = JSON.parse(eventData); + } catch (e) { + Alert.alert('Error', 'Invalid JSON format in event data'); + setLoading(false); + return; + } + + // Create tracking options + const trackingOptions: ApplicationTrackingOptions = { + serverUrl, + applicationId, + }; + + // Use the client SDK to report the event + const result = await reportApplicationEvent( + trackingOptions, + eventName, + parsedEventData + ); + + toast.show({ + placement: 'top', + duration: 3000, + render: () => { + return ( + + Success! + Event has been sent. + + ); + }, + }); + + setResults((prev) => [ + { + success: true, + message: `Event sent successfully: ${result}`, + timestamp, + }, + ...prev, + ]); + } catch (error) { + setResults((prev) => [ + { + success: false, + message: `Exception: ${error instanceof Error ? error.message : String(error)}`, + timestamp: new Date().toISOString(), + }, + ...prev, + ]); + } finally { + setLoading(false); + } + }; + + const sendIdentifyEvent = async () => { + if (!applicationId) { + Alert.alert('Error', 'Please enter application ID'); + return; + } + + try { + setLoading(true); + const timestamp = new Date().toISOString(); + + const userData = { + userId: 'user123', + email: 'test@example.com', + name: 'Test User', + plan: 'premium', + signupDate: new Date().toISOString(), + }; + + // Create tracking options + const trackingOptions: ApplicationTrackingOptions = { + serverUrl, + applicationId, + }; + + // Use the client SDK to identify the user + const result = await identifyApplicationUser(trackingOptions, userData); + + toast.show({ + placement: 'top', + duration: 3000, + render: () => { + return ( + + Success! + Event has been sent. + + ); + }, + }); + + setResults((prev) => [ + { + success: true, + message: `User identification event sent successfully: ${result}`, + timestamp, + }, + ...prev, + ]); + } catch (error) { + setResults((prev) => [ + { + success: false, + message: `Exception: ${error instanceof Error ? error.message : String(error)}`, + timestamp: new Date().toISOString(), + }, + ...prev, + ]); + } finally { + setLoading(false); + } + }; + + const clearResults = () => { + setResults([]); + }; + + return ( + + + Application Event Test + + + + + + + + Server Address + + + + + + + + + Application ID + + + + + + + + + + + + + Event Name + + + + + + + + + Event Data (JSON) + + + + + + + + + + + + Send user identification event, including user ID, email and other + information + + + + + + + + + Event Sending Records + + + + {results.length === 0 ? ( + No records + ) : ( + results.map((result, index) => ( + + {result.timestamp} + + {result.message} + + + )) + )} + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingHorizontal: 16, + }, + titleContainer: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginBottom: 20, + marginTop: 12, + }, + formSection: { + marginBottom: 16, + }, + jsonInput: { + borderWidth: 1, + borderColor: '#ccc', + borderRadius: 8, + padding: 12, + minHeight: 100, + fontFamily: 'SpaceMono', + fontSize: 14, + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.1, + shadowRadius: 2, + elevation: 2, + }, + resultItem: { + padding: 16, + borderWidth: 1, + borderRadius: 12, + marginBottom: 12, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 3, + elevation: 3, + }, +}); diff --git a/example/expo/app/(tabs)/explore.tsx b/example/expo/app/(tabs)/explore.tsx new file mode 100644 index 0000000..06e70c4 --- /dev/null +++ b/example/expo/app/(tabs)/explore.tsx @@ -0,0 +1,109 @@ +import { StyleSheet, Image, Platform } from 'react-native'; + +import { Collapsible } from '@/components/Collapsible'; +import { ExternalLink } from '@/components/ExternalLink'; +import ParallaxScrollView from '@/components/ParallaxScrollView'; +import { ThemedText } from '@/components/ThemedText'; +import { ThemedView } from '@/components/ThemedView'; +import { IconSymbol } from '@/components/ui/IconSymbol'; + +export default function TabTwoScreen() { + return ( + + }> + + Explore + + This app includes example code to help you get started. + + + This app has two screens:{' '} + app/(tabs)/index.tsx and{' '} + app/(tabs)/explore.tsx + + + The layout file in app/(tabs)/_layout.tsx{' '} + sets up the tab navigator. + + + Learn more + + + + + You can open this project on Android, iOS, and the web. To open the web version, press{' '} + w in the terminal running this project. + + + + + For static images, you can use the @2x and{' '} + @3x suffixes to provide files for + different screen densities + + + + Learn more + + + + + Open app/_layout.tsx to see how to load{' '} + + custom fonts such as this one. + + + + Learn more + + + + + This template has light and dark mode support. The{' '} + useColorScheme() hook lets you inspect + what the user's current color scheme is, and so you can adjust UI colors accordingly. + + + Learn more + + + + + This template includes an example of an animated component. The{' '} + components/HelloWave.tsx component uses + the powerful react-native-reanimated{' '} + library to create a waving hand animation. + + {Platform.select({ + ios: ( + + The components/ParallaxScrollView.tsx{' '} + component provides a parallax effect for the header image. + + ), + })} + + + ); +} + +const styles = StyleSheet.create({ + headerImage: { + color: '#808080', + bottom: -90, + left: -35, + position: 'absolute', + }, + titleContainer: { + flexDirection: 'row', + gap: 8, + }, +}); diff --git a/example/expo/app/(tabs)/index.tsx b/example/expo/app/(tabs)/index.tsx new file mode 100644 index 0000000..886b079 --- /dev/null +++ b/example/expo/app/(tabs)/index.tsx @@ -0,0 +1,74 @@ +import { Image, StyleSheet, Platform } from 'react-native'; + +import { HelloWave } from '@/components/HelloWave'; +import ParallaxScrollView from '@/components/ParallaxScrollView'; +import { ThemedText } from '@/components/ThemedText'; +import { ThemedView } from '@/components/ThemedView'; + +export default function HomeScreen() { + return ( + + }> + + Welcome! + + + + Step 1: Try it + + Edit app/(tabs)/index.tsx to see changes. + Press{' '} + + {Platform.select({ + ios: 'cmd + d', + android: 'cmd + m', + web: 'F12' + })} + {' '} + to open developer tools. + + + + Step 2: Explore + + Tap the Explore tab to learn more about what's included in this starter app. + + + + Step 3: Get a fresh start + + When you're ready, run{' '} + npm run reset-project to get a fresh{' '} + app directory. This will move the current{' '} + app to{' '} + app-example. + + + + ); +} + +const styles = StyleSheet.create({ + titleContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + stepContainer: { + gap: 8, + marginBottom: 8, + }, + reactLogo: { + height: 178, + width: 290, + bottom: 0, + left: 0, + position: 'absolute', + }, +}); diff --git a/example/expo/app/+not-found.tsx b/example/expo/app/+not-found.tsx new file mode 100644 index 0000000..63ba38d --- /dev/null +++ b/example/expo/app/+not-found.tsx @@ -0,0 +1,32 @@ +import { Link, Stack } from 'expo-router'; +import { StyleSheet } from 'react-native'; +import React from 'react'; +import { ThemedText } from '@/components/ThemedText'; +import { ThemedView } from '@/components/ThemedView'; + +export default function NotFoundScreen() { + return ( + <> + + + This screen doesn't exist. + + Go to home screen! + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + padding: 20, + }, + link: { + marginTop: 15, + paddingVertical: 15, + }, +}); diff --git a/example/expo/app/_layout.tsx b/example/expo/app/_layout.tsx new file mode 100644 index 0000000..486ee49 --- /dev/null +++ b/example/expo/app/_layout.tsx @@ -0,0 +1,54 @@ +import { + DarkTheme, + DefaultTheme, + ThemeProvider, +} from '@react-navigation/native'; +import { useFonts } from 'expo-font'; +import { Stack, useGlobalSearchParams, usePathname } from 'expo-router'; +import * as SplashScreen from 'expo-splash-screen'; +import { StatusBar } from 'expo-status-bar'; +import { useEffect } from 'react'; +import { useColorScheme } from '@/hooks/useColorScheme'; +import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider'; +import { updateCurrentApplicationScreen } from 'tianji-react-native'; + +import 'react-native-reanimated'; +import '../global.css'; + +// Prevent the splash screen from auto-hiding before asset loading is complete. +SplashScreen.preventAutoHideAsync(); + +export default function RootLayout() { + const colorScheme = useColorScheme(); + const [loaded] = useFonts({ + SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), + }); + const pathname = usePathname(); + const params = useGlobalSearchParams(); + + useEffect(() => { + if (loaded) { + SplashScreen.hideAsync(); + } + }, [loaded]); + + useEffect(() => { + updateCurrentApplicationScreen(pathname, params); + }, [pathname, params]); + + if (!loaded) { + return null; + } + + return ( + + + + + + + + + + ); +} diff --git a/example/expo/assets/fonts/SpaceMono-Regular.ttf b/example/expo/assets/fonts/SpaceMono-Regular.ttf new file mode 100755 index 0000000..28d7ff7 Binary files /dev/null and b/example/expo/assets/fonts/SpaceMono-Regular.ttf differ diff --git a/example/expo/assets/images/adaptive-icon.png b/example/expo/assets/images/adaptive-icon.png new file mode 100644 index 0000000..03d6f6b Binary files /dev/null and b/example/expo/assets/images/adaptive-icon.png differ diff --git a/example/expo/assets/images/favicon.png b/example/expo/assets/images/favicon.png new file mode 100644 index 0000000..e75f697 Binary files /dev/null and b/example/expo/assets/images/favicon.png differ diff --git a/example/expo/assets/images/icon.png b/example/expo/assets/images/icon.png new file mode 100644 index 0000000..a0b1526 Binary files /dev/null and b/example/expo/assets/images/icon.png differ diff --git a/example/expo/assets/images/partial-react-logo.png b/example/expo/assets/images/partial-react-logo.png new file mode 100644 index 0000000..66fd957 Binary files /dev/null and b/example/expo/assets/images/partial-react-logo.png differ diff --git a/example/expo/assets/images/react-logo.png b/example/expo/assets/images/react-logo.png new file mode 100644 index 0000000..9d72a9f Binary files /dev/null and b/example/expo/assets/images/react-logo.png differ diff --git a/example/expo/assets/images/react-logo@2x.png b/example/expo/assets/images/react-logo@2x.png new file mode 100644 index 0000000..2229b13 Binary files /dev/null and b/example/expo/assets/images/react-logo@2x.png differ diff --git a/example/expo/assets/images/react-logo@3x.png b/example/expo/assets/images/react-logo@3x.png new file mode 100644 index 0000000..a99b203 Binary files /dev/null and b/example/expo/assets/images/react-logo@3x.png differ diff --git a/example/expo/assets/images/splash-icon.png b/example/expo/assets/images/splash-icon.png new file mode 100644 index 0000000..03d6f6b Binary files /dev/null and b/example/expo/assets/images/splash-icon.png differ diff --git a/example/expo/babel.config.js b/example/expo/babel.config.js new file mode 100644 index 0000000..f3c649b --- /dev/null +++ b/example/expo/babel.config.js @@ -0,0 +1,9 @@ +module.exports = function (api) { + api.cache(true); + return { + presets: [ + ["babel-preset-expo", { jsxImportSource: "nativewind" }], + "nativewind/babel", + ], + }; +}; diff --git a/example/expo/components/Collapsible.tsx b/example/expo/components/Collapsible.tsx new file mode 100644 index 0000000..c1aebd6 --- /dev/null +++ b/example/expo/components/Collapsible.tsx @@ -0,0 +1,79 @@ +import { PropsWithChildren, useState } from 'react'; +import { StyleSheet, TouchableOpacity, Animated, Easing } from 'react-native'; + +import { ThemedText } from '@/components/ThemedText'; +import { ThemedView } from '@/components/ThemedView'; +import { IconSymbol } from '@/components/ui/IconSymbol'; +import { Colors } from '@/constants/Colors'; +import { useColorScheme } from '@/hooks/useColorScheme'; + +export function Collapsible({ + children, + title, +}: PropsWithChildren & { title: string }) { + const [isOpen, setIsOpen] = useState(false); + const [animation] = useState(new Animated.Value(0)); + const theme = useColorScheme() ?? 'light'; + + const toggleOpen = () => { + setIsOpen(!isOpen); + Animated.timing(animation, { + toValue: isOpen ? 0 : 1, + duration: 300, + easing: Easing.bezier(0.4, 0, 0.2, 1), + useNativeDriver: true, + }).start(); + }; + + const rotateInterpolate = animation.interpolate({ + inputRange: [0, 1], + outputRange: ['0deg', '90deg'], + }); + + return ( + + + + + + + {title} + + {isOpen && {children}} + + ); +} + +const styles = StyleSheet.create({ + container: { + marginBottom: 16, + borderRadius: 12, + overflow: 'hidden', + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.05, + shadowRadius: 2, + elevation: 1, + padding: 12, + }, + heading: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + paddingVertical: 8, + }, + content: { + marginTop: 8, + marginLeft: 26, + paddingBottom: 8, + }, +}); diff --git a/example/expo/components/ExternalLink.tsx b/example/expo/components/ExternalLink.tsx new file mode 100644 index 0000000..8f05675 --- /dev/null +++ b/example/expo/components/ExternalLink.tsx @@ -0,0 +1,24 @@ +import { Link } from 'expo-router'; +import { openBrowserAsync } from 'expo-web-browser'; +import { type ComponentProps } from 'react'; +import { Platform } from 'react-native'; + +type Props = Omit, 'href'> & { href: string }; + +export function ExternalLink({ href, ...rest }: Props) { + return ( + { + if (Platform.OS !== 'web') { + // Prevent the default behavior of linking to the default browser on native. + event.preventDefault(); + // Open the link in an in-app browser. + await openBrowserAsync(href); + } + }} + /> + ); +} diff --git a/example/expo/components/HapticTab.tsx b/example/expo/components/HapticTab.tsx new file mode 100644 index 0000000..6d106ba --- /dev/null +++ b/example/expo/components/HapticTab.tsx @@ -0,0 +1,19 @@ +import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs'; +import { PlatformPressable } from '@react-navigation/elements'; +import * as Haptics from 'expo-haptics'; +import React from 'react'; + +export function HapticTab(props: BottomTabBarButtonProps) { + return ( + { + if (process.env.EXPO_OS === 'ios') { + // Add a soft haptic feedback when pressing down on the tabs. + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + } + props.onPressIn?.(ev); + }} + /> + ); +} diff --git a/example/expo/components/HelloWave.tsx b/example/expo/components/HelloWave.tsx new file mode 100644 index 0000000..9b4bc31 --- /dev/null +++ b/example/expo/components/HelloWave.tsx @@ -0,0 +1,40 @@ +import { useEffect } from 'react'; +import { StyleSheet } from 'react-native'; +import Animated, { + useSharedValue, + useAnimatedStyle, + withTiming, + withRepeat, + withSequence, +} from 'react-native-reanimated'; + +import { ThemedText } from '@/components/ThemedText'; + +export function HelloWave() { + const rotationAnimation = useSharedValue(0); + + useEffect(() => { + rotationAnimation.value = withRepeat( + withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })), + 4 // Run the animation 4 times + ); + }, []); + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ rotate: `${rotationAnimation.value}deg` }], + })); + + return ( + + 👋 + + ); +} + +const styles = StyleSheet.create({ + text: { + fontSize: 28, + lineHeight: 32, + marginTop: -6, + }, +}); diff --git a/example/expo/components/ParallaxScrollView.tsx b/example/expo/components/ParallaxScrollView.tsx new file mode 100644 index 0000000..5df1d75 --- /dev/null +++ b/example/expo/components/ParallaxScrollView.tsx @@ -0,0 +1,82 @@ +import type { PropsWithChildren, ReactElement } from 'react'; +import { StyleSheet } from 'react-native'; +import Animated, { + interpolate, + useAnimatedRef, + useAnimatedStyle, + useScrollViewOffset, +} from 'react-native-reanimated'; + +import { ThemedView } from '@/components/ThemedView'; +import { useBottomTabOverflow } from '@/components/ui/TabBarBackground'; +import { useColorScheme } from '@/hooks/useColorScheme'; + +const HEADER_HEIGHT = 250; + +type Props = PropsWithChildren<{ + headerImage: ReactElement; + headerBackgroundColor: { dark: string; light: string }; +}>; + +export default function ParallaxScrollView({ + children, + headerImage, + headerBackgroundColor, +}: Props) { + const colorScheme = useColorScheme() ?? 'light'; + const scrollRef = useAnimatedRef(); + const scrollOffset = useScrollViewOffset(scrollRef); + const bottom = useBottomTabOverflow(); + const headerAnimatedStyle = useAnimatedStyle(() => { + return { + transform: [ + { + translateY: interpolate( + scrollOffset.value, + [-HEADER_HEIGHT, 0, HEADER_HEIGHT], + [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75] + ), + }, + { + scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]), + }, + ], + }; + }); + + return ( + + + + {headerImage} + + {children} + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + header: { + height: HEADER_HEIGHT, + overflow: 'hidden', + }, + content: { + flex: 1, + padding: 32, + gap: 16, + overflow: 'hidden', + }, +}); diff --git a/example/expo/components/ThemedText.tsx b/example/expo/components/ThemedText.tsx new file mode 100644 index 0000000..c0e1a78 --- /dev/null +++ b/example/expo/components/ThemedText.tsx @@ -0,0 +1,60 @@ +import { Text, type TextProps, StyleSheet } from 'react-native'; + +import { useThemeColor } from '@/hooks/useThemeColor'; + +export type ThemedTextProps = TextProps & { + lightColor?: string; + darkColor?: string; + type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link'; +}; + +export function ThemedText({ + style, + lightColor, + darkColor, + type = 'default', + ...rest +}: ThemedTextProps) { + const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text'); + + return ( + + ); +} + +const styles = StyleSheet.create({ + default: { + fontSize: 16, + lineHeight: 24, + }, + defaultSemiBold: { + fontSize: 16, + lineHeight: 24, + fontWeight: '600', + }, + title: { + fontSize: 32, + fontWeight: 'bold', + lineHeight: 32, + }, + subtitle: { + fontSize: 20, + fontWeight: 'bold', + }, + link: { + lineHeight: 30, + fontSize: 16, + color: '#0a7ea4', + }, +}); diff --git a/example/expo/components/ThemedView.tsx b/example/expo/components/ThemedView.tsx new file mode 100644 index 0000000..681974c --- /dev/null +++ b/example/expo/components/ThemedView.tsx @@ -0,0 +1,49 @@ +import { View, type ViewProps, StyleSheet } from 'react-native'; + +import { useThemeColor } from '@/hooks/useThemeColor'; + +export type ThemedViewProps = ViewProps & { + lightColor?: string; + darkColor?: string; + variant?: 'default' | 'card'; + className?: string; +}; + +export function ThemedView({ + style, + lightColor, + darkColor, + variant = 'default', + ...otherProps +}: ThemedViewProps) { + const backgroundColor = useThemeColor( + { light: lightColor, dark: darkColor }, + variant === 'card' ? 'cardBackground' : 'background' + ); + const borderColor = useThemeColor({}, 'border'); + + return ( + + ); +} + +const styles = StyleSheet.create({ + card: { + borderRadius: 12, + borderWidth: 1, + padding: 16, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.05, + shadowRadius: 3, + elevation: 2, + }, +}); diff --git a/example/expo/components/__tests__/ThemedText-test.tsx b/example/expo/components/__tests__/ThemedText-test.tsx new file mode 100644 index 0000000..1ac3225 --- /dev/null +++ b/example/expo/components/__tests__/ThemedText-test.tsx @@ -0,0 +1,10 @@ +import * as React from 'react'; +import renderer from 'react-test-renderer'; + +import { ThemedText } from '../ThemedText'; + +it(`renders correctly`, () => { + const tree = renderer.create(Snapshot test!).toJSON(); + + expect(tree).toMatchSnapshot(); +}); diff --git a/example/expo/components/__tests__/__snapshots__/ThemedText-test.tsx.snap b/example/expo/components/__tests__/__snapshots__/ThemedText-test.tsx.snap new file mode 100644 index 0000000..b68e53e --- /dev/null +++ b/example/expo/components/__tests__/__snapshots__/ThemedText-test.tsx.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + + Snapshot test! + +`; diff --git a/example/expo/components/ui/IconSymbol.ios.tsx b/example/expo/components/ui/IconSymbol.ios.tsx new file mode 100644 index 0000000..9177f4d --- /dev/null +++ b/example/expo/components/ui/IconSymbol.ios.tsx @@ -0,0 +1,32 @@ +import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols'; +import { StyleProp, ViewStyle } from 'react-native'; + +export function IconSymbol({ + name, + size = 24, + color, + style, + weight = 'regular', +}: { + name: SymbolViewProps['name']; + size?: number; + color: string; + style?: StyleProp; + weight?: SymbolWeight; +}) { + return ( + + ); +} diff --git a/example/expo/components/ui/IconSymbol.tsx b/example/expo/components/ui/IconSymbol.tsx new file mode 100644 index 0000000..f1fabd4 --- /dev/null +++ b/example/expo/components/ui/IconSymbol.tsx @@ -0,0 +1,43 @@ +// This file is a fallback for using MaterialIcons on Android and web. + +import MaterialIcons from '@expo/vector-icons/MaterialIcons'; +import { SymbolWeight } from 'expo-symbols'; +import React from 'react'; +import { OpaqueColorValue, StyleProp, ViewStyle } from 'react-native'; + +// Add your SFSymbol to MaterialIcons mappings here. +const MAPPING = { + // See MaterialIcons here: https://icons.expo.fyi + // See SF Symbols in the SF Symbols app on Mac. + 'house.fill': 'home', + 'paperplane.fill': 'send', + 'chevron.left.forwardslash.chevron.right': 'code', + 'chevron.right': 'chevron-right', +} as Partial< + Record< + import('expo-symbols').SymbolViewProps['name'], + React.ComponentProps['name'] + > +>; + +export type IconSymbolName = keyof typeof MAPPING; + +/** + * An icon component that uses native SFSymbols on iOS, and MaterialIcons on Android and web. This ensures a consistent look across platforms, and optimal resource usage. + * + * Icon `name`s are based on SFSymbols and require manual mapping to MaterialIcons. + */ +export function IconSymbol({ + name, + size = 24, + color, + style, +}: { + name: IconSymbolName; + size?: number; + color: string | OpaqueColorValue; + style?: StyleProp; + weight?: SymbolWeight; +}) { + return ; +} diff --git a/example/expo/components/ui/TabBarBackground.ios.tsx b/example/expo/components/ui/TabBarBackground.ios.tsx new file mode 100644 index 0000000..6668e78 --- /dev/null +++ b/example/expo/components/ui/TabBarBackground.ios.tsx @@ -0,0 +1,22 @@ +import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'; +import { BlurView } from 'expo-blur'; +import { StyleSheet } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +export default function BlurTabBarBackground() { + return ( + + ); +} + +export function useBottomTabOverflow() { + const tabHeight = useBottomTabBarHeight(); + const { bottom } = useSafeAreaInsets(); + return tabHeight - bottom; +} diff --git a/example/expo/components/ui/TabBarBackground.tsx b/example/expo/components/ui/TabBarBackground.tsx new file mode 100644 index 0000000..70d1c3c --- /dev/null +++ b/example/expo/components/ui/TabBarBackground.tsx @@ -0,0 +1,6 @@ +// This is a shim for web and Android where the tab bar is generally opaque. +export default undefined; + +export function useBottomTabOverflow() { + return 0; +} diff --git a/example/expo/components/ui/button/index.tsx b/example/expo/components/ui/button/index.tsx new file mode 100644 index 0000000..e8a8ac5 --- /dev/null +++ b/example/expo/components/ui/button/index.tsx @@ -0,0 +1,430 @@ +'use client'; +import React from 'react'; +import { createButton } from '@gluestack-ui/button'; +import { tva } from '@gluestack-ui/nativewind-utils/tva'; +import { + withStyleContext, + useStyleContext, +} from '@gluestack-ui/nativewind-utils/withStyleContext'; +import { cssInterop } from 'nativewind'; +import { ActivityIndicator, Pressable, Text, View } from 'react-native'; +import type { VariantProps } from '@gluestack-ui/nativewind-utils'; +import { PrimitiveIcon, UIIcon } from '@gluestack-ui/icon'; + +const SCOPE = 'BUTTON'; + +const Root = withStyleContext(Pressable, SCOPE); + +const UIButton = createButton({ + Root: Root, + Text, + Group: View, + Spinner: ActivityIndicator, + Icon: UIIcon, +}); + +cssInterop(PrimitiveIcon, { + className: { + target: 'style', + nativeStyleToProp: { + height: true, + width: true, + fill: true, + color: 'classNameColor', + stroke: true, + }, + }, +}); + +const buttonStyle = tva({ + base: 'group/button rounded bg-primary-500 flex-row items-center justify-center data-[focus-visible=true]:web:outline-none data-[focus-visible=true]:web:ring-2 data-[disabled=true]:opacity-40 gap-2', + variants: { + action: { + primary: + 'bg-primary-500 data-[hover=true]:bg-primary-600 data-[active=true]:bg-primary-700 border-primary-300 data-[hover=true]:border-primary-400 data-[active=true]:border-primary-500 data-[focus-visible=true]:web:ring-indicator-info', + secondary: + 'bg-secondary-500 border-secondary-300 data-[hover=true]:bg-secondary-600 data-[hover=true]:border-secondary-400 data-[active=true]:bg-secondary-700 data-[active=true]:border-secondary-700 data-[focus-visible=true]:web:ring-indicator-info', + positive: + 'bg-success-500 border-success-300 data-[hover=true]:bg-success-600 data-[hover=true]:border-success-400 data-[active=true]:bg-success-700 data-[active=true]:border-success-500 data-[focus-visible=true]:web:ring-indicator-info', + negative: + 'bg-error-500 border-error-300 data-[hover=true]:bg-error-600 data-[hover=true]:border-error-400 data-[active=true]:bg-error-700 data-[active=true]:border-error-500 data-[focus-visible=true]:web:ring-indicator-info', + default: + 'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', + }, + variant: { + link: 'px-0', + outline: + 'bg-transparent border data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', + solid: '', + }, + + size: { + xs: 'px-3.5 h-8', + sm: 'px-4 h-9', + md: 'px-5 h-10', + lg: 'px-6 h-11', + xl: 'px-7 h-12', + }, + }, + compoundVariants: [ + { + action: 'primary', + variant: 'link', + class: + 'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent', + }, + { + action: 'secondary', + variant: 'link', + class: + 'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent', + }, + { + action: 'positive', + variant: 'link', + class: + 'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent', + }, + { + action: 'negative', + variant: 'link', + class: + 'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent', + }, + { + action: 'primary', + variant: 'outline', + class: + 'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', + }, + { + action: 'secondary', + variant: 'outline', + class: + 'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', + }, + { + action: 'positive', + variant: 'outline', + class: + 'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', + }, + { + action: 'negative', + variant: 'outline', + class: + 'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', + }, + ], +}); + +const buttonTextStyle = tva({ + base: 'text-typography-0 font-semibold web:select-none', + parentVariants: { + action: { + primary: + 'text-primary-600 data-[hover=true]:text-primary-600 data-[active=true]:text-primary-700', + secondary: + 'text-typography-500 data-[hover=true]:text-typography-600 data-[active=true]:text-typography-700', + positive: + 'text-success-600 data-[hover=true]:text-success-600 data-[active=true]:text-success-700', + negative: + 'text-error-600 data-[hover=true]:text-error-600 data-[active=true]:text-error-700', + }, + variant: { + link: 'data-[hover=true]:underline data-[active=true]:underline', + outline: '', + solid: + 'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', + }, + size: { + xs: 'text-xs', + sm: 'text-sm', + md: 'text-base', + lg: 'text-lg', + xl: 'text-xl', + }, + }, + parentCompoundVariants: [ + { + variant: 'solid', + action: 'primary', + class: + 'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', + }, + { + variant: 'solid', + action: 'secondary', + class: + 'text-typography-800 data-[hover=true]:text-typography-800 data-[active=true]:text-typography-800', + }, + { + variant: 'solid', + action: 'positive', + class: + 'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', + }, + { + variant: 'solid', + action: 'negative', + class: + 'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', + }, + { + variant: 'outline', + action: 'primary', + class: + 'text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500', + }, + { + variant: 'outline', + action: 'secondary', + class: + 'text-typography-500 data-[hover=true]:text-primary-600 data-[active=true]:text-typography-700', + }, + { + variant: 'outline', + action: 'positive', + class: + 'text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500', + }, + { + variant: 'outline', + action: 'negative', + class: + 'text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500', + }, + ], +}); + +const buttonIconStyle = tva({ + base: 'fill-none', + parentVariants: { + variant: { + link: 'data-[hover=true]:underline data-[active=true]:underline', + outline: '', + solid: + 'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', + }, + size: { + xs: 'h-3.5 w-3.5', + sm: 'h-4 w-4', + md: 'h-[18px] w-[18px]', + lg: 'h-[18px] w-[18px]', + xl: 'h-5 w-5', + }, + action: { + primary: + 'text-primary-600 data-[hover=true]:text-primary-600 data-[active=true]:text-primary-700', + secondary: + 'text-typography-500 data-[hover=true]:text-typography-600 data-[active=true]:text-typography-700', + positive: + 'text-success-600 data-[hover=true]:text-success-600 data-[active=true]:text-success-700', + + negative: + 'text-error-600 data-[hover=true]:text-error-600 data-[active=true]:text-error-700', + }, + }, + parentCompoundVariants: [ + { + variant: 'solid', + action: 'primary', + class: + 'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', + }, + { + variant: 'solid', + action: 'secondary', + class: + 'text-typography-800 data-[hover=true]:text-typography-800 data-[active=true]:text-typography-800', + }, + { + variant: 'solid', + action: 'positive', + class: + 'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', + }, + { + variant: 'solid', + action: 'negative', + class: + 'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', + }, + ], +}); + +const buttonGroupStyle = tva({ + base: '', + variants: { + space: { + 'xs': 'gap-1', + 'sm': 'gap-2', + 'md': 'gap-3', + 'lg': 'gap-4', + 'xl': 'gap-5', + '2xl': 'gap-6', + '3xl': 'gap-7', + '4xl': 'gap-8', + }, + isAttached: { + true: 'gap-0', + }, + flexDirection: { + 'row': 'flex-row', + 'column': 'flex-col', + 'row-reverse': 'flex-row-reverse', + 'column-reverse': 'flex-col-reverse', + }, + }, +}); + +type IButtonProps = Omit< + React.ComponentPropsWithoutRef, + 'context' +> & + VariantProps & { className?: string }; + +const Button = React.forwardRef< + React.ComponentRef, + IButtonProps +>(function Button( + { className, variant = 'solid', size = 'md', action = 'primary', ...props }, + ref +) { + return ( + + ); +}); + +type IButtonTextProps = React.ComponentPropsWithoutRef & + VariantProps & { className?: string }; + +const ButtonText = React.forwardRef< + React.ComponentRef, + IButtonTextProps +>(function ButtonText({ className, variant, size, action, ...props }, ref) { + const { + variant: parentVariant, + size: parentSize, + action: parentAction, + } = useStyleContext(SCOPE); + + return ( + + ); +}); + +const ButtonSpinner = UIButton.Spinner; + +type IButtonIcon = React.ComponentPropsWithoutRef & + VariantProps & { + className?: string | undefined; + as?: React.ElementType; + height?: number; + width?: number; + }; + +const ButtonIcon = React.forwardRef< + React.ComponentRef, + IButtonIcon +>(function ButtonIcon({ className, size, ...props }, ref) { + const { + variant: parentVariant, + size: parentSize, + action: parentAction, + } = useStyleContext(SCOPE); + + if (typeof size === 'number') { + return ( + + ); + } else if ( + (props.height !== undefined || props.width !== undefined) && + size === undefined + ) { + return ( + + ); + } + return ( + + ); +}); + +type IButtonGroupProps = React.ComponentPropsWithoutRef & + VariantProps; + +const ButtonGroup = React.forwardRef< + React.ComponentRef, + IButtonGroupProps +>(function ButtonGroup( + { + className, + space = 'md', + isAttached = false, + flexDirection = 'column', + ...props + }, + ref +) { + return ( + + ); +}); + +Button.displayName = 'Button'; +ButtonText.displayName = 'ButtonText'; +ButtonSpinner.displayName = 'ButtonSpinner'; +ButtonIcon.displayName = 'ButtonIcon'; +ButtonGroup.displayName = 'ButtonGroup'; + +export { Button, ButtonText, ButtonSpinner, ButtonIcon, ButtonGroup }; diff --git a/example/expo/components/ui/form-control/index.tsx b/example/expo/components/ui/form-control/index.tsx new file mode 100644 index 0000000..f1cfcdf --- /dev/null +++ b/example/expo/components/ui/form-control/index.tsx @@ -0,0 +1,468 @@ +'use client'; +import { Text, View } from 'react-native'; +import React from 'react'; +import { createFormControl } from '@gluestack-ui/form-control'; +import { tva } from '@gluestack-ui/nativewind-utils/tva'; +import { + withStyleContext, + useStyleContext, +} from '@gluestack-ui/nativewind-utils/withStyleContext'; +import { cssInterop } from 'nativewind'; +import type { VariantProps } from '@gluestack-ui/nativewind-utils'; +import { PrimitiveIcon, UIIcon } from '@gluestack-ui/icon'; + +const SCOPE = 'FORM_CONTROL'; + +const formControlStyle = tva({ + base: 'flex flex-col', + variants: { + size: { + sm: '', + md: '', + lg: '', + }, + }, +}); + +const formControlErrorIconStyle = tva({ + base: 'text-error-700 fill-none', + variants: { + size: { + '2xs': 'h-3 w-3', + 'xs': 'h-3.5 w-3.5', + 'sm': 'h-4 w-4', + 'md': 'h-[18px] w-[18px]', + 'lg': 'h-5 w-5', + 'xl': 'h-6 w-6', + }, + }, +}); + +const formControlErrorStyle = tva({ + base: 'flex flex-row justify-start items-center mt-1 gap-1', +}); + +const formControlErrorTextStyle = tva({ + base: 'text-error-700', + variants: { + isTruncated: { + true: 'web:truncate', + }, + bold: { + true: 'font-bold', + }, + underline: { + true: 'underline', + }, + strikeThrough: { + true: 'line-through', + }, + size: { + '2xs': 'text-2xs', + 'xs': 'text-xs', + 'sm': 'text-sm', + 'md': 'text-base', + 'lg': 'text-lg', + 'xl': 'text-xl', + '2xl': 'text-2xl', + '3xl': 'text-3xl', + '4xl': 'text-4xl', + '5xl': 'text-5xl', + '6xl': 'text-6xl', + }, + sub: { + true: 'text-xs', + }, + italic: { + true: 'italic', + }, + highlight: { + true: 'bg-yellow-500', + }, + }, +}); + +const formControlHelperStyle = tva({ + base: 'flex flex-row justify-start items-center mt-1', +}); + +const formControlHelperTextStyle = tva({ + base: 'text-typography-500', + variants: { + isTruncated: { + true: 'web:truncate', + }, + bold: { + true: 'font-bold', + }, + underline: { + true: 'underline', + }, + strikeThrough: { + true: 'line-through', + }, + size: { + '2xs': 'text-2xs', + 'xs': 'text-xs', + 'sm': 'text-xs', + 'md': 'text-sm', + 'lg': 'text-base', + 'xl': 'text-xl', + '2xl': 'text-2xl', + '3xl': 'text-3xl', + '4xl': 'text-4xl', + '5xl': 'text-5xl', + '6xl': 'text-6xl', + }, + sub: { + true: 'text-xs', + }, + italic: { + true: 'italic', + }, + highlight: { + true: 'bg-yellow-500', + }, + }, +}); + +const formControlLabelStyle = tva({ + base: 'flex flex-row justify-start items-center mb-1', +}); + +const formControlLabelTextStyle = tva({ + base: 'font-medium text-typography-900', + variants: { + isTruncated: { + true: 'web:truncate', + }, + bold: { + true: 'font-bold', + }, + underline: { + true: 'underline', + }, + strikeThrough: { + true: 'line-through', + }, + size: { + '2xs': 'text-2xs', + 'xs': 'text-xs', + 'sm': 'text-sm', + 'md': 'text-base', + 'lg': 'text-lg', + 'xl': 'text-xl', + '2xl': 'text-2xl', + '3xl': 'text-3xl', + '4xl': 'text-4xl', + '5xl': 'text-5xl', + '6xl': 'text-6xl', + }, + sub: { + true: 'text-xs', + }, + italic: { + true: 'italic', + }, + highlight: { + true: 'bg-yellow-500', + }, + }, +}); + +const formControlLabelAstrickStyle = tva({ + base: 'font-medium text-typography-900', + variants: { + isTruncated: { + true: 'web:truncate', + }, + bold: { + true: 'font-bold', + }, + underline: { + true: 'underline', + }, + strikeThrough: { + true: 'line-through', + }, + size: { + '2xs': 'text-2xs', + 'xs': 'text-xs', + 'sm': 'text-sm', + 'md': 'text-base', + 'lg': 'text-lg', + 'xl': 'text-xl', + '2xl': 'text-2xl', + '3xl': 'text-3xl', + '4xl': 'text-4xl', + '5xl': 'text-5xl', + '6xl': 'text-6xl', + }, + sub: { + true: 'text-xs', + }, + italic: { + true: 'italic', + }, + highlight: { + true: 'bg-yellow-500', + }, + }, +}); + +type IFormControlLabelAstrickProps = React.ComponentPropsWithoutRef< + typeof Text +> & + VariantProps; + +const FormControlLabelAstrick = React.forwardRef< + React.ComponentRef, + IFormControlLabelAstrickProps +>(function FormControlLabelAstrick({ className, ...props }, ref) { + const { size: parentSize } = useStyleContext(SCOPE); + + return ( + + ); +}); + +export const UIFormControl = createFormControl({ + Root: withStyleContext(View, SCOPE), + Error: View, + ErrorText: Text, + ErrorIcon: UIIcon, + Label: View, + LabelText: Text, + LabelAstrick: FormControlLabelAstrick, + Helper: View, + HelperText: Text, +}); + +cssInterop(PrimitiveIcon, { + className: { + target: 'style', + nativeStyleToProp: { + height: true, + width: true, + fill: true, + color: true, + stroke: true, + }, + }, +}); + +type IFormControlProps = React.ComponentProps & + VariantProps; + +const FormControl = React.forwardRef< + React.ComponentRef, + IFormControlProps +>(function FormControl({ className, size = 'md', ...props }, ref) { + return ( + + ); +}); + +type IFormControlErrorProps = React.ComponentProps & + VariantProps; + +const FormControlError = React.forwardRef< + React.ComponentRef, + IFormControlErrorProps +>(function FormControlError({ className, ...props }, ref) { + return ( + + ); +}); + +type IFormControlErrorTextProps = React.ComponentProps< + typeof UIFormControl.Error.Text +> & + VariantProps; + +const FormControlErrorText = React.forwardRef< + React.ComponentRef, + IFormControlErrorTextProps +>(function FormControlErrorText({ className, size, ...props }, ref) { + const { size: parentSize } = useStyleContext(SCOPE); + return ( + + ); +}); + +type IFormControlErrorIconProps = React.ComponentProps< + typeof UIFormControl.Error.Icon +> & + VariantProps & { + height?: number; + width?: number; + }; + +const FormControlErrorIcon = React.forwardRef< + React.ComponentRef, + IFormControlErrorIconProps +>(function FormControlErrorIcon({ className, size, ...props }, ref) { + const { size: parentSize } = useStyleContext(SCOPE); + + if (typeof size === 'number') { + return ( + + ); + } else if ( + (props.height !== undefined || props.width !== undefined) && + size === undefined + ) { + return ( + + ); + } + return ( + + ); +}); + +type IFormControlLabelProps = React.ComponentProps & + VariantProps; + +const FormControlLabel = React.forwardRef< + React.ComponentRef, + IFormControlLabelProps +>(function FormControlLabel({ className, ...props }, ref) { + return ( + + ); +}); + +type IFormControlLabelTextProps = React.ComponentProps< + typeof UIFormControl.Label.Text +> & + VariantProps; + +const FormControlLabelText = React.forwardRef< + React.ComponentRef, + IFormControlLabelTextProps +>(function FormControlLabelText({ className, size, ...props }, ref) { + const { size: parentSize } = useStyleContext(SCOPE); + + return ( + + ); +}); + +type IFormControlHelperProps = React.ComponentProps< + typeof UIFormControl.Helper +> & + VariantProps; + +const FormControlHelper = React.forwardRef< + React.ComponentRef, + IFormControlHelperProps +>(function FormControlHelper({ className, ...props }, ref) { + return ( + + ); +}); + +type IFormControlHelperTextProps = React.ComponentProps< + typeof UIFormControl.Helper.Text +> & + VariantProps; + +const FormControlHelperText = React.forwardRef< + React.ComponentRef, + IFormControlHelperTextProps +>(function FormControlHelperText({ className, size, ...props }, ref) { + const { size: parentSize } = useStyleContext(SCOPE); + + return ( + + ); +}); + +FormControl.displayName = 'FormControl'; +FormControlError.displayName = 'FormControlError'; +FormControlErrorText.displayName = 'FormControlErrorText'; +FormControlErrorIcon.displayName = 'FormControlErrorIcon'; +FormControlLabel.displayName = 'FormControlLabel'; +FormControlLabelText.displayName = 'FormControlLabelText'; +FormControlLabelAstrick.displayName = 'FormControlLabelAstrick'; +FormControlHelper.displayName = 'FormControlHelper'; +FormControlHelperText.displayName = 'FormControlHelperText'; + +export { + FormControl, + FormControlError, + FormControlErrorText, + FormControlErrorIcon, + FormControlLabel, + FormControlLabelText, + FormControlLabelAstrick, + FormControlHelper, + FormControlHelperText, +}; diff --git a/example/expo/components/ui/gluestack-ui-provider/config.ts b/example/expo/components/ui/gluestack-ui-provider/config.ts new file mode 100644 index 0000000..f388cc6 --- /dev/null +++ b/example/expo/components/ui/gluestack-ui-provider/config.ts @@ -0,0 +1,309 @@ +'use client'; +import { vars } from 'nativewind'; + +export const config = { + light: vars({ + '--color-primary-0': '179 179 179', + '--color-primary-50': '153 153 153', + '--color-primary-100': '128 128 128', + '--color-primary-200': '115 115 115', + '--color-primary-300': '102 102 102', + '--color-primary-400': '82 82 82', + '--color-primary-500': '51 51 51', + '--color-primary-600': '41 41 41', + '--color-primary-700': '31 31 31', + '--color-primary-800': '13 13 13', + '--color-primary-900': '10 10 10', + '--color-primary-950': '8 8 8', + + /* Secondary */ + '--color-secondary-0': '253 253 253', + '--color-secondary-50': '251 251 251', + '--color-secondary-100': '246 246 246', + '--color-secondary-200': '242 242 242', + '--color-secondary-300': '237 237 237', + '--color-secondary-400': '230 230 231', + '--color-secondary-500': '217 217 219', + '--color-secondary-600': '198 199 199', + '--color-secondary-700': '189 189 189', + '--color-secondary-800': '177 177 177', + '--color-secondary-900': '165 164 164', + '--color-secondary-950': '157 157 157', + + /* Tertiary */ + '--color-tertiary-0': '255 250 245', + '--color-tertiary-50': '255 242 229', + '--color-tertiary-100': '255 233 213', + '--color-tertiary-200': '254 209 170', + '--color-tertiary-300': '253 180 116', + '--color-tertiary-400': '251 157 75', + '--color-tertiary-500': '231 129 40', + '--color-tertiary-600': '215 117 31', + '--color-tertiary-700': '180 98 26', + '--color-tertiary-800': '130 73 23', + '--color-tertiary-900': '108 61 19', + '--color-tertiary-950': '84 49 18', + + /* Error */ + '--color-error-0': '254 233 233', + '--color-error-50': '254 226 226', + '--color-error-100': '254 202 202', + '--color-error-200': '252 165 165', + '--color-error-300': '248 113 113', + '--color-error-400': '239 68 68', + '--color-error-500': '230 53 53', + '--color-error-600': '220 38 38', + '--color-error-700': '185 28 28', + '--color-error-800': '153 27 27', + '--color-error-900': '127 29 29', + '--color-error-950': '83 19 19', + + /* Success */ + '--color-success-0': '228 255 244', + '--color-success-50': '202 255 232', + '--color-success-100': '162 241 192', + '--color-success-200': '132 211 162', + '--color-success-300': '102 181 132', + '--color-success-400': '72 151 102', + '--color-success-500': '52 131 82', + '--color-success-600': '42 121 72', + '--color-success-700': '32 111 62', + '--color-success-800': '22 101 52', + '--color-success-900': '20 83 45', + '--color-success-950': '27 50 36', + + /* Warning */ + '--color-warning-0': '255 249 245', + '--color-warning-50': '255 244 236', + '--color-warning-100': '255 231 213', + '--color-warning-200': '254 205 170', + '--color-warning-300': '253 173 116', + '--color-warning-400': '251 149 75', + '--color-warning-500': '231 120 40', + '--color-warning-600': '215 108 31', + '--color-warning-700': '180 90 26', + '--color-warning-800': '130 68 23', + '--color-warning-900': '108 56 19', + '--color-warning-950': '84 45 18', + + /* Info */ + '--color-info-0': '236 248 254', + '--color-info-50': '199 235 252', + '--color-info-100': '162 221 250', + '--color-info-200': '124 207 248', + '--color-info-300': '87 194 246', + '--color-info-400': '50 180 244', + '--color-info-500': '13 166 242', + '--color-info-600': '11 141 205', + '--color-info-700': '9 115 168', + '--color-info-800': '7 90 131', + '--color-info-900': '5 64 93', + '--color-info-950': '3 38 56', + + /* Typography */ + '--color-typography-0': '254 254 255', + '--color-typography-50': '245 245 245', + '--color-typography-100': '229 229 229', + '--color-typography-200': '219 219 220', + '--color-typography-300': '212 212 212', + '--color-typography-400': '163 163 163', + '--color-typography-500': '140 140 140', + '--color-typography-600': '115 115 115', + '--color-typography-700': '82 82 82', + '--color-typography-800': '64 64 64', + '--color-typography-900': '38 38 39', + '--color-typography-950': '23 23 23', + + /* Outline */ + '--color-outline-0': '253 254 254', + '--color-outline-50': '243 243 243', + '--color-outline-100': '230 230 230', + '--color-outline-200': '221 220 219', + '--color-outline-300': '211 211 211', + '--color-outline-400': '165 163 163', + '--color-outline-500': '140 141 141', + '--color-outline-600': '115 116 116', + '--color-outline-700': '83 82 82', + '--color-outline-800': '65 65 65', + '--color-outline-900': '39 38 36', + '--color-outline-950': '26 23 23', + + /* Background */ + '--color-background-0': '255 255 255', + '--color-background-50': '246 246 246', + '--color-background-100': '242 241 241', + '--color-background-200': '220 219 219', + '--color-background-300': '213 212 212', + '--color-background-400': '162 163 163', + '--color-background-500': '142 142 142', + '--color-background-600': '116 116 116', + '--color-background-700': '83 82 82', + '--color-background-800': '65 64 64', + '--color-background-900': '39 38 37', + '--color-background-950': '18 18 18', + + /* Background Special */ + '--color-background-error': '254 241 241', + '--color-background-warning': '255 243 234', + '--color-background-success': '237 252 242', + '--color-background-muted': '247 248 247', + '--color-background-info': '235 248 254', + + /* Focus Ring Indicator */ + '--color-indicator-primary': '55 55 55', + '--color-indicator-info': '83 153 236', + '--color-indicator-error': '185 28 28', + }), + dark: vars({ + '--color-primary-0': '166 166 166', + '--color-primary-50': '175 175 175', + '--color-primary-100': '186 186 186', + '--color-primary-200': '197 197 197', + '--color-primary-300': '212 212 212', + '--color-primary-400': '221 221 221', + '--color-primary-500': '230 230 230', + '--color-primary-600': '240 240 240', + '--color-primary-700': '250 250 250', + '--color-primary-800': '253 253 253', + '--color-primary-900': '254 249 249', + '--color-primary-950': '253 252 252', + + /* Secondary */ + '--color-secondary-0': '20 20 20', + '--color-secondary-50': '23 23 23', + '--color-secondary-100': '31 31 31', + '--color-secondary-200': '39 39 39', + '--color-secondary-300': '44 44 44', + '--color-secondary-400': '56 57 57', + '--color-secondary-500': '63 64 64', + '--color-secondary-600': '86 86 86', + '--color-secondary-700': '110 110 110', + '--color-secondary-800': '135 135 135', + '--color-secondary-900': '150 150 150', + '--color-secondary-950': '164 164 164', + + /* Tertiary */ + '--color-tertiary-0': '84 49 18', + '--color-tertiary-50': '108 61 19', + '--color-tertiary-100': '130 73 23', + '--color-tertiary-200': '180 98 26', + '--color-tertiary-300': '215 117 31', + '--color-tertiary-400': '231 129 40', + '--color-tertiary-500': '251 157 75', + '--color-tertiary-600': '253 180 116', + '--color-tertiary-700': '254 209 170', + '--color-tertiary-800': '255 233 213', + '--color-tertiary-900': '255 242 229', + '--color-tertiary-950': '255 250 245', + + /* Error */ + '--color-error-0': '83 19 19', + '--color-error-50': '127 29 29', + '--color-error-100': '153 27 27', + '--color-error-200': '185 28 28', + '--color-error-300': '220 38 38', + '--color-error-400': '230 53 53', + '--color-error-500': '239 68 68', + '--color-error-600': '249 97 96', + '--color-error-700': '229 91 90', + '--color-error-800': '254 202 202', + '--color-error-900': '254 226 226', + '--color-error-950': '254 233 233', + + /* Success */ + '--color-success-0': '27 50 36', + '--color-success-50': '20 83 45', + '--color-success-100': '22 101 52', + '--color-success-200': '32 111 62', + '--color-success-300': '42 121 72', + '--color-success-400': '52 131 82', + '--color-success-500': '72 151 102', + '--color-success-600': '102 181 132', + '--color-success-700': '132 211 162', + '--color-success-800': '162 241 192', + '--color-success-900': '202 255 232', + '--color-success-950': '228 255 244', + + /* Warning */ + '--color-warning-0': '84 45 18', + '--color-warning-50': '108 56 19', + '--color-warning-100': '130 68 23', + '--color-warning-200': '180 90 26', + '--color-warning-300': '215 108 31', + '--color-warning-400': '231 120 40', + '--color-warning-500': '251 149 75', + '--color-warning-600': '253 173 116', + '--color-warning-700': '254 205 170', + '--color-warning-800': '255 231 213', + '--color-warning-900': '255 244 237', + '--color-warning-950': '255 249 245', + + /* Info */ + '--color-info-0': '3 38 56', + '--color-info-50': '5 64 93', + '--color-info-100': '7 90 131', + '--color-info-200': '9 115 168', + '--color-info-300': '11 141 205', + '--color-info-400': '13 166 242', + '--color-info-500': '50 180 244', + '--color-info-600': '87 194 246', + '--color-info-700': '124 207 248', + '--color-info-800': '162 221 250', + '--color-info-900': '199 235 252', + '--color-info-950': '236 248 254', + + /* Typography */ + '--color-typography-0': '23 23 23', + '--color-typography-50': '38 38 39', + '--color-typography-100': '64 64 64', + '--color-typography-200': '82 82 82', + '--color-typography-300': '115 115 115', + '--color-typography-400': '140 140 140', + '--color-typography-500': '163 163 163', + '--color-typography-600': '212 212 212', + '--color-typography-700': '219 219 220', + '--color-typography-800': '229 229 229', + '--color-typography-900': '245 245 245', + '--color-typography-950': '254 254 255', + + /* Outline */ + '--color-outline-0': '26 23 23', + '--color-outline-50': '39 38 36', + '--color-outline-100': '65 65 65', + '--color-outline-200': '83 82 82', + '--color-outline-300': '115 116 116', + '--color-outline-400': '140 141 141', + '--color-outline-500': '165 163 163', + '--color-outline-600': '211 211 211', + '--color-outline-700': '221 220 219', + '--color-outline-800': '230 230 230', + '--color-outline-900': '243 243 243', + '--color-outline-950': '253 254 254', + + /* Background */ + '--color-background-0': '18 18 18', + '--color-background-50': '39 38 37', + '--color-background-100': '65 64 64', + '--color-background-200': '83 82 82', + '--color-background-300': '116 116 116', + '--color-background-400': '142 142 142', + '--color-background-500': '162 163 163', + '--color-background-600': '213 212 212', + '--color-background-700': '229 228 228', + '--color-background-800': '242 241 241', + '--color-background-900': '246 246 246', + '--color-background-950': '255 255 255', + + /* Background Special */ + '--color-background-error': '66 43 43', + '--color-background-warning': '65 47 35', + '--color-background-success': '28 43 33', + '--color-background-muted': '51 51 51', + '--color-background-info': '26 40 46', + + /* Focus Ring Indicator */ + '--color-indicator-primary': '247 247 247', + '--color-indicator-info': '161 199 245', + '--color-indicator-error': '232 70 69', + }), +}; diff --git a/example/expo/components/ui/gluestack-ui-provider/index.tsx b/example/expo/components/ui/gluestack-ui-provider/index.tsx new file mode 100644 index 0000000..d98420f --- /dev/null +++ b/example/expo/components/ui/gluestack-ui-provider/index.tsx @@ -0,0 +1,37 @@ +import React, { useEffect } from 'react'; +import { config } from './config'; +import { View, ViewProps } from 'react-native'; +import { OverlayProvider } from '@gluestack-ui/overlay'; +import { ToastProvider } from '@gluestack-ui/toast'; +import { useColorScheme } from 'nativewind'; +import { ModeType } from './types'; + +export function GluestackUIProvider({ + mode = 'light', + ...props +}: { + mode?: ModeType; + children?: React.ReactNode; + style?: ViewProps['style']; +}) { + const { colorScheme, setColorScheme } = useColorScheme(); + + useEffect(() => { + setColorScheme(mode); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [mode]); + + return ( + + + {props.children} + + + ); +} diff --git a/example/expo/components/ui/gluestack-ui-provider/index.web.tsx b/example/expo/components/ui/gluestack-ui-provider/index.web.tsx new file mode 100644 index 0000000..d76161c --- /dev/null +++ b/example/expo/components/ui/gluestack-ui-provider/index.web.tsx @@ -0,0 +1,95 @@ +'use client'; +import React, { useEffect, useLayoutEffect } from 'react'; +import { config } from './config'; +import { OverlayProvider } from '@gluestack-ui/overlay'; +import { ToastProvider } from '@gluestack-ui/toast'; +import { setFlushStyles } from '@gluestack-ui/nativewind-utils/flush'; +import { script } from './script'; +import { ModeType } from './types'; + +const variableStyleTagId = 'nativewind-style'; +const createStyle = (styleTagId: string) => { + const style = document.createElement('style'); + style.id = styleTagId; + style.appendChild(document.createTextNode('')); + return style; +}; + +export const useSafeLayoutEffect = + typeof window !== 'undefined' ? useLayoutEffect : useEffect; + +export function GluestackUIProvider({ + mode = 'light', + ...props +}: { + mode?: ModeType; + children?: React.ReactNode; +}) { + let cssVariablesWithMode = ``; + Object.keys(config).forEach((configKey) => { + cssVariablesWithMode += + configKey === 'dark' ? `\n .dark {\n ` : `\n:root {\n`; + const cssVariables = Object.keys( + config[configKey as keyof typeof config] + ).reduce((acc: string, curr: string) => { + acc += `${curr}:${config[configKey as keyof typeof config][curr]}; `; + return acc; + }, ''); + cssVariablesWithMode += `${cssVariables} \n}`; + }); + + setFlushStyles(cssVariablesWithMode); + + const handleMediaQuery = React.useCallback((e: MediaQueryListEvent) => { + script(e.matches ? 'dark' : 'light'); + }, []); + + useSafeLayoutEffect(() => { + if (mode !== 'system') { + const documentElement = document.documentElement; + if (documentElement) { + documentElement.classList.add(mode); + documentElement.classList.remove(mode === 'light' ? 'dark' : 'light'); + documentElement.style.colorScheme = mode; + } + } + }, [mode]); + + useSafeLayoutEffect(() => { + if (mode !== 'system') return; + const media = window.matchMedia('(prefers-color-scheme: dark)'); + + media.addListener(handleMediaQuery); + + return () => media.removeListener(handleMediaQuery); + }, [handleMediaQuery]); + + useSafeLayoutEffect(() => { + if (typeof window !== 'undefined') { + const documentElement = document.documentElement; + if (documentElement) { + const head = documentElement.querySelector('head'); + let style = head?.querySelector(`[id='${variableStyleTagId}']`); + if (!style) { + style = createStyle(variableStyleTagId); + style.innerHTML = cssVariablesWithMode; + if (head) head.appendChild(style); + } + } + } + }, []); + + return ( + <> + `; + + return ( +

+ + + + + + {t('Tracking code')} + + {t('Add this code into your website head script')} + + + + + {trackScript} + + + + ); + } +); +WebsiteCodeBtn.displayName = 'WebsiteCodeBtn'; diff --git a/src/client/components/website/WebsiteConfig.tsx b/src/client/components/website/WebsiteConfig.tsx new file mode 100644 index 0000000..cd005ac --- /dev/null +++ b/src/client/components/website/WebsiteConfig.tsx @@ -0,0 +1,281 @@ +import { Form, Input, message } from 'antd'; +import React, { useMemo } from 'react'; +import { useRequest } from '../../hooks/useRequest'; +import { useCurrentWorkspaceId } from '../../store/user'; +import { ErrorTip } from '../ErrorTip'; +import { Loading } from '../Loading'; +import { NoWorkspaceTip } from '../NoWorkspaceTip'; +import { MonitorPickerOld } from '../monitor/MonitorPicker'; +import { + defaultErrorHandler, + defaultSuccessHandler, + trpc, +} from '../../api/trpc'; +import { useEvent } from '../../hooks/useEvent'; +import { hostnameValidator } from '../../utils/validator'; +import { useTranslation } from '@i18next-toolkit/react'; +import { useNavigate } from '@tanstack/react-router'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs'; +import { AlertConfirm } from '../AlertConfirm'; +import { Button } from '../ui/button'; +import { Card, CardContent, CardHeader } from '../ui/card'; +import { toast } from 'sonner'; +import copy from 'copy-to-clipboard'; +import { LuShare2, LuTrash } from 'react-icons/lu'; + +export const WebsiteConfig: React.FC<{ websiteId: string }> = React.memo( + (props) => { + const { websiteId } = props; + const { t } = useTranslation(); + const workspaceId = useCurrentWorkspaceId(); + const navigate = useNavigate(); + const trpcUtils = trpc.useUtils(); + + const { data: website, isLoading } = trpc.website.info.useQuery({ + workspaceId, + websiteId, + }); + + const updateMutation = trpc.website.updateInfo.useMutation({ + onSuccess: defaultSuccessHandler, + onError: defaultErrorHandler, + }); + const deleteMutation = trpc.website.delete.useMutation({ + onError: defaultErrorHandler, + }); + const enableShareMutation = trpc.website.createOrEnableShare.useMutation({ + onError: defaultErrorHandler, + }); + const disableShareMutation = trpc.website.disableShare.useMutation({ + onError: defaultErrorHandler, + }); + + const handleSave = useEvent( + async (values: { name: string; domain: string; monitorId: string }) => { + await updateMutation.mutateAsync({ + workspaceId, + websiteId, + name: values.name, + domain: values.domain, + monitorId: values.monitorId ?? null, + }); + + trpcUtils.website.info.refetch({ + workspaceId, + websiteId, + }); + trpcUtils.website.all.refetch({ workspaceId }); + + navigate({ + to: '/website/$websiteId', + params: { + websiteId, + }, + }); + } + ); + + const [, handleDeleteWebsite] = useRequest(async () => { + await deleteMutation.mutateAsync({ workspaceId, websiteId }); + + message.success(t('Delete Success')); + + await trpcUtils.website.all.refetch({ workspaceId }); + + navigate({ + to: '/website', + }); + }); + + const handleEnableShare = useEvent(async () => { + const res = await enableShareMutation.mutateAsync({ + workspaceId, + websiteId, + }); + + copy(res.shareUrl); + toast.success(t('Public share link copied to clipboard')); + + await trpcUtils.website.info.refetch({ workspaceId, websiteId }); + }); + + const handleDisableShare = useEvent(async () => { + await disableShareMutation.mutateAsync({ workspaceId, websiteId }); + + toast.success(t('Disable Public Share')); + await trpcUtils.website.info.refetch({ workspaceId, websiteId }); + }); + + const shareUrl = useMemo(() => { + if (!website?.shareId) { + return ''; + } + + const origin = + typeof window !== 'undefined' ? window.location.origin : ''; + + if (origin) { + return `${origin}/website/public/${website.shareId}`; + } + + return `/website/public/${website.shareId}`; + }, [website?.shareId]); + + if (!workspaceId) { + return ; + } + + if (!websiteId) { + return ; + } + + if (isLoading) { + return ; + } + + if (!website) { + return ; + } + + return ( +
+
+ + + {t('Detail')} + {t('Data')} + + +
+ + + + + + + + + + + + + + + + + +
+
+ + + + {t('Public Share')} + + + {website.shareId ? ( +
+
+ {shareUrl} +
+ +
+ + + +
+
+ ) : ( + + )} +
+
+ + + + {t('Danger Zone')} + + +
+ handleDeleteWebsite()} + > + + +
+
+
+
+
+
+
+ ); + } +); +WebsiteConfig.displayName = 'WebsiteConfig'; diff --git a/src/client/components/website/WebsiteEventAnalysis.tsx b/src/client/components/website/WebsiteEventAnalysis.tsx new file mode 100644 index 0000000..e7f061d --- /dev/null +++ b/src/client/components/website/WebsiteEventAnalysis.tsx @@ -0,0 +1,87 @@ +import React, { useMemo } from 'react'; +import dayjs from 'dayjs'; +import { Empty } from 'antd'; +import { useTranslation } from '@i18next-toolkit/react'; + +import { trpc } from '@/api/trpc'; +import { InsightQueryChart } from '@/components/insights/InsightQueryChart'; +import { LoadingView } from '@/components/LoadingView'; +import { useCurrentWorkspaceId } from '@/store/user'; + +const MAX_EVENT_SERIES = 5; + +export const WebsiteEventAnalysis: React.FC<{ + websiteId: string; +}> = React.memo((props) => { + const { websiteId } = props; + const { t } = useTranslation(); + const workspaceId = useCurrentWorkspaceId(); + + const { startAt, endAt } = useMemo(() => { + const end = dayjs().endOf('hour'); + const start = end.subtract(24, 'hour'); + + return { + startAt: start.valueOf(), + endAt: end.valueOf(), + }; + }, [websiteId]); + + const { data: eventMetrics = [], isLoading: isLoadingEventMetrics } = + trpc.website.metrics.useQuery( + { + workspaceId, + websiteId, + type: 'event', + startAt, + endAt, + }, + { + trpc: { + context: { + skipBatch: true, + }, + }, + } + ); + + const metricsConfig = useMemo(() => { + return eventMetrics + .filter((item) => Boolean(item.x)) + .slice(0, MAX_EVENT_SERIES) + .map((item) => ({ + name: String(item.x), + math: 'events' as const, + alias: String(item.x), + })); + }, [eventMetrics]); + + return ( +
+ + {metricsConfig.length === 0 ? ( +
+ +
+ ) : ( + + )} +
+
+ ); +}); +WebsiteEventAnalysis.displayName = 'WebsiteEventAnalysis'; diff --git a/src/client/components/website/WebsiteLighthouseBtn.tsx b/src/client/components/website/WebsiteLighthouseBtn.tsx new file mode 100644 index 0000000..cf0af94 --- /dev/null +++ b/src/client/components/website/WebsiteLighthouseBtn.tsx @@ -0,0 +1,195 @@ +import React, { useMemo, useState } from 'react'; +import { Button } from '../ui/button'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '../ui/dialog'; +import { useTranslation } from '@i18next-toolkit/react'; +import { TbBuildingLighthouse } from 'react-icons/tb'; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger, +} from '../ui/sheet'; +import { defaultErrorHandler, defaultSuccessHandler, trpc } from '@/api/trpc'; +import { useCurrentWorkspaceId, useHasPermission } from '@/store/user'; +import { formatDate } from '@/utils/date'; +import { Input } from '../ui/input'; +import { toast } from 'sonner'; +import { useEvent } from '@/hooks/useEvent'; +import { Badge } from '../ui/badge'; +import { LuArrowRight, LuPlus } from 'react-icons/lu'; +import { ROLES } from '@tianji/shared'; +import { useSocketSubscribe } from '@/api/socketio'; + +interface WebsiteLighthouseBtnProps { + websiteId: string; +} +export const WebsiteLighthouseBtn: React.FC = + React.memo((props) => { + const workspaceId = useCurrentWorkspaceId(); + const { t } = useTranslation(); + const [url, setUrl] = useState(''); + const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); + const hasAdminPermission = useHasPermission(ROLES.admin); + + const { data, hasNextPage, fetchNextPage, isFetchingNextPage, refetch } = + trpc.website.getLighthouseReport.useInfiniteQuery( + { + workspaceId, + websiteId: props.websiteId, + }, + { + getNextPageParam: (lastPage) => lastPage.nextCursor, + } + ); + const createMutation = trpc.website.generateLighthouseReport.useMutation({ + onSuccess: defaultSuccessHandler, + onError: defaultErrorHandler, + }); + + useSocketSubscribe('onLighthouseWorkCompleted', (data) => { + const { websiteId } = data; + if (websiteId !== props.websiteId) { + return; + } + + refetch(); + toast.info(t('Lighthouse report completed!')); + }); + + const allData = useMemo(() => { + if (!data) { + return []; + } + + return [...data.pages.flatMap((p) => p.items)]; + }, [data]); + + const handleGenerateReport = useEvent(async () => { + if (!url) { + toast.error(t('Url is required')); + return; + } + + await createMutation.mutateAsync({ + workspaceId, + websiteId: props.websiteId, + url, + }); + setIsCreateDialogOpen(false); + refetch(); + }); + + return ( + + + + + + + + {t('Generate Lighthouse Report')} + + + {t('Its will take a while to generate the report.')} + + + +
+ setUrl(e.target.value)} + placeholder="https://google.com" + /> +
+ + + + +
+ + + )} + +
+ {allData.map((report) => { + return ( +
+ {report.status} + +
+
{report.url}
+
+ {formatDate(report.createdAt)} +
+
+ + {report.status === 'Success' && ( +
+ ); + })} +
+ + {hasNextPage && ( + + )} + + +
+ ); + }); +WebsiteLighthouseBtn.displayName = 'WebsiteLighthouseBtn'; diff --git a/src/client/components/website/WebsiteMetricsTable.tsx b/src/client/components/website/WebsiteMetricsTable.tsx new file mode 100644 index 0000000..a9b8fe0 --- /dev/null +++ b/src/client/components/website/WebsiteMetricsTable.tsx @@ -0,0 +1,112 @@ +import { Table } from 'antd'; +import { ColumnsType } from 'antd/es/table/interface'; +import React from 'react'; +import { AppRouterOutput, trpc } from '../../api/trpc'; +import { useCurrentWorkspaceId } from '../../store/user'; +import { sum } from 'lodash-es'; +import { formatNumber } from '../../utils/common'; +import { useTranslation } from '@i18next-toolkit/react'; +import { useCountryMap } from '@/utils/country'; +import { LoadingView } from '../LoadingView'; + +type MetricsItemType = AppRouterOutput['website']['metrics'][number]; + +interface MetricsTableProps { + websiteId: string; + title: [string, string]; + type: + | 'url' + | 'language' + | 'referrer' + | 'title' + | 'browser' + | 'os' + | 'device' + | 'country' + | 'event' + | 'utm_source' + | 'utm_medium' + | 'utm_campaign' + | 'utm_term' + | 'utm_content'; + startAt: number; + endAt: number; +} +export const WebsiteMetricsTable: React.FC = React.memo( + (props) => { + const { websiteId, title, type, startAt, endAt } = props; + const workspaceId = useCurrentWorkspaceId(); + const { t } = useTranslation(); + const countryMap = useCountryMap(); + const labelMap: Record = { + desktop: t('Desktop'), + laptop: t('Laptop'), + tablet: t('Tablet'), + mobile: t('Mobile'), + ...countryMap, + }; + + const { isLoading, data: metrics = [] } = trpc.website.metrics.useQuery({ + workspaceId, + websiteId, + type, + startAt, + endAt, + }); + + const total = sum(metrics.map((m) => m.y)); + + const columns: ColumnsType = [ + { + title: title[0], + dataIndex: 'x', + ellipsis: true, + render: (val) => + val ? ( + {labelMap[val] ?? val} + ) : ( + {t('(None)')} + ), + }, + { + title: title[1], + dataIndex: 'y', + width: 100, + align: 'center', + render: (val) => { + const percent = (Number(val) / total) * 100; + + return ( +
+
{formatNumber(val)}
+
+
+ {percent.toFixed(0)}% +
+
+ ); + }, + }, + ]; + + return ( + + + + ); + } +); +WebsiteMetricsTable.displayName = 'WebsiteMetricsTable'; diff --git a/src/client/components/website/WebsiteOnlineCount.tsx b/src/client/components/website/WebsiteOnlineCount.tsx new file mode 100644 index 0000000..23a178b --- /dev/null +++ b/src/client/components/website/WebsiteOnlineCount.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { trpc } from '../../api/trpc'; +import { useTranslation } from '@i18next-toolkit/react'; + +export const WebsiteOnlineCount: React.FC<{ + workspaceId: string; + websiteId: string; +}> = React.memo((props) => { + const { t } = useTranslation(); + const { workspaceId, websiteId } = props; + + const { data: count } = trpc.website.onlineCount.useQuery({ + workspaceId, + websiteId, + }); + + if (typeof count === 'number' && count > 0) { + return ( +
+
+
+
+
+ + {count} {t('current visitor')} + +
+ ); + } + + return null; +}); +WebsiteOnlineCount.displayName = 'WebsiteOnlineCount'; diff --git a/src/client/components/website/WebsiteOverview.tsx b/src/client/components/website/WebsiteOverview.tsx new file mode 100644 index 0000000..1d6509b --- /dev/null +++ b/src/client/components/website/WebsiteOverview.tsx @@ -0,0 +1,249 @@ +import { Button, message, Spin, Switch } from 'antd'; +import React from 'react'; +import { SyncOutlined } from '@ant-design/icons'; +import { DateFilter } from '../DateFilter'; +import { WebsiteInfo } from '../../api/model/website'; +import { getDateArray } from '../../utils/date'; +import { useEvent } from '../../hooks/useEvent'; +import { MetricCard } from '../MetricCard'; +import { formatNumber, formatShortTime } from '../../utils/common'; +import { WebsiteOnlineCount } from './WebsiteOnlineCount'; +import { useGlobalRangeDate } from '../../hooks/useGlobalRangeDate'; +import { MonitorHealthBar } from '../monitor/MonitorHealthBar'; +import { AppRouterOutput, trpc } from '../../api/trpc'; +import { getUserTimezone } from '../../api/model/user'; +import { useGlobalStateStore } from '../../store/global'; +import { useTranslation } from '@i18next-toolkit/react'; +import { TimeEventChart } from '../chart/TimeEventChart'; +import { Link } from '@tanstack/react-router'; +import { LoadingView } from '../LoadingView'; +import { toast } from 'sonner'; + +export const WebsiteOverview: React.FC<{ + website: WebsiteInfo; + showDateFilter?: boolean; + actions?: React.ReactNode; +}> = React.memo((props) => { + const { t } = useTranslation(); + const { website, showDateFilter = false, actions } = props; + const { startDate, endDate, unit, refresh } = useGlobalRangeDate(); + const showPreviousPeriod = useGlobalStateStore( + (state) => state.showPreviousPeriod + ); + + const { + data: chartData = [], + isLoading: isLoadingPageview, + refetch: refetchPageview, + } = trpc.insights.query.useQuery( + { + workspaceId: website.workspaceId, + insightId: website.id, + insightType: 'website', + metrics: [ + { name: '$page_view', math: 'events', alias: 'A' }, + { name: '$page_view', math: 'sessions', alias: 'B' }, + ], + filters: [], + groups: [], + time: { + startAt: startDate.valueOf(), + endAt: endDate.valueOf(), + unit, + timezone: getUserTimezone(), + }, + }, + { + select(data) { + const pvSeries = data?.[0]?.data ?? []; + const uvSeries = data?.[1]?.data ?? []; + + const pvMap = new Map( + pvSeries.map((d) => [d.date, Number(d.value) || 0]) + ); + const uvMap = new Map( + uvSeries.map((d) => [d.date, Number(d.value) || 0]) + ); + + const dates: string[] = pvSeries.map((d) => d.date); + + return dates.map((date) => ({ + pv: pvMap.get(date) ?? 0, + uv: uvMap.get(date) ?? 0, + date, + })); + }, + trpc: { + context: { + skipBatch: true, + }, + }, + } + ); + + const { + data: stats, + isLoading: isLoadingStats, + refetch: refetchStats, + } = trpc.website.stats.useQuery( + { + workspaceId: website.workspaceId, + websiteId: website.id, + startAt: startDate.unix() * 1000, + endAt: endDate.unix() * 1000, + timezone: getUserTimezone(), + unit, + }, + { + trpc: { + context: { + skipBatch: true, + }, + }, + } + ); + + const handleRefresh = useEvent(async () => { + refresh(); + + await Promise.all([refetchPageview(), refetchStats()]); + + toast.success(t('Refreshed')); + }); + + const loading = isLoadingPageview || isLoadingStats; + + return ( + +
+
+ + {website.name} + + + {website.monitorId && ( + + + + )} + +
+ +
+
+ +
{actions}
+
+ +
+
{stats && }
+ +
+
+ + useGlobalStateStore.setState({ + showPreviousPeriod: checked, + }) + } + /> + {t('Previous period')} +
+ +
+
+ +
+ +
+
+ ); +}); +WebsiteOverview.displayName = 'WebsiteOverview'; + +const MetricsBar: React.FC<{ + stats: AppRouterOutput['website']['stats']; +}> = React.memo((props) => { + const { t } = useTranslation(); + const { pageviews, uniques, bounces, totaltime } = props.stats || {}; + const bouncesNum = Math.min(uniques.value, bounces.value) / uniques.value; + const prevBouncesNum = Math.min(uniques.prev, bounces.prev) / uniques.prev; + + return ( +
+ + + formatNumber(n) + '%'} + /> + + `${n < 0 ? '-' : ''}${formatShortTime( + Math.abs(~~n), + ['m', 's'], + ' ' + )}` + } + /> +
+ ); +}); +MetricsBar.displayName = 'MetricsBar'; diff --git a/src/client/components/website/WebsiteSimpleMap.tsx b/src/client/components/website/WebsiteSimpleMap.tsx new file mode 100644 index 0000000..f610da9 --- /dev/null +++ b/src/client/components/website/WebsiteSimpleMap.tsx @@ -0,0 +1,35 @@ +import React, { useMemo } from 'react'; +import { trpc } from '../../api/trpc'; +import { useCurrentWorkspaceId } from '../../store/user'; +import { SimpleWorldMap } from '../SimpleWorldMap'; + +interface WebsiteSimpleMapProps { + websiteId: string; + startAt: number; + endAt: number; +} + +export const WebsiteSimpleMap: React.FC = React.memo( + (props) => { + const { websiteId, startAt, endAt } = props; + const workspaceId = useCurrentWorkspaceId(); + + const { data: metrics = [] } = trpc.website.metrics.useQuery({ + workspaceId, + websiteId, + type: 'country', + startAt, + endAt, + }); + + const data = useMemo(() => { + return metrics.map((m) => ({ + country: m.x ?? '', + visitors: m.y, + })); + }, [metrics]); + + return ; + } +); +WebsiteSimpleMap.displayName = 'WebsiteSimpleMap'; diff --git a/src/client/components/website/WebsiteVisitorMap/VisitorLarkMap.tsx b/src/client/components/website/WebsiteVisitorMap/VisitorLarkMap.tsx new file mode 100644 index 0000000..647bdd1 --- /dev/null +++ b/src/client/components/website/WebsiteVisitorMap/VisitorLarkMap.tsx @@ -0,0 +1,95 @@ +import { + FullscreenControl, + LarkMap, + LarkMapProps, + PointLayer, + PointLayerProps, +} from '@antv/larkmap'; +import React, { useMemo } from 'react'; +import { AppRouterOutput } from '../../../api/trpc'; +import { useGlobalConfig } from '../../../hooks/useConfig'; +import { useTheme } from '../../../store/settings'; +import { mapCenter } from './utils'; + +const layerOptions: Omit = { + autoFit: true, + shape: 'circle', + size: 5, + blend: 'additive', + color: { + field: 'count', + value: [ + 'rgb(102,37,6)', + 'rgb(153,52,4)', + 'rgb(204,76,2)', + 'rgb(236,112,20)', + 'rgb(254,153,41)', + 'rgb(254,196,79)', + 'rgb(254,227,145)', + ], + }, +}; + +function useMapConfig(mapType: 'Mapbox' | 'Gaode' = 'Mapbox'): LarkMapProps { + const { amapToken, mapboxToken } = useGlobalConfig(); + const colorScheme = useTheme(); + + const baseOption: LarkMapProps['mapOptions'] = { + center: [mapCenter.lng, mapCenter.lat], + zoom: 0, + }; + + if (mapType === 'Gaode') { + return { + mapType: 'Gaode', + mapOptions: { + ...baseOption, + style: + colorScheme === 'light' + ? 'amap://styles/light' + : 'amap://styles/dark', + token: amapToken, + }, + logoVisible: false, + }; + } else { + return { + mapType: 'Mapbox', + mapOptions: { + ...baseOption, + style: colorScheme === 'light' ? 'light' : 'dark', + token: mapboxToken, + }, + logoVisible: false, + }; + } +} + +export const VisitorLarkMap: React.FC<{ + data: AppRouterOutput['website']['geoStats']; + mapType: 'Mapbox' | 'Gaode'; + fullScreen?: boolean; +}> = React.memo((props) => { + const config = useMapConfig(props.mapType); + + const source: PointLayerProps['source'] = { + data: props.data ?? [], + parser: { type: 'json', x: 'longitude', y: 'latitude' }, + }; + + const size = useMemo(() => { + if (props.data.length > 5000) { + return 3; + } + + return 5; + }, [props.data.length]); + + return ( + + + + + ); +}); +VisitorLarkMap.displayName = 'VisitorLarkMap'; diff --git a/src/client/components/website/WebsiteVisitorMap/VisitorLeafletMap.css b/src/client/components/website/WebsiteVisitorMap/VisitorLeafletMap.css new file mode 100644 index 0000000..bd8f2cb --- /dev/null +++ b/src/client/components/website/WebsiteVisitorMap/VisitorLeafletMap.css @@ -0,0 +1,3 @@ +.leaflet-control-attribution.leaflet-control { + display: none; +} diff --git a/src/client/components/website/WebsiteVisitorMap/VisitorLeafletMap.tsx b/src/client/components/website/WebsiteVisitorMap/VisitorLeafletMap.tsx new file mode 100644 index 0000000..3de4828 --- /dev/null +++ b/src/client/components/website/WebsiteVisitorMap/VisitorLeafletMap.tsx @@ -0,0 +1,90 @@ +import React, { useMemo } from 'react'; +import { AppRouterOutput } from '../../../api/trpc'; +import { MapContainer, CircleMarker, Popup, TileLayer } from 'react-leaflet'; +import { mapCenter } from './utils'; +import { useTranslation } from '@i18next-toolkit/react'; +import { useTheme } from '@/store/settings'; +import { cn } from '@/utils/style'; +import 'leaflet/dist/leaflet.css'; +import './VisitorLeafletMap.css'; + +export const UserDataPoint: React.FC<{ + pointRadius?: number; + longitude: number; + latitude: number; + count: number; +}> = React.memo((props) => { + const { t } = useTranslation(); + const pointRadius = props.pointRadius ?? 5; + + return ( + + + {t('{{num}} users', { + num: props.count, + })} + + + ); +}); +UserDataPoint.displayName = 'UserDataPoint'; + +export const VisitorLeafletMap: React.FC<{ + data: AppRouterOutput['website']['geoStats']; + fullScreen?: boolean; +}> = React.memo((props) => { + const theme = useTheme(); + const pointRadius = useMemo(() => { + if (props.data.length > 20000) { + return 1; + } + + if (props.data.length > 5000) { + return 2; + } + + if (props.data.length > 1000) { + return 3; + } + + return 5; + }, [props.data.length]); + + return ( + + {/* */} + + {theme === 'light' ? ( + + ) : ( + + )} + + {props.data.map((item) => ( + + ))} + + ); +}); +VisitorLeafletMap.displayName = 'VisitorLeafletMap'; diff --git a/src/client/components/website/WebsiteVisitorMap/index.tsx b/src/client/components/website/WebsiteVisitorMap/index.tsx new file mode 100644 index 0000000..e5eb51a --- /dev/null +++ b/src/client/components/website/WebsiteVisitorMap/index.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { trpc } from '../../../api/trpc'; +import { useCurrentWorkspaceId } from '../../../store/user'; +import { useGlobalRangeDate } from '../../../hooks/useGlobalRangeDate'; +import loadable from '@loadable/component'; +import { Loading } from '../../Loading'; +import { useGlobalConfig } from '../../../hooks/useConfig'; + +const VisitorLeafletMap = loadable(() => + import('./VisitorLeafletMap').then((m) => m.VisitorLeafletMap) +); + +const VisitorLarkMap = loadable(() => + import('./VisitorLarkMap').then((m) => m.VisitorLarkMap) +); + +function useMapType() { + const { amapToken, mapboxToken } = useGlobalConfig(); + + if (mapboxToken) { + return 'Mapbox'; + } else if (amapToken) { + return 'Gaode'; + } else { + return 'Leaflet'; + } +} + +interface WebsiteVisitorMapProps { + websiteId: string; + fullScreen?: boolean; +} +export const WebsiteVisitorMap: React.FC = React.memo( + (props) => { + const workspaceId = useCurrentWorkspaceId(); + const { startDate, endDate } = useGlobalRangeDate(); + const startAt = startDate.valueOf(); + const endAt = endDate.valueOf(); + const { data } = trpc.website.geoStats.useQuery({ + workspaceId, + websiteId: props.websiteId, + startAt, + endAt, + }); + + const mapType = useMapType(); + + if (!data) { + return ; + } + + if (mapType === 'Mapbox' || mapType === 'Gaode') { + return ( + + ); + } + + return ; + } +); +WebsiteVisitorMap.displayName = 'WebsiteVisitorMap'; diff --git a/src/client/components/website/WebsiteVisitorMap/utils.ts b/src/client/components/website/WebsiteVisitorMap/utils.ts new file mode 100644 index 0000000..8acf828 --- /dev/null +++ b/src/client/components/website/WebsiteVisitorMap/utils.ts @@ -0,0 +1,4 @@ +export const mapCenter = { + lat: 30.246026, + lng: 120.210792, +}; diff --git a/src/client/components/website/WebsiteVisitorMapBtn.tsx b/src/client/components/website/WebsiteVisitorMapBtn.tsx new file mode 100644 index 0000000..a6f68eb --- /dev/null +++ b/src/client/components/website/WebsiteVisitorMapBtn.tsx @@ -0,0 +1,39 @@ +import { useTranslation } from '@i18next-toolkit/react'; +import React from 'react'; +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, + SheetTrigger, +} from '../ui/sheet'; +import { Button } from '../ui/button'; +import { WebsiteVisitorMap } from './WebsiteVisitorMap'; +import { LuMap } from 'react-icons/lu'; + +interface WebsiteVisitorMapBtnProps { + websiteId: string; +} +export const WebsiteVisitorMapBtn: React.FC = + React.memo((props) => { + const { t } = useTranslation(); + + return ( + + + + + + + + {t('Visitor Map')} + + + + + + ); + }); +WebsiteVisitorMapBtn.displayName = 'WebsiteVisitorMapBtn'; diff --git a/src/client/components/worker/UrlParamsInput.tsx b/src/client/components/worker/UrlParamsInput.tsx new file mode 100644 index 0000000..8e5191b --- /dev/null +++ b/src/client/components/worker/UrlParamsInput.tsx @@ -0,0 +1,132 @@ +import React, { useCallback } from 'react'; +import { useTranslation } from '@i18next-toolkit/react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { LuX } from 'react-icons/lu'; +import qs from 'qs'; +import { useEvent } from '@/hooks/useEvent'; + +export interface UrlParam { + key: string; + value: string; +} + +interface UrlParamsInputProps { + params: UrlParam[]; + onChange: (params: UrlParam[]) => void; + className?: string; +} + +export const UrlParamsInput: React.FC = ({ + params, + onChange, + className = '', +}) => { + const { t } = useTranslation(); + + // Helper function to remove parameter row + const removeParamRow = useEvent((index: number) => { + onChange(params.filter((_, i) => i !== index)); + }); + + // Helper function to update parameter + const updateParam = useEvent( + (index: number, field: 'key' | 'value', value: string) => { + const newParams = params.map((param, i) => + i === index ? { ...param, [field]: value } : param + ); + + // Check if we need to auto-add a new row + let shouldAddNewRow = false; + + // Only auto-add if this is the last row and both key and value are filled + if (index === params.length - 1) { + const currentParam = newParams[index]; + const isKeyField = field === 'key'; + const otherField = isKeyField ? 'value' : 'key'; + + // Check if both fields are now filled + if (isKeyField && value.trim() !== '') { + // Only add new row if we're adding content (not deleting) + shouldAddNewRow = true; + } + } + + // Update params first + onChange(newParams); + + // Then add new row if needed + if (shouldAddNewRow) { + onChange([...newParams, { key: '', value: '' }]); + } + } + ); + + return ( +
+
+ +
+
+ {params.map((param, index) => ( +
+ updateParam(index, 'key', e.target.value)} + className="flex-1 text-sm" + /> + updateParam(index, 'value', e.target.value)} + className="flex-1 text-sm" + /> + +
+ ))} +
+

+ {t('Add key-value pairs to append as query parameters')} +

+
+ ); +}; + +export function getQueryString(params: UrlParam[]): string { + const validParams = params.filter((p) => p.key.trim() && p.value.trim()); + if (validParams.length === 0) return ''; + + // Convert array of {key, value} objects to a plain object + const queryObject = validParams.reduce( + (acc, param) => { + acc[param.key.trim()] = param.value.trim(); + return acc; + }, + {} as Record + ); + + return qs.stringify(queryObject); +} + +export function getPayloadFromParams(params: UrlParam[]): Record { + const payload: Record = {}; + + params.forEach((param) => { + if (param.key && param.key.trim() !== '') { + payload[param.key] = param.value; + } + }); + + return payload; +} diff --git a/src/client/components/worker/WorkerApiPreview.tsx b/src/client/components/worker/WorkerApiPreview.tsx new file mode 100644 index 0000000..19a61c6 --- /dev/null +++ b/src/client/components/worker/WorkerApiPreview.tsx @@ -0,0 +1,417 @@ +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from '@i18next-toolkit/react'; + +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { SimpleTooltip } from '@/components/ui/tooltip'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { + UrlParamsInput, + getPayloadFromParams, + getQueryString, + type UrlParam, +} from '@/components/worker/UrlParamsInput'; +import { cn } from '@/utils/style'; +import { toast } from 'sonner'; +import { LuCopy, LuExternalLink, LuFlaskConical, LuPlay } from 'react-icons/lu'; +import { WorkerExecutionDetail } from './WorkerExecutionDetail'; + +type WorkerApiPreviewVariant = 'card' | 'split'; + +export interface WorkerApiPreviewProps { + workspaceId: string; + workerId: string; + isActive: boolean; + disabled?: boolean; + className?: string; + initialParams?: UrlParam[]; + params?: UrlParam[]; + onParamsChange?: (params: UrlParam[]) => void; + onExecute?: () => void; + onCopyUrl?: (url: string) => void; + onOpenNewWindow?: (url: string) => void; + onPreviewLoaded?: () => void; + onTest?: (payload?: Record) => Promise; + variant?: WorkerApiPreviewVariant; +} + +export const WorkerApiPreview: React.FC = ({ + workspaceId, + workerId, + isActive, + disabled, + className, + initialParams, + params: controlledParams, + onParamsChange, + onExecute, + onCopyUrl, + onOpenNewWindow, + onPreviewLoaded, + onTest, + variant = 'card', +}) => { + const { t } = useTranslation(); + + const isControlled = Array.isArray(controlledParams); + + const [params, setParams] = useState(() => { + if (controlledParams && controlledParams.length > 0) { + return [...controlledParams]; + } + + if (initialParams && initialParams.length > 0) { + return [...initialParams]; + } + + return [{ key: '', value: '' }]; + }); + + const [activeParams, setActiveParams] = useState(() => { + if (controlledParams && controlledParams.length > 0) { + return [...controlledParams]; + } + + if (initialParams && initialParams.length > 0) { + return [...initialParams]; + } + + return [{ key: '', value: '' }]; + }); + const [previewKey, setPreviewKey] = useState(0); + const [isLoadingPreview, setIsLoadingPreview] = useState(false); + const [isTestExecuting, setIsTestExecuting] = useState(false); + const [testResult, setTestResult] = useState(null); + const [isTestResultOpen, setIsTestResultOpen] = useState(false); + + useEffect(() => { + if (controlledParams) { + setParams( + controlledParams.length > 0 + ? [...controlledParams] + : [{ key: '', value: '' }] + ); + } + }, [controlledParams]); + + const basePreviewUrl = useMemo(() => { + if (typeof window === 'undefined') { + return ''; + } + + return `${window.location.origin}/api/worker/${workspaceId}/${workerId}`; + }, [workspaceId, workerId]); + + const handleParamsChange = useCallback( + (nextParams: UrlParam[]) => { + const normalized = + nextParams.length > 0 ? nextParams : [{ key: '', value: '' }]; + + if (!isControlled) { + setParams(normalized); + } + + onParamsChange?.(normalized); + }, + [isControlled, onParamsChange] + ); + + const handleCopyUrl = useCallback(async () => { + if (!basePreviewUrl) { + return; + } + + const queryString = getQueryString(params); + const url = queryString + ? `${basePreviewUrl}?${queryString}` + : basePreviewUrl; + + try { + if (onCopyUrl) { + onCopyUrl(url); + } else if (typeof navigator !== 'undefined' && navigator.clipboard) { + await navigator.clipboard.writeText(url); + } else if (typeof document !== 'undefined') { + const textArea = document.createElement('textarea'); + textArea.value = url; + document.body.appendChild(textArea); + textArea.select(); + document.execCommand('copy'); + document.body.removeChild(textArea); + } + toast.success(t('API endpoint URL copied to clipboard')); + } catch (error) { + toast.error(t('Failed to copy URL')); + } + }, [basePreviewUrl, onCopyUrl, params, t]); + + const handleOpenInNewWindow = useCallback(() => { + if (!basePreviewUrl) { + return; + } + + const queryString = getQueryString(params); + const url = queryString + ? `${basePreviewUrl}?${queryString}` + : basePreviewUrl; + if (onOpenNewWindow) { + onOpenNewWindow(url); + } else { + window.open(url, '_blank', 'noopener,noreferrer'); + } + }, [basePreviewUrl, onOpenNewWindow, params]); + + const handleExecutePreview = useCallback(() => { + if (!basePreviewUrl) { + return; + } + + setActiveParams(params.length > 0 ? params : [{ key: '', value: '' }]); + setIsLoadingPreview(true); + setPreviewKey((prev) => prev + 1); + onExecute?.(); + }, [basePreviewUrl, onExecute, params]); + + const handlePreviewLoad = useCallback(() => { + setIsLoadingPreview(false); + onPreviewLoaded?.(); + }, [onPreviewLoaded]); + + const handleTestExecution = useCallback(async () => { + if (!onTest) { + return; + } + + try { + setIsTestExecuting(true); + + const payload = getPayloadFromParams(params); + + const result = await onTest(payload); + setTestResult(result); + setIsTestResultOpen(true); + + if (result.status === 'Success') { + toast.success(t('Test execution completed successfully')); + } else { + toast.error(t('Test execution failed')); + } + } catch (error: any) { + toast.error(error.message || t('Test execution failed')); + } finally { + setIsTestExecuting(false); + } + }, [onTest, params, t]); + + const previewSrc = useMemo(() => { + if (!basePreviewUrl) { + return undefined; + } + + const queryString = getQueryString(activeParams); + return queryString ? `${basePreviewUrl}?${queryString}` : basePreviewUrl; + }, [activeParams, basePreviewUrl]); + + const hasPreview = previewKey > 0; + const copyDisabled = !basePreviewUrl; + const openDisabled = !isActive || !basePreviewUrl; + const executeDisabled = !isActive || !basePreviewUrl || disabled; + + const renderActionButtons = () => ( +
+ +
+ ); + + const renderPreviewFrame = (containerClassName: string) => ( +
+ {hasPreview && previewSrc ? ( +