diff --git a/.cursor/rules/global.mdc b/.cursor/rules/global.mdc new file mode 100644 index 000000000..311b29df2 --- /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/.dockerignore b/.dockerignore new file mode 100644 index 000000000..739ef3f46 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +apps +example +docker +node_modules +packages +website diff --git a/.env.example b/.env.example index 792a37de3..47c944b8a 100644 --- a/.env.example +++ b/.env.example @@ -2,8 +2,8 @@ DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public" # Whether allow feature -ALLOW_REGISTER= -ALLOW_OPENAPI= +ALLOW_REGISTER=false +ALLOW_OPENAPI=true # For analyze tianji self WEBSITE_ID= @@ -12,3 +12,9 @@ WEBSITE_ID= 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.yaml b/.github/workflows/ci.yaml index a0cfa3203..b00a2be27 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,6 +6,8 @@ on: - master paths: - "src/**" + - "package.json" + - "pnpm-lock.yaml" workflow_dispatch: jobs: @@ -17,11 +19,11 @@ jobs: - name: Install Node.js uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 22 - name: Install pnpm uses: pnpm/action-setup@v2 with: - version: 8 + version: 9.7.1 run_install: false - name: Get pnpm store directory shell: bash diff --git a/.github/workflows/reporter-release.yml b/.github/workflows/reporter-release.yml index 748cf8786..5a3f166ea 100644 --- a/.github/workflows/reporter-release.yml +++ b/.github/workflows/reporter-release.yml @@ -22,7 +22,29 @@ jobs: project_path: ./reporter goos: ${{ matrix.goos }} goarch: ${{ matrix.goarch }} - goversion: 1.21.1 + goversion: 1.23.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.23.11 + binary_name: "tianji-reporter" + compress_assets: "OFF" + asset_name: "tianji-reporter-${{ matrix.goos }}-${{ matrix.goarch }}-alpine" diff --git a/.gitignore b/.gitignore index 5c018da3d..5361350d9 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ dist-ssr *.njsproj *.sln *.sw? + +*storybook.log +storybook-static diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..d19a42036 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +package-manager-strict=true +package-manager-strict-version=false diff --git a/.release-it.json b/.release-it.json index f8c90ce16..4f59afcfd 100644 --- a/.release-it.json +++ b/.release-it.json @@ -3,7 +3,9 @@ "release": true }, "git": { - "commitMessage": "chore: release v${version}" + "commitMessage": "chore: release v${version}", + "tag": true, + "tagName": "v${version}" }, "npm": { "publish": false diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..f3cebdb25 --- /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 index 2a9e28155..80f759dc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,3204 @@ +## [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) diff --git a/Dockerfile b/Dockerfile index 13236b490..7a13f8141 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,34 @@ # tianji reporter -FROM golang:1.21.1-bookworm AS reporter -WORKDIR /app/reporter +FROM golang:1.23.11-bookworm AS reporter +WORKDIR /app COPY ./reporter/ ./reporter/ RUN apt update -RUN cd reporter && go build . +RUN cd reporter && CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o tianji-reporter . -# # Base ------------------------------ -FROM node:20-alpine AS base +# Base ------------------------------ +# The current Chromium version in Alpine 3.20 is causing timeout issues with Puppeteer. Downgrading to Alpine 3.19 fixes the issue. See #11640, #12637, #12189 +FROM node:22.14.0-alpine3.20 AS base -RUN npm install -g pnpm@8.3.1 +RUN npm install -g pnpm@9.7.1 + +# For apprise RUN apk add --update --no-cache python3 py3-pip g++ make +# 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 @@ -22,9 +38,10 @@ ARG VERSION COPY . . -RUN pnpm install --frozen-lockfile +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 @@ -32,6 +49,11 @@ RUN pnpm build:static 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 @@ -40,9 +62,18 @@ 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 pip install apprise --break-system-packages +RUN pip install apprise cryptography --break-system-packages + +# Copy startup script +COPY ./scripts/start-tianji-container.sh /usr/local/bin/start-tianji-container.sh +RUN chmod +x /usr/local/bin/start-tianji-container.sh + RUN rm -rf ./src/client RUN rm -rf ./website @@ -50,4 +81,7 @@ RUN rm -rf ./reporter EXPOSE 12345 -CMD ["pnpm", "start:docker"] +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost:12345/health || exit 1 + +CMD ["/usr/local/bin/start-tianji-container.sh"] diff --git a/README.md b/README.md index 8c54f5691..c0b10b930 100644 --- a/README.md +++ b/README.md @@ -27,26 +27,39 @@ It's good to specialize in one thing, if we are experts in related abilities we - [x] website analysis - [x] monitor + - [x] support passive reception of results - [x] server status - [x] problem notification - [x] telemetry - [x] openapi - [x] website -- [ ] team collaboration -- [ ] utm track -- [ ] waitlist -- [ ] survey -- [ ] lighthouse report -- [ ] hooks -- [ ] links +- [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/preview1.png) +![](./website/static/img/preview/1.png) -![](./website/static/img/preview2.png) +![](./website/static/img/preview/2.png) -![](./website/static/img/preview3.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 @@ -54,7 +67,7 @@ It's good to specialize in one thing, if we are experts in related abilities we modify those file: - `src/client/i18next-toolkit.config.cjs` in this file, edit country code -- `src/client/utils/constants.ts` in this file, add for display +- `src/client/utils/i18n.ts` in this file, add for display Then, run below code to auto generate @@ -79,8 +92,12 @@ And its inspired by `umami` license which under `MIT` and `uptime-kuma` which un ### 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.zh.md b/README.zh.md deleted file mode 100644 index 93182c446..000000000 --- a/README.zh.md +++ /dev/null @@ -1,36 +0,0 @@ -# 天机 Tianji - - - -**All-in-One 的数据洞察中心** - -`网站分析器` + `状态监控器` + `服务状态上报` = `Tianji` - -所有一切都在一起! - -## Motivation - -在我们对网站进行观察时。我们往往需要多个应用一起来组合使用。比如我们需要ga/umami等分析工具来查看pvuv以及各个页面的访问量,我们需要uptime监控器来检查服务器的网络质量与连通性,我们需要通关prometheus获取服务端上报的状态来检查服务器的质量。另外如果开发的是一个允许被开源部署的应用,我们往往还需要一个遥测系统来帮助我们对其他人的部署情况做一个最简单的信息收集。 - -我认为这些工具应当是为同一个目的而服务的,那么有没有一款应用能够轻量级的将这些常见的需求整合为一体呢?毕竟在大部分时候我们并不需要非常专业与深入的功能。但是我为了实现全面的监控却需要安装如此多的服务。 - -专精于一项这很好,如果我们是相关能力的专家我们需要这样的专业工具。但是对于大部分只有轻量级需求的用户而言,一个all in one的应用会更加方便与易于使用 - - -## Preview - -![](./website/static/img/preview1.png) - -![](./website/static/img/preview2.png) - -![](./website/static/img/preview3.png) - -## 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 Sealos](https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://bja.sealos.run/?openapp=system-template%3FtemplateName%3Dtianji) diff --git a/apps/appstore-review/.gitignore b/apps/appstore-review/.gitignore new file mode 100644 index 000000000..aefa952f8 --- /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 000000000..0d7a5abc4 --- /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 000000000..b8da3337d --- /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 000000000..58b777c62 --- /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 000000000..522c0cf22 --- /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 000000000..a94002297 --- /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 000000000..da7a19fc5 --- /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 000000000..97babed86 --- /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 000000000..afe9dfad6 --- /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 000000000..1dea2186c --- /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 000000000..689e64be9 --- /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 000000000..1e6aec687 --- /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 000000000..d55990a5f --- /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 000000000..cdf45c1e7 --- /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 000000000..a727df347 --- /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 000000000..3b0fe33c4 --- /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 000000000..5c7b5d3c7 --- /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 000000000..1949f1fc0 --- /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 000000000..a50721e91 --- /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 000000000..48cac211b --- /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 000000000..1d7b108bd --- /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 000000000..7c4f23979 --- /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 000000000..fbee335d7 --- /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 000000000..7fc436289 --- /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 000000000..33bdb791a --- /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 000000000..977f64c4e --- /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 000000000..f5d3ca945 --- /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 000000000..c3e6ecdf2 --- /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 000000000..2754949fe --- /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 000000000..83ab76401 --- /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 000000000..a50f51ec9 --- /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 000000000..95d825672 --- /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 000000000..f429adde0 --- /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 000000000..c036d2e54 --- /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 000000000..494018e62 --- /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 000000000..a065c8b51 --- /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 000000000..d4bab72bb --- /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 000000000..faca33fe7 --- /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/apps/openclaw-skill/SKILL.md b/apps/openclaw-skill/SKILL.md new file mode 100644 index 000000000..7e32967d3 --- /dev/null +++ b/apps/openclaw-skill/SKILL.md @@ -0,0 +1,124 @@ +--- +name: tianji +description: > + Query website analytics, monitor uptime, survey results, telemetry data, + feed events, application stats, and more from the Tianji platform via its + read-only OpenAPI (69 GET endpoints across 14 service domains). + Use when the user asks about website traffic, pageviews, monitor status, + survey feedback, telemetry events, feed channels, billing usage, + or any Tianji platform data. +--- + +# Tianji Analytics + +Query any read-only data from the Tianji monitoring and analytics platform. + +## Configuration + +Three values are required (provided via skill config): + +| Variable | Description | +|----------|-------------| +| `TIANJI_BASE_URL` | Tianji instance URL (e.g. `https://tianji.example.com`) | +| `TIANJI_API_KEY` | API key for authentication | +| `TIANJI_WORKSPACE_ID` | Default workspace ID | + +## Making API Requests + +All endpoints are under `{TIANJI_BASE_URL}/open` and require a Bearer token: + +```bash +curl -H "Authorization: Bearer {TIANJI_API_KEY}" \ + "{TIANJI_BASE_URL}/open/workspace/{TIANJI_WORKSPACE_ID}/website/all" +``` + +Only GET requests are allowed. All responses are JSON. + +## Service Domains + +Find the exact endpoint and parameters in [api-endpoints.md](references/api-endpoints.md). +For full parameter schemas, consult [openapi-readonly.json](references/openapi-readonly.json). + +| Domain | Endpoints | Typical Questions | +|--------|-----------|-------------------| +| **Website** | 13 | Traffic stats, pageviews, geo distribution, Lighthouse scores | +| **Monitor** | 9 | Uptime status, recent check data, monitor events | +| **Survey** | 8 | Survey responses, result stats, AI categories | +| **Telemetry** | 7 | Custom event counts, telemetry pageviews, metrics | +| **Billing** | 7 | Usage quotas, subscription tier, credit balance | +| **Feed** | 6 | Feed channels, event streams, feed states | +| **Application** | 5 | App store reviews, app info, event stats | +| **AI/AIGateway** | 5 | Gateway logs, model pricing, quota alerts | +| **Worker** | 3 | Worker list, worker details, revisions | +| **Page** | 2 | Status pages | +| **Workspace** | 2 | Members, service counts | +| **Global** | 1 | Platform configuration | +| **AuditLog** | 1 | Audit trail | + +## Workflow + +1. Identify the service domain from the user's question +2. Read [api-endpoints.md](references/api-endpoints.md) to find the endpoint +3. Construct the GET request with required path/query parameters +4. Parse the JSON response and summarize for the user + +## Common Scenarios + +### Website traffic overview + +``` +GET /open/workspace/{workspaceId}/website/all +``` +Pick the target website ID, then: +``` +GET /open/workspace/{workspaceId}/website/{websiteId}/stats?startAt={timestamp}&endAt={timestamp} +``` + +### Monitor health check + +``` +GET /open/workspace/{workspaceId}/monitor/all +``` +Pick the target monitor ID, then: +``` +GET /open/workspace/{workspaceId}/monitor/{monitorId}/get +GET /open/workspace/{workspaceId}/monitor/{monitorId}/status +``` + +### Survey results analysis + +``` +GET /open/workspace/{workspaceId}/survey/all +``` +Pick the target survey ID, then: +``` +GET /open/workspace/{workspaceId}/survey/{surveyId}/result/list?startAt={timestamp}&endAt={timestamp}&limit=50 +GET /open/workspace/{workspaceId}/survey/{surveyId}/stats?startAt={timestamp}&endAt={timestamp} +``` + +### Feed event inspection + +``` +GET /open/workspace/{workspaceId}/feed/channels +``` +Pick the channel ID, then: +``` +GET /open/workspace/{workspaceId}/feed/{channelId}/fetchEventsByCursor?limit=20 +``` + +## Sensitive Data Handling + +Some GET endpoints may return fields containing platform-stored secrets (e.g. `modelApiKey`, +`customModelBaseUrl` in AI Gateway responses). Additionally, endpoints like workspace members, +audit logs, and billing may contain PII or internal details. + +**Rules:** +- NEVER display `modelApiKey`, `apiKey`, `secret`, `token`, `password`, or `credential` fields to the user +- Redact or omit these fields when summarizing API responses +- When querying workspace members or audit logs, only surface non-sensitive metadata (names, roles, timestamps) unless the user explicitly requests full detail + +## Notes + +- Timestamps use milliseconds since epoch (e.g. `1704067200000` for 2024-01-01) +- Pagination: some endpoints use `cursor` parameter; check the response for `nextCursor` +- The `type` parameter in website metrics accepts: `url`, `language`, `referrer`, `title`, `browser`, `os`, `device`, `country`, `event` diff --git a/apps/openclaw-skill/build.sh b/apps/openclaw-skill/build.sh new file mode 100755 index 000000000..92b0a227f --- /dev/null +++ b/apps/openclaw-skill/build.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +node "$SCRIPT_DIR/scripts/filter-openapi.cjs" "$@" diff --git a/apps/openclaw-skill/clawhub.json b/apps/openclaw-skill/clawhub.json new file mode 100644 index 000000000..8d6f174d3 --- /dev/null +++ b/apps/openclaw-skill/clawhub.json @@ -0,0 +1,24 @@ +{ + "name": "tianji-analytics", + "tagline": "Query all read-only data from Tianji monitoring & analytics platform", + "category": "analytics", + "tags": [ + "monitoring", + "analytics", + "website", + "survey", + "uptime", + "telemetry", + "feed" + ], + "license": "MIT", + "author": "moonrailgun", + "repository": "https://github.com/msgbyte/tianji", + "requiredEnvVars": [ + "TIANJI_BASE_URL", + "TIANJI_API_KEY", + "TIANJI_WORKSPACE_ID" + ], + "primaryCredential": "TIANJI_API_KEY", + "sensitiveDataNote": "Some GET endpoints (e.g. aiGateway) may return platform-stored secrets such as modelApiKey. The skill instructs the agent to redact these fields before presenting results to the user." +} diff --git a/apps/openclaw-skill/references/api-endpoints.md b/apps/openclaw-skill/references/api-endpoints.md new file mode 100644 index 000000000..0dcc17bec --- /dev/null +++ b/apps/openclaw-skill/references/api-endpoints.md @@ -0,0 +1,146 @@ +# Tianji API Endpoints (GET-only) + +Base path: `{TIANJI_BASE_URL}/open` +Auth: `Authorization: Bearer {TIANJI_API_KEY}` +Timestamps: milliseconds since epoch (e.g. `1704067200000`) + +--- + +## Website (13) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /workspace/{workspaceId}/website/{websiteId}/onlineCount` | Get online count | — | +| `GET /workspace/{workspaceId}/website/all` | Get all websites | — | +| `GET /workspace/{workspaceId}/website/allOverview` | Get overview | — | +| `GET /workspace/{workspaceId}/website/{websiteId}/info` | Get website info | — | +| `GET /workspace/{workspaceId}/website/{websiteId}/stats` | Get stats | `startAt (query)`, `endAt (query)`, `unit? (query)`, `url? (query)`, `country? (query)`, `region? (query)`, `city? (query)`, `timezone? (query)`, `referrer? (query)`, `title? (query)`, `os? (query)`, `browser? (query)`, `device? (query)` | +| `GET /workspace/{workspaceId}/website/{websiteId}/geoStats` | Get geo stats | `startAt (query)`, `endAt (query)` | +| `GET /workspace/{workspaceId}/website/{websiteId}/pageviews` | Get pageviews | `startAt (query)`, `endAt (query)`, `unit? (query)`, `url? (query)`, `country? (query)`, `region? (query)`, `city? (query)`, `timezone? (query)`, `referrer? (query)`, `title? (query)`, `os? (query)`, `browser? (query)`, `device? (query)` | +| `GET /workspace/{workspaceId}/website/{websiteId}/metrics` | Get metrics | `type (query) enum:url|language|referrer|title|browser|os|device|country|event|utm_source|utm_medium|utm_campaign|utm_term|utm_content`, `startAt (query)`, `endAt (query)`, `url? (query)`, `referrer? (query)`, `title? (query)`, `os? (query)`, `browser? (query)`, `device? (query)`, `country? (query)`, `region? (query)`, `city? (query)`, `language? (query)`, `event? (query)`, `utm_source? (query)`, `utm_medium? (query)`, `utm_campaign? (query)`, `utm_term? (query)`, `utm_content? (query)` | +| `GET /workspace/{workspaceId}/website/{websiteId}/getLighthouseReport` | Get Lighthouse report | `limit? (query) default:10`, `cursor? (query)` | +| `GET /lighthouse/{lighthouseId}` | Get Lighthouse JSON | — | +| `GET /website/public/{shareId}/info` | Get public info | — | +| `GET /website/public/{shareId}/stats` | Get public stats | `range? (query) enum:realtime|24h|7d|30d|90d default:24h` | +| `GET /website/public/{shareId}/metrics` | Get public metrics | `type (query) enum:url|language|referrer|title|browser|os|device|country|event|utm_source|utm_medium|utm_campaign|utm_term|utm_content`, `range? (query) enum:realtime|24h|7d|30d|90d default:24h` | + +## Monitor (9) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /workspace/{workspaceId}/monitor/all` | Get all monitors | — | +| `GET /workspace/{workspaceId}/monitor/{monitorId}/get` | Get monitor | — | +| `GET /workspace/{workspaceId}/monitor/{monitorId}/data` | Get data | `startAt (query)`, `endAt (query)` | +| `GET /workspace/{workspaceId}/monitor/{monitorId}/recentData` | Get recent data | `take (query)` | +| `GET /workspace/{workspaceId}/monitor/{monitorId}/publicSummary` | Get public summary | — | +| `GET /workspace/{workspaceId}/monitor/{monitorId}/publicData` | Get public data | — | +| `GET /workspace/{workspaceId}/monitor/{monitorId}/dataMetrics` | Get data metrics | — | +| `GET /workspace/{workspaceId}/monitor/events` | Get events | `monitorId? (query)`, `limit? (query) default:20` | +| `GET /workspace/{workspaceId}/monitor/{monitorId}/status` | Get status | `statusName (query)` | + +## Survey (8) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /workspace/{workspaceId}/survey/all` | Get all surveys | — | +| `GET /workspace/{workspaceId}/survey/{surveyId}/get` | Get survey | — | +| `GET /workspace/{workspaceId}/survey/{surveyId}/count` | Get result count | — | +| `GET /workspace/{workspaceId}/survey/allResultCount` | Get all result counts | — | +| `GET /workspace/{workspaceId}/survey/result/{resultId}` | Get result | — | +| `GET /workspace/{workspaceId}/survey/{surveyId}/result/list` | Get result list | `limit? (query) default:50`, `cursor? (query)`, `startAt? (query)`, `endAt? (query)`, `filter? (query)` | +| `GET /workspace/{workspaceId}/survey/{surveyId}/stats` | Get stats | `startAt? (query)`, `endAt? (query)` | +| `GET /workspace/{workspaceId}/survey/{surveyId}/aiCategoryList` | Get AI categories | — | + +## Telemetry (7) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /workspace/{workspaceId}/telemetry/all` | Get all telemetry | — | +| `GET /workspace/{workspaceId}/telemetry/info` | Get telemetry info | `telemetryId (query)` | +| `GET /workspace/{workspaceId}/telemetry/allEventCount` | Get all event count | — | +| `GET /workspace/{workspaceId}/telemetry/eventCount` | Get event count | `telemetryId (query)` | +| `GET /workspace/{workspaceId}/telemetry/pageviews` | Get pageviews | `telemetryId (query)`, `startAt (query)`, `endAt (query)`, `unit? (query)`, `url? (query)`, `country? (query)`, `region? (query)`, `city? (query)`, `timezone? (query)` | +| `GET /workspace/{workspaceId}/telemetry/metrics` | Get metrics | `telemetryId (query)`, `type (query) enum:source|url|event|referrer|country`, `startAt (query)`, `endAt (query)`, `url? (query)`, `country? (query)`, `region? (query)`, `city? (query)`, `timezone? (query)` | +| `GET /workspace/{workspaceId}/telemetry/stats` | Get stats | `telemetryId (query)`, `startAt (query)`, `endAt (query)`, `unit? (query)`, `url? (query)`, `country? (query)`, `region? (query)`, `city? (query)`, `timezone? (query)` | + +## Billing (7) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /billing/usage` | Get usage | `workspaceId (query)`, `startAt (query)`, `endAt (query)` | +| `GET /billing/limit` | Get limit | `workspaceId (query)` | +| `GET /billing/currentTier` | Get current tier | `workspaceId (query)` | +| `GET /billing/currentSubscription` | Get subscription | `workspaceId (query)` | +| `GET /billing/credit` | Get credit | `workspaceId (query)` | +| `GET /billing/credit/bills` | Get credit bills | `workspaceId (query)`, `page? (query) default:1`, `pageSize? (query) default:10` | +| `GET /billing/credit/packs` | Get credit packs | `workspaceId (query)` | + +## Feed (6) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /workspace/{workspaceId}/feed/channels` | Get all channels | — | +| `GET /workspace/{workspaceId}/feed/{channelId}/info` | Get channel info | — | +| `GET /workspace/{workspaceId}/feed/{channelId}/fetchEventsByCursor` | Fetch events | `limit? (query) default:50`, `cursor? (query)`, `archived? (query) default:false` | +| `GET /feed/public/{shareId}/events` | feed-fetchPublicEventsByCursor | `limit? (query) default:50`, `cursor? (query)` | +| `GET /feed/public/{shareId}/info` | feed-getChannelByShareId | — | +| `GET /workspace/{workspaceId}/feed/state/all` | Get all states | `channelId (query)`, `limit? (query) default:5` | + +## Application (5) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /workspace/{workspaceId}/application/all` | Get all applications | — | +| `GET /workspace/{workspaceId}/application/{applicationId}/info` | Get application info | — | +| `GET /application/storeAppSearch` | Search store apps | `workspaceId (query)`, `keyword (query)`, `storeType (query) enum:appstore|googleplay` | +| `GET /workspace/{workspaceId}/application/{applicationId}/storeInfoHistory` | Get store info history | `storeType (query) enum:appstore|googleplay`, `storeId? (query)`, `startAt (query)`, `endAt (query)` | +| `GET /workspace/{workspaceId}/application/{applicationId}/eventStats` | Get event stats | `startAt (query)`, `endAt (query)`, `timezone? (query)`, `unit? (query) enum:minute|hour|day|month|year` | + +## AIGateway (1) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /workspace/{workspaceId}/aiGateway/all` | Get all gateways | — | + +## AI (4) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /aiGateway/info` | Get gateway info | `workspaceId (query)`, `gatewayId (query)` | +| `GET /aiGateway/logs` | Get gateway logs | `workspaceId (query)`, `gatewayId (query)`, `cursor? (query)`, `limit? (query) default:20`, `logId? (query)` | +| `GET /aiGateway/model-pricing` | Get model pricing | `workspaceId (query)`, `search? (query)`, `limit? (query) default:10` | +| `GET /aiGateway/quota-alert` | Get quota alert | `workspaceId (query)`, `gatewayId (query)` | + +## Worker (3) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /workspace/{workspaceId}/worker/all` | Get all workers in workspace | — | +| `GET /workspace/{workspaceId}/worker/{workerId}/info` | Get worker by ID | — | +| `GET /workspace/{workspaceId}/worker/{workerId}/revisions` | Get worker revisions | — | + +## Page (2) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /workspace/{workspaceId}/page/all` | Get all pages | `type? (query) enum:status|static|all default:all` | +| `GET /page/{slug}` | Get page info | — | + +## Workspace (2) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /workspace/{workspaceId}/members` | Get members | — | +| `GET /workspace/{workspaceId}/getServiceCount` | Get service count | — | + +## Global (1) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /global/config` | Get global config | — | + +## AuditLog (1) + +| Endpoint | Summary | Key Params | +|----------|---------|------------| +| `GET /audit/fetchByCursor` | Fetch audit log | `workspaceId (query)`, `limit? (query) default:50`, `cursor? (query)` | diff --git a/apps/openclaw-skill/references/openapi-readonly.json b/apps/openclaw-skill/references/openapi-readonly.json new file mode 100644 index 000000000..740dc7ed5 --- /dev/null +++ b/apps/openclaw-skill/references/openapi-readonly.json @@ -0,0 +1,11216 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Tianji OpenAPI (Read-Only)", + "description": "Filtered OpenAPI spec containing only GET endpoints for safe read-only access.", + "version": "v1.30.25" + }, + "servers": [ + { + "url": "/open" + } + ], + "paths": { + "/workspace/{workspaceId}/aiGateway/all": { + "get": { + "operationId": "aiGateway-all", + "summary": "Get all gateways", + "tags": [ + "AIGateway" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "modelApiKey": { + "type": "string", + "description": "[REDACTED] This field may contain sensitive data and should not be displayed." + }, + "customModelBaseUrl": { + "type": "string", + "description": "[REDACTED] This field may contain sensitive data and should not be displayed." + }, + "customModelName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "customModelInputPrice": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "customModelOutputPrice": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "workspaceId", + "name", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/aiGateway/info": { + "get": { + "operationId": "aiGateway-info", + "summary": "Get gateway info", + "tags": [ + "AI" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "gatewayId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "modelApiKey": { + "type": "string", + "description": "[REDACTED] This field may contain sensitive data and should not be displayed." + }, + "customModelBaseUrl": { + "type": "string", + "description": "[REDACTED] This field may contain sensitive data and should not be displayed." + }, + "customModelName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "customModelInputPrice": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "customModelOutputPrice": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "workspaceId", + "name", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/aiGateway/logs": { + "get": { + "operationId": "aiGateway-logs", + "summary": "Get gateway logs", + "tags": [ + "AI" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "gatewayId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "cursor", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "default": 20, + "type": "integer", + "minimum": 1, + "maximum": 100 + } + }, + { + "in": "query", + "name": "logId", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "gatewayId": { + "type": "string" + }, + "inputToken": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "outputToken": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "stream": { + "type": "boolean" + }, + "modelName": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "Pending", + "Success", + "Failed" + ] + }, + "duration": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "ttft": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "price": { + "type": "number" + }, + "userId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "requestPayload": {}, + "responsePayload": {} + }, + "required": [ + "id", + "workspaceId", + "gatewayId", + "inputToken", + "outputToken", + "stream", + "modelName", + "status", + "duration", + "ttft", + "price", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + }, + "nextCursor": { + "type": "string" + } + }, + "required": [ + "items" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/aiGateway/model-pricing": { + "get": { + "operationId": "aiGateway-modelPricing", + "summary": "Get model pricing", + "tags": [ + "AI" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "search", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "default": 10, + "type": "integer", + "minimum": 1, + "maximum": 50 + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "providers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "api": { + "type": "string" + }, + "doc": { + "type": "string" + }, + "models": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "attachment": { + "type": "boolean" + }, + "reasoning": { + "type": "boolean" + }, + "temperature": { + "type": "boolean" + }, + "tool_call": { + "type": "boolean" + }, + "knowledge": { + "type": "string" + }, + "release_date": { + "type": "string" + }, + "last_updated": { + "type": "string" + }, + "modalities": { + "type": "object", + "properties": { + "input": { + "type": "array", + "items": { + "type": "string" + } + }, + "output": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "open_weights": { + "type": "boolean" + }, + "cost": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cache_read": { + "type": "number" + } + }, + "additionalProperties": false + }, + "limit": { + "type": "object", + "properties": { + "context": { + "type": "number" + }, + "output": { + "type": "number" + } + }, + "additionalProperties": false + } + }, + "required": [ + "id", + "name" + ], + "additionalProperties": false + } + } + }, + "required": [ + "id", + "name", + "models" + ], + "additionalProperties": false + } + } + }, + "required": [ + "providers" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/aiGateway/quota-alert": { + "get": { + "operationId": "aiGateway-quotaAlert-get", + "summary": "Get quota alert", + "tags": [ + "AI" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "gatewayId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "dailyQuota": { + "type": "number" + }, + "enabled": { + "type": "boolean" + }, + "notificationId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "lastAlertSentAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "alertLevel80Sent": { + "type": "boolean" + }, + "alertLevel100Sent": { + "type": "boolean" + }, + "alertLevel150Sent": { + "type": "boolean" + } + }, + "required": [ + "id", + "dailyQuota", + "enabled", + "notificationId", + "lastAlertSentAt", + "alertLevel80Sent", + "alertLevel100Sent", + "alertLevel150Sent" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/global/config": { + "get": { + "operationId": "global-config", + "summary": "Get global config", + "description": "Get Tianji system global config", + "tags": [ + "Global" + ], + "security": [ + { + "Authorization": [] + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "allowRegister": { + "type": "boolean" + }, + "websiteId": { + "type": "string" + }, + "amapToken": { + "type": "string" + }, + "mapboxToken": { + "type": "string" + }, + "alphaMode": { + "type": "boolean" + }, + "disableAnonymousTelemetry": { + "type": "boolean" + }, + "customTrackerScriptName": { + "type": "string" + }, + "serverTimezone": { + "type": "string" + }, + "authProvider": { + "type": "array", + "items": { + "type": "string" + } + }, + "customAuthProviderIcon": { + "type": "string" + }, + "smtpAvailable": { + "type": "boolean" + }, + "enableBilling": { + "type": "boolean" + }, + "ai": { + "type": "object", + "properties": { + "enable": { + "type": "boolean" + }, + "contextWindow": { + "type": "number" + } + }, + "required": [ + "enable", + "contextWindow" + ], + "additionalProperties": false + }, + "enableFunctionWorker": { + "type": "boolean" + }, + "observability": { + "type": "object", + "properties": { + "tianji": { + "type": "object", + "properties": { + "baseUrl": { + "type": "string" + }, + "websiteId": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "required": [ + "tianji" + ], + "additionalProperties": false + } + }, + "required": [ + "allowRegister", + "alphaMode", + "disableAnonymousTelemetry", + "authProvider", + "smtpAvailable", + "enableBilling", + "ai", + "enableFunctionWorker", + "observability" + ], + "additionalProperties": false + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/members": { + "get": { + "operationId": "workspace-members", + "summary": "Get members", + "tags": [ + "Workspace" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "userId": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "role": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "user": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "nickname": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "emailVerified": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "username", + "nickname", + "email", + "emailVerified" + ], + "additionalProperties": false + } + }, + "required": [ + "userId", + "workspaceId", + "role", + "createdAt", + "updatedAt", + "user" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/getServiceCount": { + "get": { + "operationId": "workspace-getServiceCount", + "summary": "Get service count", + "tags": [ + "Workspace" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "website": { + "type": "number" + }, + "application": { + "type": "number" + }, + "monitor": { + "type": "number" + }, + "server": { + "type": "number" + }, + "telemetry": { + "type": "number" + }, + "page": { + "type": "number" + }, + "survey": { + "type": "number" + }, + "feed": { + "type": "number" + }, + "shortLink": { + "type": "number" + }, + "aiGateway": { + "type": "number" + }, + "functionWorker": { + "type": "number" + } + }, + "required": [ + "website", + "application", + "monitor", + "server", + "telemetry", + "page", + "survey", + "feed", + "shortLink", + "aiGateway", + "functionWorker" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/website/{websiteId}/onlineCount": { + "get": { + "operationId": "website-onlineCount", + "summary": "Get online count", + "tags": [ + "Website" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "websiteId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "number" + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/website/all": { + "get": { + "operationId": "website-all", + "summary": "Get all websites", + "tags": [ + "Website" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "domain": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "shareId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resetAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "monitorId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "deletedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "workspaceId", + "name", + "domain", + "shareId", + "resetAt", + "monitorId", + "createdAt", + "updatedAt", + "deletedAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/website/allOverview": { + "get": { + "operationId": "website-allOverview", + "summary": "Get overview", + "tags": [ + "Website" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "number" + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/website/{websiteId}/info": { + "get": { + "operationId": "website-info", + "summary": "Get website info", + "tags": [ + "Website" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "websiteId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "domain": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "shareId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "resetAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "monitorId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "deletedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "workspaceId", + "name", + "domain", + "shareId", + "resetAt", + "monitorId", + "createdAt", + "updatedAt", + "deletedAt" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/website/{websiteId}/stats": { + "get": { + "operationId": "website-stats", + "summary": "Get stats", + "tags": [ + "Website" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "websiteId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "unit", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "url", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "country", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "region", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "city", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "timezone", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "referrer", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "title", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "os", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "browser", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "device", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "pageviews": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "prev": { + "type": "number" + } + }, + "required": [ + "value", + "prev" + ], + "additionalProperties": false + }, + "uniques": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "prev": { + "type": "number" + } + }, + "required": [ + "value", + "prev" + ], + "additionalProperties": false + }, + "totaltime": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "prev": { + "type": "number" + } + }, + "required": [ + "value", + "prev" + ], + "additionalProperties": false + }, + "bounces": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "prev": { + "type": "number" + } + }, + "required": [ + "value", + "prev" + ], + "additionalProperties": false + } + }, + "required": [ + "pageviews", + "uniques", + "totaltime", + "bounces" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/website/{websiteId}/geoStats": { + "get": { + "operationId": "website-geoStats", + "summary": "Get geo stats", + "tags": [ + "Website" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "websiteId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "longitude": { + "type": "number" + }, + "latitude": { + "type": "number" + }, + "count": { + "type": "number" + } + }, + "required": [ + "longitude", + "latitude", + "count" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/website/{websiteId}/pageviews": { + "get": { + "operationId": "website-pageviews", + "summary": "Get pageviews", + "tags": [ + "Website" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "websiteId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "unit", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "url", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "country", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "region", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "city", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "timezone", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "referrer", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "title", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "os", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "browser", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "device", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "pageviews": {}, + "sessions": {} + }, + "required": [ + "pageviews", + "sessions" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/website/{websiteId}/metrics": { + "get": { + "operationId": "website-metrics", + "summary": "Get metrics", + "tags": [ + "Website" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "websiteId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "type", + "schema": { + "type": "string", + "enum": [ + "url", + "language", + "referrer", + "title", + "browser", + "os", + "device", + "country", + "event", + "utm_source", + "utm_medium", + "utm_campaign", + "utm_term", + "utm_content" + ] + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "url", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "referrer", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "title", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "os", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "browser", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "device", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "country", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "region", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "city", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "language", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "event", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "utm_source", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "utm_medium", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "utm_campaign", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "utm_term", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "utm_content", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "x": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "y": { + "type": "number" + } + }, + "required": [ + "x", + "y" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/website/{websiteId}/getLighthouseReport": { + "get": { + "operationId": "website-getLighthouseReport", + "summary": "Get Lighthouse report", + "tags": [ + "Website" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "websiteId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "limit", + "schema": { + "default": 10, + "type": "number", + "minimum": 1, + "maximum": 100 + } + }, + { + "in": "query", + "name": "cursor", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "Pending", + "Success", + "Failed" + ] + }, + "url": { + "type": "string" + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "id", + "status", + "url", + "createdAt" + ], + "additionalProperties": false + } + }, + "nextCursor": { + "type": "string" + } + }, + "required": [ + "items" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/lighthouse/{lighthouseId}": { + "get": { + "operationId": "website-getLighthouseJSON", + "summary": "Get Lighthouse JSON", + "tags": [ + "Website" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "lighthouseId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/website/public/{shareId}/info": { + "get": { + "operationId": "website-getPublicInfoByShareId", + "summary": "Get public info", + "tags": [ + "Website" + ], + "parameters": [ + { + "in": "path", + "name": "shareId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "domain": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "shareId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/website/public/{shareId}/stats": { + "get": { + "operationId": "website-getPublicStatsByShareId", + "summary": "Get public stats", + "tags": [ + "Website" + ], + "parameters": [ + { + "in": "path", + "name": "shareId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "range", + "schema": { + "default": "24h", + "type": "string", + "enum": [ + "realtime", + "24h", + "7d", + "30d", + "90d" + ] + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "pageviews": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "prev": { + "type": "number" + } + }, + "required": [ + "value", + "prev" + ], + "additionalProperties": false + }, + "visitors": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "prev": { + "type": "number" + } + }, + "required": [ + "value", + "prev" + ], + "additionalProperties": false + }, + "bounce_rate": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "prev": { + "type": "number" + } + }, + "required": [ + "value", + "prev" + ], + "additionalProperties": false + }, + "average_visit_duration": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "prev": { + "type": "number" + } + }, + "required": [ + "value", + "prev" + ], + "additionalProperties": false + }, + "pageviews_trend": { + "type": "array", + "items": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "required": [ + "date", + "value" + ], + "additionalProperties": false + } + } + }, + "required": [ + "pageviews", + "visitors", + "bounce_rate", + "average_visit_duration", + "pageviews_trend" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/website/public/{shareId}/metrics": { + "get": { + "operationId": "website-getPublicMetricsByShareId", + "summary": "Get public metrics", + "tags": [ + "Website" + ], + "parameters": [ + { + "in": "path", + "name": "shareId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "type", + "schema": { + "type": "string", + "enum": [ + "url", + "language", + "referrer", + "title", + "browser", + "os", + "device", + "country", + "event", + "utm_source", + "utm_medium", + "utm_campaign", + "utm_term", + "utm_content" + ] + }, + "required": true + }, + { + "in": "query", + "name": "range", + "schema": { + "default": "24h", + "type": "string", + "enum": [ + "realtime", + "24h", + "7d", + "30d", + "90d" + ] + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "x": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "y": { + "type": "number" + }, + "ratio": { + "type": "number" + } + }, + "required": [ + "x", + "y", + "ratio" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/application/all": { + "get": { + "operationId": "application-all", + "summary": "Get all applications", + "tags": [ + "Application" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "deletedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "workspaceId", + "name", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/application/{applicationId}/info": { + "get": { + "operationId": "application-info", + "summary": "Get application info", + "tags": [ + "Application" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "applicationId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "deletedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "applicationStoreInfos": { + "type": "array", + "items": { + "type": "object", + "properties": { + "applicationId": { + "type": "string" + }, + "storeType": { + "type": "string" + }, + "storeId": { + "type": "string" + }, + "appId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "releaseNotes": { + "type": "string" + }, + "url": { + "type": "string" + }, + "downloads": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "score": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "ratingCount": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "reviews": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "size": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "applicationId", + "storeType", + "storeId", + "appId", + "title", + "description", + "releaseNotes", + "url", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + } + }, + "required": [ + "id", + "workspaceId", + "name", + "createdAt", + "updatedAt", + "applicationStoreInfos" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/application/storeAppSearch": { + "get": { + "operationId": "application-storeAppSearch", + "summary": "Search store apps", + "tags": [ + "Application" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "keyword", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "storeType", + "schema": { + "type": "string", + "enum": [ + "appstore", + "googleplay" + ] + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "appId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "icon": { + "type": "string" + } + }, + "required": [ + "appId", + "title", + "icon" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/application/{applicationId}/storeInfoHistory": { + "get": { + "operationId": "application-storeInfoHistory", + "summary": "Get store info history", + "tags": [ + "Application" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "applicationId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "storeType", + "schema": { + "type": "string", + "enum": [ + "appstore", + "googleplay" + ] + }, + "required": true + }, + { + "in": "query", + "name": "storeId", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "downloads": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "score": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "ratingCount": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "reviews": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "size": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "createdAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/application/{applicationId}/eventStats": { + "get": { + "operationId": "application-eventStats", + "summary": "Get event stats", + "tags": [ + "Application" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "applicationId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "timezone", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "unit", + "schema": { + "type": "string", + "enum": [ + "minute", + "hour", + "day", + "month", + "year" + ] + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "current": { + "type": "array", + "items": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "eventCount": { + "type": "number" + }, + "sessionCount": { + "type": "number" + }, + "totalTime": { + "type": "number" + }, + "avgEventsPerSession": { + "type": "number" + }, + "avgScreensPerSession": { + "type": "number" + } + }, + "required": [ + "date", + "eventCount", + "sessionCount", + "totalTime", + "avgEventsPerSession", + "avgScreensPerSession" + ], + "additionalProperties": false + } + }, + "previous": { + "type": "array", + "items": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "eventCount": { + "type": "number" + }, + "sessionCount": { + "type": "number" + }, + "totalTime": { + "type": "number" + }, + "avgEventsPerSession": { + "type": "number" + }, + "avgScreensPerSession": { + "type": "number" + } + }, + "required": [ + "date", + "eventCount", + "sessionCount", + "totalTime", + "avgEventsPerSession", + "avgScreensPerSession" + ], + "additionalProperties": false + } + }, + "currentTotalSessionCount": { + "type": "number" + }, + "previousTotalSessionCount": { + "type": "number" + } + }, + "required": [ + "current", + "previous", + "currentTotalSessionCount", + "previousTotalSessionCount" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/monitor/all": { + "get": { + "operationId": "monitor-all", + "summary": "Get all monitors", + "tags": [ + "Monitor" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "active": { + "type": "boolean" + }, + "interval": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "maxRetries": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "payload": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "trendingMode": { + "type": "boolean" + }, + "recentError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "upMessageTemplate": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "downMessageTemplate": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "notifications": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "additionalProperties": false + } + } + }, + "required": [ + "id", + "workspaceId", + "name", + "type", + "active", + "interval", + "maxRetries", + "payload", + "trendingMode", + "createdAt", + "updatedAt", + "notifications" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/monitor/{monitorId}/get": { + "get": { + "operationId": "monitor-get", + "summary": "Get monitor", + "tags": [ + "Monitor" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "monitorId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "active": { + "type": "boolean" + }, + "interval": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "maxRetries": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "payload": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "trendingMode": { + "type": "boolean" + }, + "recentError": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "upMessageTemplate": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "downMessageTemplate": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "notifications": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "additionalProperties": false + } + } + }, + "required": [ + "id", + "workspaceId", + "name", + "type", + "active", + "interval", + "maxRetries", + "payload", + "trendingMode", + "createdAt", + "updatedAt", + "notifications" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/monitor/{monitorId}/data": { + "get": { + "operationId": "monitor-data", + "summary": "Get data", + "tags": [ + "Monitor" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "monitorId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "value", + "createdAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/monitor/{monitorId}/recentData": { + "get": { + "operationId": "monitor-recentData", + "summary": "Get recent data", + "tags": [ + "Monitor" + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "monitorId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "take", + "schema": { + "type": "number" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "value", + "createdAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/monitor/{monitorId}/publicSummary": { + "get": { + "operationId": "monitor-publicSummary", + "summary": "Get public summary", + "tags": [ + "Monitor" + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "monitorId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "day": { + "type": "string" + }, + "totalCount": { + "type": "number" + }, + "upCount": { + "type": "number" + }, + "upRate": { + "type": "number" + } + }, + "required": [ + "day", + "totalCount", + "upCount", + "upRate" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/monitor/{monitorId}/publicData": { + "get": { + "operationId": "monitor-publicData", + "summary": "Get public data", + "tags": [ + "Monitor" + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "monitorId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "value", + "createdAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/monitor/{monitorId}/dataMetrics": { + "get": { + "operationId": "monitor-dataMetrics", + "summary": "Get data metrics", + "tags": [ + "Monitor" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "monitorId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "recent1DayAvg": { + "type": "number" + }, + "recent1DayOnlineCount": { + "type": "number" + }, + "recent1DayOfflineCount": { + "type": "number" + }, + "recent30DayOnlineCount": { + "type": "number" + }, + "recent30DayOfflineCount": { + "type": "number" + } + }, + "required": [ + "recent1DayAvg", + "recent1DayOnlineCount", + "recent1DayOfflineCount", + "recent30DayOnlineCount", + "recent30DayOfflineCount" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/monitor/events": { + "get": { + "operationId": "monitor-events", + "summary": "Get events", + "tags": [ + "Monitor" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "monitorId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "default": 20, + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "message": { + "type": "string" + }, + "monitorId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "id", + "message", + "monitorId", + "type", + "createdAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/monitor/{monitorId}/status": { + "get": { + "operationId": "monitor-getStatus", + "summary": "Get status", + "tags": [ + "Monitor" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "monitorId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "statusName", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "monitorId": { + "type": "string" + }, + "statusName": { + "type": "string" + }, + "payload": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + { + "type": "array", + "items": {} + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "number" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "monitorId", + "statusName", + "payload", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/page/all": { + "get": { + "operationId": "page-getAllPages", + "summary": "Get all pages", + "tags": [ + "Page" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "type", + "schema": { + "default": "all", + "type": "string", + "enum": [ + "status", + "static", + "all" + ] + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "body": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "monitorList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "showCurrent": { + "default": false, + "type": "boolean" + }, + "showDetail": { + "default": true, + "type": "boolean" + } + }, + "required": [ + "id" + ], + "additionalProperties": false + } + }, + "domain": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "const": "status" + } + }, + "required": [ + "id", + "workspaceId", + "slug", + "title", + "description", + "body", + "monitorList", + "createdAt", + "updatedAt", + "type" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "type": { + "type": "string", + "const": "static" + }, + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "domain": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "payload": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "workspaceId", + "type", + "slug", + "title", + "description", + "payload", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + ] + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/page/{slug}": { + "get": { + "operationId": "page-getPageInfo", + "summary": "Get page info", + "tags": [ + "Page" + ], + "parameters": [ + { + "in": "path", + "name": "slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "body": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "monitorList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "showCurrent": { + "default": false, + "type": "boolean" + }, + "showDetail": { + "default": true, + "type": "boolean" + } + }, + "required": [ + "id" + ], + "additionalProperties": false + } + }, + "domain": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "type": { + "type": "string", + "const": "status" + } + }, + "required": [ + "id", + "workspaceId", + "slug", + "title", + "description", + "body", + "monitorList", + "createdAt", + "updatedAt", + "type" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "type": { + "type": "string", + "const": "static" + }, + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "domain": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "payload": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "workspaceId", + "type", + "slug", + "title", + "description", + "payload", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + ] + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/telemetry/all": { + "get": { + "operationId": "telemetry-all", + "summary": "Get all telemetry", + "tags": [ + "Telemetry" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "deletedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "workspaceId", + "name", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/telemetry/info": { + "get": { + "operationId": "telemetry-info", + "summary": "Get telemetry info", + "tags": [ + "Telemetry" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "telemetryId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "deletedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "workspaceId", + "name", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/telemetry/allEventCount": { + "get": { + "operationId": "telemetry-allEventCount", + "summary": "Get all event count", + "tags": [ + "Telemetry" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "number" + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/telemetry/eventCount": { + "get": { + "operationId": "telemetry-eventCount", + "summary": "Get event count", + "tags": [ + "Telemetry" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "telemetryId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "number" + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/telemetry/pageviews": { + "get": { + "operationId": "telemetry-pageviews", + "summary": "Get pageviews", + "tags": [ + "Telemetry" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "telemetryId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "unit", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "url", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "country", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "region", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "city", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "timezone", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "pageviews": {}, + "sessions": {} + }, + "required": [ + "pageviews", + "sessions" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/telemetry/metrics": { + "get": { + "operationId": "telemetry-metrics", + "summary": "Get metrics", + "tags": [ + "Telemetry" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "telemetryId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "type", + "schema": { + "type": "string", + "enum": [ + "source", + "url", + "event", + "referrer", + "country" + ] + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "url", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "country", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "region", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "city", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "timezone", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "x": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "y": { + "type": "number" + } + }, + "required": [ + "x", + "y" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/telemetry/stats": { + "get": { + "operationId": "telemetry-stats", + "summary": "Get stats", + "tags": [ + "Telemetry" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "telemetryId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "unit", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "url", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "country", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "region", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "city", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "timezone", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "pageviews": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "prev": { + "type": "number" + } + }, + "required": [ + "value", + "prev" + ], + "additionalProperties": false + }, + "uniques": { + "type": "object", + "properties": { + "value": { + "type": "number" + }, + "prev": { + "type": "number" + } + }, + "required": [ + "value", + "prev" + ], + "additionalProperties": false + } + }, + "required": [ + "pageviews", + "uniques" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/survey/all": { + "get": { + "operationId": "survey-all", + "summary": "Get all surveys", + "description": "Get all surveys", + "tags": [ + "Survey" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "desc": { + "type": "string" + }, + "payload": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "text", + "select", + "email", + "imageUrl", + "hidden" + ] + }, + "options": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "label", + "name", + "type" + ], + "additionalProperties": false + } + } + }, + "required": [ + "items" + ], + "additionalProperties": false + }, + "feedChannelIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "feedTemplate": { + "type": "string" + }, + "webhookUrl": { + "type": "string" + }, + "recentSuggestionCategory": { + "type": "array", + "items": { + "type": "string" + } + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "workspaceId", + "name", + "desc", + "payload", + "feedChannelIds", + "feedTemplate", + "webhookUrl", + "recentSuggestionCategory", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/survey/{surveyId}/get": { + "get": { + "operationId": "survey-get", + "summary": "Get survey", + "description": "Get a specific survey by ID", + "tags": [ + "Survey" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "surveyId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "desc": { + "type": "string" + }, + "payload": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "text", + "select", + "email", + "imageUrl", + "hidden" + ] + }, + "options": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "label", + "name", + "type" + ], + "additionalProperties": false + } + } + }, + "required": [ + "items" + ], + "additionalProperties": false + }, + "feedChannelIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "feedTemplate": { + "type": "string" + }, + "webhookUrl": { + "type": "string" + }, + "recentSuggestionCategory": { + "type": "array", + "items": { + "type": "string" + } + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "workspaceId", + "name", + "desc", + "payload", + "feedChannelIds", + "feedTemplate", + "webhookUrl", + "recentSuggestionCategory", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/survey/{surveyId}/count": { + "get": { + "operationId": "survey-count", + "summary": "Get result count", + "description": "Get the total count of survey results", + "tags": [ + "Survey" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "surveyId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "number" + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/survey/allResultCount": { + "get": { + "operationId": "survey-allResultCount", + "summary": "Get all result counts", + "description": "Get result counts for all surveys in the workspace", + "tags": [ + "Survey" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "number" + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/survey/result/{resultId}": { + "get": { + "operationId": "survey-getResult", + "summary": "Get result", + "description": "Get a specific survey result by result ID", + "tags": [ + "Survey" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "resultId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "surveyId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "sessionId": { + "type": "string" + }, + "payload": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "browser": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "os": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ip": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "country": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "subdivision1": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "subdivision2": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "city": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "longitude": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "latitude": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "accuracyRadius": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "aiCategory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "aiTranslation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "surveyId", + "createdAt", + "sessionId", + "payload" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/survey/{surveyId}/result/list": { + "get": { + "operationId": "survey-resultList", + "summary": "Get result list", + "description": "Get paginated list of survey results with optional date range and filters", + "tags": [ + "Survey" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "surveyId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "limit", + "schema": { + "default": 50, + "type": "number", + "minimum": 1, + "maximum": 1000 + } + }, + { + "in": "query", + "name": "cursor", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + } + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + } + }, + { + "in": "query", + "name": "filter", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "surveyId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "sessionId": { + "type": "string" + }, + "payload": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "browser": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "os": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "ip": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "country": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "subdivision1": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "subdivision2": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "city": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "longitude": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "latitude": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "accuracyRadius": { + "anyOf": [ + { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + { + "type": "null" + } + ] + }, + "aiCategory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "aiTranslation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "surveyId", + "createdAt", + "sessionId", + "payload" + ], + "additionalProperties": false + } + }, + "nextCursor": { + "type": "string" + } + }, + "required": [ + "items" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/survey/{surveyId}/stats": { + "get": { + "operationId": "survey-stats", + "summary": "Get stats", + "description": "Get survey submission statistics grouped by date", + "tags": [ + "Survey" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "surveyId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + } + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "date": { + "type": "string" + }, + "count": { + "type": "number" + } + }, + "required": [ + "date", + "count" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/survey/{surveyId}/aiCategoryList": { + "get": { + "operationId": "survey-aiCategoryList", + "summary": "Get AI categories", + "description": "Get AI-categorized survey results with counts for each category", + "tags": [ + "Survey" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "surveyId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "count": { + "type": "number" + } + }, + "required": [ + "name", + "count" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/audit/fetchByCursor": { + "get": { + "operationId": "auditLog-fetchByCursor", + "summary": "Fetch audit log", + "description": "Fetch workspace audit log", + "tags": [ + "AuditLog" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "limit", + "schema": { + "default": 50, + "type": "number", + "minimum": 1, + "maximum": 100 + } + }, + { + "in": "query", + "name": "cursor", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "content": { + "type": "string" + }, + "relatedId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "relatedType": { + "anyOf": [ + { + "type": "string", + "enum": [ + "Monitor", + "Notification", + "Task", + "FunctionWorker" + ] + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "id", + "workspaceId", + "content", + "createdAt" + ], + "additionalProperties": false + } + }, + "nextCursor": { + "type": "string" + } + }, + "required": [ + "items" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/billing/usage": { + "get": { + "operationId": "billing-usage", + "summary": "Get usage", + "description": "get workspace usage", + "tags": [ + "Billing" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "startAt", + "schema": { + "type": "number" + }, + "required": true + }, + { + "in": "query", + "name": "endAt", + "schema": { + "type": "number" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "websiteAcceptedCount": { + "type": "number" + }, + "websiteEventCount": { + "type": "number" + }, + "monitorExecutionCount": { + "type": "number" + }, + "surveyCount": { + "type": "number" + }, + "feedEventCount": { + "type": "number" + } + }, + "required": [ + "websiteAcceptedCount", + "websiteEventCount", + "monitorExecutionCount", + "surveyCount", + "feedEventCount" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/billing/limit": { + "get": { + "operationId": "billing-limit", + "summary": "Get limit", + "description": "get workspace subscription limit", + "tags": [ + "Billing" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "maxWebsiteCount": { + "type": "number" + }, + "maxWebsiteEventCount": { + "type": "number" + }, + "maxMonitorExecutionCount": { + "type": "number" + }, + "maxSurveyCount": { + "type": "number" + }, + "maxFeedChannelCount": { + "type": "number" + }, + "maxFeedEventCount": { + "type": "number" + } + }, + "required": [ + "maxWebsiteCount", + "maxWebsiteEventCount", + "maxMonitorExecutionCount", + "maxSurveyCount", + "maxFeedChannelCount", + "maxFeedEventCount" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/billing/currentTier": { + "get": { + "operationId": "billing-currentTier", + "summary": "Get current tier", + "description": "get workspace current tier", + "tags": [ + "Billing" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "string", + "enum": [ + "FREE", + "PRO", + "TEAM", + "UNLIMITED" + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/billing/currentSubscription": { + "get": { + "operationId": "billing-currentSubscription", + "summary": "Get subscription", + "description": "get workspace current subscription", + "tags": [ + "Billing" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "subscriptionId": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "storeId": { + "type": "string" + }, + "productId": { + "type": "string" + }, + "variantId": { + "type": "string" + }, + "status": { + "type": "string" + }, + "cardBrand": { + "type": "string" + }, + "cardLastFour": { + "type": "string" + }, + "renewsAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "tier": { + "type": "string" + } + }, + "required": [ + "subscriptionId", + "workspaceId", + "storeId", + "productId", + "variantId", + "status", + "cardBrand", + "cardLastFour", + "renewsAt", + "createdAt", + "updatedAt", + "tier" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/billing/credit": { + "get": { + "operationId": "billing-credit", + "summary": "Get credit", + "description": "get workspace credit balance", + "tags": [ + "Billing" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "credit": { + "type": "number" + } + }, + "required": [ + "credit" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/billing/credit/bills": { + "get": { + "operationId": "billing-creditBills", + "summary": "Get credit bills", + "description": "list workspace credit bills", + "tags": [ + "Billing" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "default": 1, + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + } + }, + { + "in": "query", + "name": "pageSize", + "schema": { + "default": 10, + "type": "integer", + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "amount": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "id", + "workspaceId", + "type", + "amount", + "createdAt" + ], + "additionalProperties": false + } + }, + "total": { + "type": "number" + }, + "page": { + "type": "number" + }, + "pageSize": { + "type": "number" + } + }, + "required": [ + "list", + "total", + "page", + "pageSize" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/billing/credit/packs": { + "get": { + "operationId": "billing-creditPacks", + "summary": "Get credit packs", + "description": "list available credit packs", + "tags": [ + "Billing" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "variantId": { + "type": "string" + }, + "credit": { + "type": "number" + }, + "price": { + "type": "number" + }, + "currency": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "variantId", + "credit", + "price", + "currency" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/feed/channels": { + "get": { + "operationId": "feed-channels", + "summary": "Get all channels", + "tags": [ + "Feed" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "webhookSignature": { + "type": "string" + }, + "notifyFrequency": { + "type": "string", + "enum": [ + "none", + "event", + "day", + "week", + "month" + ] + }, + "publicShareId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "_count": { + "type": "object", + "properties": { + "events": { + "type": "number" + } + }, + "required": [ + "events" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "workspaceId", + "name", + "webhookSignature", + "notifyFrequency", + "createdAt", + "updatedAt", + "_count" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/feed/{channelId}/info": { + "get": { + "operationId": "feed-channelInfo", + "summary": "Get channel info", + "tags": [ + "Feed" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "channelId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "notificationIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "webhookSignature": { + "type": "string" + }, + "notifyFrequency": { + "type": "string", + "enum": [ + "none", + "event", + "day", + "week", + "month" + ] + }, + "publicShareId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "notificationIds", + "id", + "workspaceId", + "name", + "webhookSignature", + "notifyFrequency", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/feed/{channelId}/fetchEventsByCursor": { + "get": { + "operationId": "feed-fetchEventsByCursor", + "summary": "Fetch events", + "description": "Fetch workspace feed channel events", + "tags": [ + "Feed" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "channelId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "limit", + "schema": { + "default": 50, + "type": "number", + "minimum": 1, + "maximum": 100 + } + }, + { + "in": "query", + "name": "cursor", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "archived", + "schema": { + "default": false, + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "channelId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "eventName": { + "type": "string" + }, + "eventContent": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "source": { + "type": "string" + }, + "senderId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "important": { + "type": "boolean" + }, + "archived": { + "type": "boolean" + }, + "payload": { + "anyOf": [ + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "channelId", + "createdAt", + "updatedAt", + "eventName", + "eventContent", + "tags", + "source", + "important", + "archived" + ], + "additionalProperties": false + } + }, + "nextCursor": { + "type": "string" + } + }, + "required": [ + "items" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/feed/public/{shareId}/events": { + "get": { + "operationId": "feed-fetchPublicEventsByCursor", + "description": "Fetch public feed channel events by shareId", + "tags": [ + "Feed" + ], + "parameters": [ + { + "in": "path", + "name": "shareId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "limit", + "schema": { + "default": 50, + "type": "number", + "minimum": 1, + "maximum": 100 + } + }, + { + "in": "query", + "name": "cursor", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "channelId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "eventName": { + "type": "string" + }, + "eventContent": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "source": { + "type": "string" + }, + "senderId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "important": { + "type": "boolean" + }, + "archived": { + "type": "boolean" + }, + "payload": { + "anyOf": [ + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "channelId", + "createdAt", + "updatedAt", + "eventName", + "eventContent", + "tags", + "source", + "important", + "archived" + ], + "additionalProperties": false + } + }, + "nextCursor": { + "type": "string" + } + }, + "required": [ + "items" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/feed/public/{shareId}/info": { + "get": { + "operationId": "feed-getChannelByShareId", + "description": "Fetch public feed channel info by shareId", + "tags": [ + "Feed" + ], + "parameters": [ + { + "in": "path", + "name": "shareId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "publicShareId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "name" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/feed/state/all": { + "get": { + "operationId": "feed-state-all", + "summary": "Get all states", + "tags": [ + "Feed" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "query", + "name": "channelId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "limit", + "schema": { + "default": 5, + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "channelId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "eventId": { + "type": "string" + }, + "eventName": { + "type": "string" + }, + "eventContent": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "source": { + "type": "string" + }, + "senderId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "senderName": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "important": { + "type": "boolean" + }, + "status": { + "type": "string", + "enum": [ + "Ongoing", + "Resolved" + ] + }, + "resolvedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "payload": { + "anyOf": [ + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "channelId", + "createdAt", + "updatedAt", + "eventId", + "eventName", + "eventContent", + "tags", + "source", + "important", + "status" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/worker/all": { + "get": { + "operationId": "worker-all", + "summary": "Get all workers in workspace", + "tags": [ + "Worker" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "code": { + "type": "string" + }, + "revision": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "active": { + "type": "boolean" + }, + "enableCron": { + "type": "boolean" + }, + "cronExpression": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "visibility": { + "type": "string", + "enum": [ + "Public", + "Private" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "workspaceId", + "name", + "code", + "revision", + "active", + "enableCron", + "visibility", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/worker/{workerId}/info": { + "get": { + "operationId": "worker-get", + "summary": "Get worker by ID", + "tags": [ + "Worker" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "workerId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workspaceId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "code": { + "type": "string" + }, + "revision": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "active": { + "type": "boolean" + }, + "enableCron": { + "type": "boolean" + }, + "cronExpression": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "visibility": { + "type": "string", + "enum": [ + "Public", + "Private" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "workspaceId", + "name", + "code", + "revision", + "active", + "enableCron", + "visibility", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + }, + "/workspace/{workspaceId}/worker/{workerId}/revisions": { + "get": { + "operationId": "worker-getRevisions", + "summary": "Get worker revisions", + "tags": [ + "Worker" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "workspaceId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + }, + { + "in": "path", + "name": "workerId", + "schema": { + "type": "string", + "format": "cuid2", + "pattern": "^[0-9a-z]+$" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "workerId": { + "type": "string" + }, + "revision": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "code": { + "type": "string" + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "id", + "workerId", + "revision", + "code", + "createdAt" + ], + "additionalProperties": false + } + } + } + } + }, + "400": { + "description": "Invalid input data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.BAD_REQUEST" + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.UNAUTHORIZED" + } + } + } + }, + "403": { + "description": "Insufficient access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.FORBIDDEN" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.NOT_FOUND" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "error.BAD_REQUEST": { + "title": "Invalid input data error (400)", + "description": "The error information", + "example": { + "code": "BAD_REQUEST", + "message": "Invalid input data", + "issues": [] + }, + "type": "object", + "properties": { + "message": { + "description": "The error message", + "example": "Invalid input data", + "type": "string" + }, + "code": { + "description": "The error code", + "example": "BAD_REQUEST", + "type": "string" + }, + "issues": { + "description": "An array of issues that were responsible for the error", + "example": [], + "type": "array", + "items": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "additionalProperties": false + } + } + }, + "required": [ + "message", + "code" + ], + "additionalProperties": false + }, + "error.UNAUTHORIZED": { + "title": "Authorization not provided error (401)", + "description": "The error information", + "example": { + "code": "UNAUTHORIZED", + "message": "Authorization not provided", + "issues": [] + }, + "type": "object", + "properties": { + "message": { + "description": "The error message", + "example": "Authorization not provided", + "type": "string" + }, + "code": { + "description": "The error code", + "example": "UNAUTHORIZED", + "type": "string" + }, + "issues": { + "description": "An array of issues that were responsible for the error", + "example": [], + "type": "array", + "items": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "additionalProperties": false + } + } + }, + "required": [ + "message", + "code" + ], + "additionalProperties": false + }, + "error.FORBIDDEN": { + "title": "Insufficient access error (403)", + "description": "The error information", + "example": { + "code": "FORBIDDEN", + "message": "Insufficient access", + "issues": [] + }, + "type": "object", + "properties": { + "message": { + "description": "The error message", + "example": "Insufficient access", + "type": "string" + }, + "code": { + "description": "The error code", + "example": "FORBIDDEN", + "type": "string" + }, + "issues": { + "description": "An array of issues that were responsible for the error", + "example": [], + "type": "array", + "items": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "additionalProperties": false + } + } + }, + "required": [ + "message", + "code" + ], + "additionalProperties": false + }, + "error.NOT_FOUND": { + "title": "Not found error (404)", + "description": "The error information", + "example": { + "code": "NOT_FOUND", + "message": "Not found", + "issues": [] + }, + "type": "object", + "properties": { + "message": { + "description": "The error message", + "example": "Not found", + "type": "string" + }, + "code": { + "description": "The error code", + "example": "NOT_FOUND", + "type": "string" + }, + "issues": { + "description": "An array of issues that were responsible for the error", + "example": [], + "type": "array", + "items": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "additionalProperties": false + } + } + }, + "required": [ + "message", + "code" + ], + "additionalProperties": false + }, + "error.INTERNAL_SERVER_ERROR": { + "title": "Internal server error error (500)", + "description": "The error information", + "example": { + "code": "INTERNAL_SERVER_ERROR", + "message": "Internal server error", + "issues": [] + }, + "type": "object", + "properties": { + "message": { + "description": "The error message", + "example": "Internal server error", + "type": "string" + }, + "code": { + "description": "The error code", + "example": "INTERNAL_SERVER_ERROR", + "type": "string" + }, + "issues": { + "description": "An array of issues that were responsible for the error", + "example": [], + "type": "array", + "items": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "additionalProperties": false + } + } + }, + "required": [ + "message", + "code" + ], + "additionalProperties": false + } + }, + "securitySchemes": { + "Authorization": { + "type": "http", + "scheme": "bearer" + } + } + } +} \ No newline at end of file diff --git a/apps/openclaw-skill/scripts/filter-openapi.cjs b/apps/openclaw-skill/scripts/filter-openapi.cjs new file mode 100644 index 000000000..af9656331 --- /dev/null +++ b/apps/openclaw-skill/scripts/filter-openapi.cjs @@ -0,0 +1,192 @@ +#!/usr/bin/env node + +/** + * Filter OpenAPI spec to GET-only endpoints and generate reference docs. + * + * Usage: node filter-openapi.cjs [input] + * input - path to full openapi.json (default: ../../../../website/static/openapi.json) + * + * Outputs (in ../references/): + * openapi-readonly.json - GET-only OpenAPI spec + * api-endpoints.md - Human-readable endpoint reference grouped by service + */ + +const fs = require('fs'); +const path = require('path'); + +const inputPath = process.argv[2] + || path.resolve(__dirname, '../../../website/static/openapi.json'); +const refsDir = path.resolve(__dirname, '../references'); +const jsonOutputPath = path.join(refsDir, 'openapi-readonly.json'); +const mdOutputPath = path.join(refsDir, 'api-endpoints.md'); + +const spec = JSON.parse(fs.readFileSync(inputPath, 'utf-8')); + +// --- Step 1: Filter to GET-only paths --- + +const filteredPaths = {}; +for (const [route, methods] of Object.entries(spec.paths || {})) { + if (methods.get) { + filteredPaths[route] = { get: methods.get }; + } +} + +// --- Step 2: Collect referenced schemas (transitive) --- + +const referencedSchemas = new Set(); + +function collectRefs(obj) { + if (!obj || typeof obj !== 'object') return; + if (Array.isArray(obj)) { + obj.forEach(collectRefs); + return; + } + for (const [key, value] of Object.entries(obj)) { + if (key === '$ref' && typeof value === 'string') { + const match = value.match(/#\/components\/schemas\/(.+)/); + if (match) referencedSchemas.add(match[1]); + } + collectRefs(value); + } +} + +collectRefs(filteredPaths); + +let prevSize = 0; +const allSchemas = spec.components?.schemas || {}; +while (referencedSchemas.size !== prevSize) { + prevSize = referencedSchemas.size; + for (const name of [...referencedSchemas]) { + if (allSchemas[name]) { + collectRefs(allSchemas[name]); + } + } +} + +const filteredSchemas = {}; +for (const name of referencedSchemas) { + if (allSchemas[name]) { + filteredSchemas[name] = allSchemas[name]; + } +} + +// --- Step 3: Redact sensitive fields from response schemas --- + +const sensitiveFields = [ + 'modelApiKey', 'customModelBaseUrl', 'apiKey', + 'secret', 'token', 'password', 'credential', +]; + +function redactSensitiveProperties(obj) { + if (!obj || typeof obj !== 'object') return; + if (Array.isArray(obj)) { + obj.forEach(redactSensitiveProperties); + return; + } + if (obj.properties && typeof obj.properties === 'object') { + for (const field of sensitiveFields) { + if (obj.properties[field]) { + obj.properties[field] = { + type: 'string', + description: '[REDACTED] This field may contain sensitive data and should not be displayed.', + }; + } + } + } + for (const value of Object.values(obj)) { + redactSensitiveProperties(value); + } +} + +redactSensitiveProperties(filteredSchemas); +redactSensitiveProperties(filteredPaths); + +// --- Step 4: Write openapi-readonly.json --- + +const output = { + openapi: spec.openapi, + info: { + ...spec.info, + title: spec.info?.title + ' (Read-Only)', + description: 'Filtered OpenAPI spec containing only GET endpoints for safe read-only access.', + }, + servers: spec.servers, + paths: filteredPaths, + components: { + ...spec.components, + schemas: filteredSchemas, + }, +}; + +if (output.components?.securitySchemes) { + output.components.securitySchemes = spec.components.securitySchemes; +} + +fs.mkdirSync(refsDir, { recursive: true }); +fs.writeFileSync(jsonOutputPath, JSON.stringify(output, null, 2), 'utf-8'); + +// --- Step 5: Generate api-endpoints.md --- + +const byTag = {}; +for (const [route, methods] of Object.entries(filteredPaths)) { + const op = methods.get; + const tag = (op.tags && op.tags[0]) || 'Other'; + if (!byTag[tag]) byTag[tag] = []; + + const params = (op.parameters || []).map((p) => { + let s = p.name; + if (!p.required) s += '?'; + s += ` (${p.in})`; + if (p.schema && p.schema.enum) s += ' enum:' + p.schema.enum.join('|'); + if (p.schema && p.schema.default !== undefined) s += ' default:' + p.schema.default; + return s; + }); + + byTag[tag].push({ + path: route, + summary: op.summary || op.operationId || '', + params, + }); +} + +const tagOrder = [ + 'Website', 'Monitor', 'Survey', 'Telemetry', 'Billing', + 'Feed', 'Application', 'AIGateway', 'AI', + 'Worker', 'Page', 'Workspace', 'Global', 'AuditLog', +]; + +const sortedTags = [ + ...tagOrder.filter((t) => byTag[t]), + ...Object.keys(byTag).filter((t) => !tagOrder.includes(t)), +]; + +let md = `# Tianji API Endpoints (GET-only)\n\n`; +md += `Base path: \`{TIANJI_BASE_URL}/open\`\n`; +md += `Auth: \`Authorization: Bearer {TIANJI_API_KEY}\`\n`; +md += `Timestamps: milliseconds since epoch (e.g. \`1704067200000\`)\n\n---\n`; + +for (const tag of sortedTags) { + const endpoints = byTag[tag]; + md += `\n## ${tag} (${endpoints.length})\n\n`; + md += `| Endpoint | Summary | Key Params |\n`; + md += `|----------|---------|------------|\n`; + + for (const ep of endpoints) { + const nonPathParams = ep.params.filter((p) => !p.includes('(path)')); + const paramStr = nonPathParams.length > 0 + ? nonPathParams.map((p) => `\`${p}\``).join(', ') + : '\u2014'; + md += `| \`GET ${ep.path}\` | ${ep.summary} | ${paramStr} |\n`; + } +} + +fs.writeFileSync(mdOutputPath, md, 'utf-8'); + +// --- Summary --- + +const endpointCount = Object.keys(filteredPaths).length; +const schemaCount = Object.keys(filteredSchemas).length; +console.log(`Filtered: ${endpointCount} GET endpoints, ${schemaCount} schemas`); +console.log(`Output:`); +console.log(` ${jsonOutputPath}`); +console.log(` ${mdOutputPath}`); diff --git a/apps/openclaw-skill/skill.yaml b/apps/openclaw-skill/skill.yaml new file mode 100644 index 000000000..a9d03819d --- /dev/null +++ b/apps/openclaw-skill/skill.yaml @@ -0,0 +1,40 @@ +name: tianji-analytics +version: 0.2.0 +author: moonrailgun +description: > + Query website analytics, monitor uptime, survey results, telemetry data, + feed events, and more from Tianji platform via its OpenAPI. + Use when the user asks about website traffic, pageviews, monitor uptime, + survey feedback, telemetry events, or any Tianji platform data. +license: MIT +permissions: + - network +entryPoint: + type: natural-language + skillFile: SKILL.md +config: + TIANJI_BASE_URL: + type: string + description: Tianji API base URL (e.g. https://tianji.example.com) + required: true + TIANJI_API_KEY: + type: string + description: Tianji API key for authentication + required: true + secret: true + TIANJI_WORKSPACE_ID: + type: string + description: Default Tianji workspace ID + required: true +triggers: + keywords: + - website analytics + - pageview + - monitor status + - survey results + - telemetry + - tianji + - uptime + - traffic analysis + - feed events + - lighthouse report diff --git a/docker-compose.yml b/docker-compose.yml index 6adf13648..bcc6990ec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,9 @@ version: '3' services: tianji: image: moonrailgun/tianji + build: + context: ./ + dockerfile: ./Dockerfile ports: - "12345:12345" environment: @@ -9,6 +12,7 @@ services: 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 diff --git a/docker/clickhouse/docker-compose.yml b/docker/clickhouse/docker-compose.yml new file mode 100644 index 000000000..7d13b0fff --- /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 000000000..ebf1d3dce --- /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 000000000..0e8a0eb36 --- /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 000000000..597c1d041 --- /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 000000000..9e8ea9805 --- /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.msgbyte.com/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 000000000..ea2e056d9 --- /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 000000000..a7a3a801d --- /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 000000000..c7c206d65 --- /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 000000000..2488ac734 --- /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 000000000..a09258a89 --- /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 000000000..16d274d53 --- /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 000000000..413392098 --- /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 000000000..c9b70aa18 --- /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 000000000..2c0cb5d36 --- /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 000000000..20c1c13cb --- /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 000000000..6eeb0fbc9 --- /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/k8s/sealos/tianji.yaml b/docker/k8s/sealos/tianji.yaml similarity index 100% rename from k8s/sealos/tianji.yaml rename to docker/k8s/sealos/tianji.yaml diff --git a/example/expo/.gitignore b/example/expo/.gitignore new file mode 100644 index 000000000..c9d575d70 --- /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 000000000..d67f37488 --- /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 000000000..cd4feb8a3 --- /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 000000000..aca82973d --- /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 000000000..851c47cf2 --- /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 000000000..090de5747 --- /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 000000000..06e70c4f9 --- /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 000000000..886b07960 --- /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 000000000..63ba38d33 --- /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 000000000..486ee4971 --- /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 000000000..28d7ff717 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 000000000..03d6f6b6c 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 000000000..e75f697b1 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 000000000..a0b1526fc 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 000000000..66fd9570e 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 000000000..9d72a9ffc 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 000000000..2229b130a 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 000000000..a99b20322 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 000000000..03d6f6b6c 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 000000000..f3c649bba --- /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 000000000..c1aebd6ab --- /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 000000000..8f05675b2 --- /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 000000000..6d106ba3f --- /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 000000000..9b4bc31cb --- /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 000000000..5df1d75fd --- /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 000000000..c0e1a78f5 --- /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 000000000..681974ca1 --- /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 000000000..1ac322506 --- /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 000000000..b68e53e9c --- /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 000000000..9177f4daf --- /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 000000000..f1fabd4a1 --- /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 000000000..6668e78d2 --- /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 000000000..70d1c3c0b --- /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 000000000..e8a8ac523 --- /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 000000000..f1cfcdf19 --- /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 000000000..f388cc6e4 --- /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 000000000..d98420fe3 --- /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 000000000..d76161cc8 --- /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 ( -

- ); - }, - }, - ] as ColumnsType; - }, []); - - if (isLoading) { - return ; - } - - return ( - - ); - } -); -WebsiteListTable.displayName = 'WebsiteListTable'; diff --git a/src/client/components/website/WebsiteMetricsTable.tsx b/src/client/components/website/WebsiteMetricsTable.tsx index f8a5dde7d..a9b8fe076 100644 --- a/src/client/components/website/WebsiteMetricsTable.tsx +++ b/src/client/components/website/WebsiteMetricsTable.tsx @@ -6,6 +6,8 @@ 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]; @@ -21,7 +23,12 @@ interface MetricsTableProps { | 'os' | 'device' | 'country' - | 'event'; + | 'event' + | 'utm_source' + | 'utm_medium' + | 'utm_campaign' + | 'utm_term' + | 'utm_content'; startAt: number; endAt: number; } @@ -30,6 +37,14 @@ export const WebsiteMetricsTable: React.FC = React.memo( 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, @@ -47,7 +62,11 @@ export const WebsiteMetricsTable: React.FC = React.memo( dataIndex: 'x', ellipsis: true, render: (val) => - val ?? {t('(None)')}, + val ? ( + {labelMap[val] ?? val} + ) : ( + {t('(None)')} + ), }, { title: title[1], @@ -74,17 +93,19 @@ export const WebsiteMetricsTable: React.FC = React.memo( ]; return ( -
+ +
+ ); } ); diff --git a/src/client/components/website/WebsiteOnlineCount.tsx b/src/client/components/website/WebsiteOnlineCount.tsx index 54d481ba5..23a178b1f 100644 --- a/src/client/components/website/WebsiteOnlineCount.tsx +++ b/src/client/components/website/WebsiteOnlineCount.tsx @@ -17,7 +17,10 @@ export const WebsiteOnlineCount: React.FC<{ if (typeof count === 'number' && count > 0) { return (
-
+
+
+
+
{count} {t('current visitor')} diff --git a/src/client/components/website/WebsiteOverview.tsx b/src/client/components/website/WebsiteOverview.tsx index feeb32598..1d6509bd5 100644 --- a/src/client/components/website/WebsiteOverview.tsx +++ b/src/client/components/website/WebsiteOverview.tsx @@ -14,8 +14,10 @@ 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 '../TimeEventChart'; +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; @@ -33,27 +35,48 @@ export const WebsiteOverview: React.FC<{ data: chartData = [], isLoading: isLoadingPageview, refetch: refetchPageview, - } = trpc.website.pageviews.useQuery( + } = trpc.insights.query.useQuery( { workspaceId: website.workspaceId, - websiteId: website.id, - startAt: startDate.valueOf(), - endAt: endDate.valueOf(), - unit, - timezone: getUserTimezone(), + 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 pageviews = data.pageviews ?? []; - const sessions = data.sessions ?? []; - - const pageviewsArr = getDateArray(pageviews, startDate, endDate, unit); - const sessionsArr = getDateArray(sessions, startDate, endDate, unit); - - return [ - ...pageviewsArr.map((item) => ({ ...item, type: 'pageview' })), - ...sessionsArr.map((item) => ({ ...item, type: 'session' })), - ]; + 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, + }, }, } ); @@ -62,27 +85,36 @@ export const WebsiteOverview: React.FC<{ 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.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()]); - message.success(t('Refreshed')); + toast.success(t('Refreshed')); }); const loading = isLoadingPageview || isLoadingStats; return ( - +
@@ -91,7 +123,7 @@ export const WebsiteOverview: React.FC<{ {website.monitorId && ( @@ -146,7 +178,7 @@ export const WebsiteOverview: React.FC<{
- + ); }); WebsiteOverview.displayName = 'WebsiteOverview'; diff --git a/src/client/components/website/WebsiteSimpleMap.tsx b/src/client/components/website/WebsiteSimpleMap.tsx new file mode 100644 index 000000000..f610da90e --- /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 index aa035d9ad..647bdd193 100644 --- a/src/client/components/website/WebsiteVisitorMap/VisitorLarkMap.tsx +++ b/src/client/components/website/WebsiteVisitorMap/VisitorLarkMap.tsx @@ -5,10 +5,10 @@ import { PointLayer, PointLayerProps, } from '@antv/larkmap'; -import React from 'react'; +import React, { useMemo } from 'react'; import { AppRouterOutput } from '../../../api/trpc'; import { useGlobalConfig } from '../../../hooks/useConfig'; -import { useSettingsStore } from '../../../store/settings'; +import { useTheme } from '../../../store/settings'; import { mapCenter } from './utils'; const layerOptions: Omit = { @@ -32,7 +32,7 @@ const layerOptions: Omit = { function useMapConfig(mapType: 'Mapbox' | 'Gaode' = 'Mapbox'): LarkMapProps { const { amapToken, mapboxToken } = useGlobalConfig(); - const colorScheme = useSettingsStore((state) => state.colorScheme); + const colorScheme = useTheme(); const baseOption: LarkMapProps['mapOptions'] = { center: [mapCenter.lng, mapCenter.lat], @@ -68,6 +68,7 @@ function useMapConfig(mapType: 'Mapbox' | 'Gaode' = 'Mapbox'): LarkMapProps { export const VisitorLarkMap: React.FC<{ data: AppRouterOutput['website']['geoStats']; mapType: 'Mapbox' | 'Gaode'; + fullScreen?: boolean; }> = React.memo((props) => { const config = useMapConfig(props.mapType); @@ -76,10 +77,18 @@ export const VisitorLarkMap: React.FC<{ parser: { type: 'json', x: 'longitude', y: 'latitude' }, }; + const size = useMemo(() => { + if (props.data.length > 5000) { + return 3; + } + + return 5; + }, [props.data.length]); + return ( - + - + ); }); diff --git a/src/client/components/website/WebsiteVisitorMap/VisitorLeafletMap.tsx b/src/client/components/website/WebsiteVisitorMap/VisitorLeafletMap.tsx index daf03de5e..3de48289d 100644 --- a/src/client/components/website/WebsiteVisitorMap/VisitorLeafletMap.tsx +++ b/src/client/components/website/WebsiteVisitorMap/VisitorLeafletMap.tsx @@ -1,17 +1,21 @@ -import React from 'react'; +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'; -import { useTranslation } from '@i18next-toolkit/react'; 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 ( = 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) => ( - + ))} ); diff --git a/src/client/components/website/WebsiteVisitorMap/index.tsx b/src/client/components/website/WebsiteVisitorMap/index.tsx index 23329916a..e5eb51a09 100644 --- a/src/client/components/website/WebsiteVisitorMap/index.tsx +++ b/src/client/components/website/WebsiteVisitorMap/index.tsx @@ -28,6 +28,7 @@ function useMapType() { interface WebsiteVisitorMapProps { websiteId: string; + fullScreen?: boolean; } export const WebsiteVisitorMap: React.FC = React.memo( (props) => { @@ -48,11 +49,17 @@ export const WebsiteVisitorMap: React.FC = React.memo( return ; } - if (mapType === 'Leaflet') { - return ; + if (mapType === 'Mapbox' || mapType === 'Gaode') { + return ( + + ); } - return ; + return ; } ); WebsiteVisitorMap.displayName = 'WebsiteVisitorMap'; diff --git a/src/client/components/website/WebsiteVisitorMapBtn.tsx b/src/client/components/website/WebsiteVisitorMapBtn.tsx index 23a6629d3..a6f68ebb9 100644 --- a/src/client/components/website/WebsiteVisitorMapBtn.tsx +++ b/src/client/components/website/WebsiteVisitorMapBtn.tsx @@ -22,13 +22,13 @@ export const WebsiteVisitorMapBtn: React.FC = - {t('Vistor Map')} + {t('Visitor Map')} diff --git a/src/client/components/worker/UrlParamsInput.tsx b/src/client/components/worker/UrlParamsInput.tsx new file mode 100644 index 000000000..8e5191bbb --- /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 000000000..19a61c695 --- /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 ? ( +