diff --git a/.oxfmtrc.jsonc b/.oxfmtrc.jsonc new file mode 100644 index 00000000000..a2758a7aba1 --- /dev/null +++ b/.oxfmtrc.jsonc @@ -0,0 +1,27 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "singleQuote": true, + "trailingComma": "all", + "endOfLine": "lf", + "printWidth": 80, + "sortPackageJson": false, + "ignorePatterns": [ + "**/dist/**", + "**/build/**", + "**/lib/**", + "**/.next/**", + "**/coverage/**", + "**/generated/**", + "**/generated-admin/**", + "**/generated-metadata/**", + "**/.cache/**", + "**/node_modules/**", + "**/*.min.js", + "**/*.snap", + "**/*.md", + "**/*.mdx", + "**/seed-project/**/*.mjs", + "packages/twenty-zapier/build/**", + "**/upgrade-version-command/**" + ] +} diff --git a/.yarnrc.yml b/.yarnrc.yml index 225305de8b9..cb63cfa2131 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -10,4 +10,8 @@ nodeLinker: node-modules npmMinimalAgeGate: 3d +npmPreapprovedPackages: + - twenty-sdk + - twenty-client-sdk + yarnPath: .yarn/releases/yarn-4.13.0.cjs diff --git a/nx.json b/nx.json index 2a18e74d025..0d4edfdfda5 100644 --- a/nx.json +++ b/nx.json @@ -44,12 +44,12 @@ "cache": true, "options": { "cwd": "{projectRoot}", - "command": "npx oxlint -c .oxlintrc.json . && (prettier . --check --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata || (echo 'ERROR: Prettier formatting check failed! Fix with: npx nx lint --configuration=fix' && false))" + "command": "npx oxlint -c .oxlintrc.json . && (npx oxfmt --check . || (echo 'ERROR: oxfmt formatting check failed! Fix with: npx nx lint --configuration=fix' && false))" }, "configurations": { "ci": {}, "fix": { - "command": "npx oxlint --fix -c .oxlintrc.json . && prettier . --write --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata" + "command": "npx oxlint --fix -c .oxlintrc.json . && npx oxfmt ." } }, "dependsOn": ["^build", "twenty-oxlint-rules:build"] @@ -59,12 +59,12 @@ "cache": false, "dependsOn": ["twenty-oxlint-rules:build"], "options": { - "command": "FILES=$(git diff --name-only --diff-filter=d main -- {projectRoot}/ | grep -E '{args.pattern}'); [ -z \"$FILES\" ] && echo 'No changed files.' || (npx oxlint -c {projectRoot}/.oxlintrc.json $FILES && (prettier --check $FILES || (echo 'ERROR: Prettier formatting check failed! Fix with: npx nx lint:diff-with-main --configuration=fix' && false)))", + "command": "FILES=$(git diff --name-only --diff-filter=d main -- {projectRoot}/ | grep -E '{args.pattern}'); [ -z \"$FILES\" ] && echo 'No changed files.' || (npx oxlint -c {projectRoot}/.oxlintrc.json $FILES && (npx oxfmt --check $FILES || (echo 'ERROR: oxfmt formatting check failed! Fix with: npx nx lint:diff-with-main --configuration=fix' && false)))", "pattern": "\\.(ts|tsx|js|jsx)$" }, "configurations": { "fix": { - "command": "FILES=$(git diff --name-only --diff-filter=d main -- {projectRoot}/ | grep -E '{args.pattern}'); [ -z \"$FILES\" ] && echo 'No changed files.' || (npx oxlint --fix -c {projectRoot}/.oxlintrc.json $FILES && prettier --write $FILES)" + "command": "FILES=$(git diff --name-only --diff-filter=d main -- {projectRoot}/ | grep -E '{args.pattern}'); [ -z \"$FILES\" ] && echo 'No changed files.' || (npx oxlint --fix -c {projectRoot}/.oxlintrc.json $FILES && npx oxfmt $FILES)" } } }, @@ -73,18 +73,14 @@ "cache": true, "options": { "cwd": "{projectRoot}", - "command": "prettier {args.files} --check --cache {args.cache} --cache-location {args.cacheLocation} --write {args.write} --cache-strategy {args.cacheStrategy}", - "cache": true, - "cacheLocation": "../../.cache/prettier/{projectRoot}", - "cacheStrategy": "metadata", - "write": false + "command": "npx oxfmt --check {args.files} {args.write}", + "files": ".", + "write": "" }, "configurations": { - "ci": { - "cacheStrategy": "content" - }, + "ci": {}, "fix": { - "write": true + "command": "npx oxfmt {args.files}" } }, "dependsOn": ["^build"] diff --git a/package.json b/package.json index 2af1bbbca35..2b4bd1d9289 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "concurrently": "^8.2.2", "http-server": "^14.1.1", "nx": "22.5.4", + "oxfmt": "0.50.0", "tsx": "^4.17.0", "verdaccio": "^6.3.1" }, diff --git a/packages/twenty-apps/examples/postcard/src/roles/default-function.role.ts b/packages/twenty-apps/examples/postcard/src/roles/default-function.role.ts index c423bfb9e4c..a1e659dab56 100644 --- a/packages/twenty-apps/examples/postcard/src/roles/default-function.role.ts +++ b/packages/twenty-apps/examples/postcard/src/roles/default-function.role.ts @@ -1,4 +1,4 @@ -import { PermissionFlag, defineRole } from 'twenty-sdk/define'; +import { SystemPermissionFlag, defineRole } from 'twenty-sdk/define'; import { CONTENT_FIELD_UNIVERSAL_IDENTIFIER, POST_CARD_UNIVERSAL_IDENTIFIER, @@ -36,5 +36,5 @@ export default defineRole({ canUpdateFieldValue: true, }, ], - permissionFlags: [PermissionFlag.APPLICATIONS], + permissionFlagUniversalIdentifiers: [SystemPermissionFlag.APPLICATIONS], }); diff --git a/packages/twenty-apps/fixtures/rich-app/src/roles/default-function.role.ts b/packages/twenty-apps/fixtures/rich-app/src/roles/default-function.role.ts index c48fdb94404..a0944cf5d6b 100644 --- a/packages/twenty-apps/fixtures/rich-app/src/roles/default-function.role.ts +++ b/packages/twenty-apps/fixtures/rich-app/src/roles/default-function.role.ts @@ -1,4 +1,4 @@ -import { PermissionFlag, defineRole } from 'twenty-sdk/define'; +import { SystemPermissionFlag, defineRole } from 'twenty-sdk/define'; import { CONTENT_FIELD_UNIVERSAL_IDENTIFIER, POST_CARD_UNIVERSAL_IDENTIFIER, @@ -36,5 +36,5 @@ export default defineRole({ canUpdateFieldValue: false, }, ], - permissionFlags: [PermissionFlag.APPLICATIONS], + permissionFlagUniversalIdentifiers: [SystemPermissionFlag.APPLICATIONS], }); diff --git a/packages/twenty-apps/internal/twenty-partners/.env.example b/packages/twenty-apps/internal/twenty-partners/.env.example new file mode 100644 index 00000000000..2996471ae63 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/.env.example @@ -0,0 +1,6 @@ +# Credentials for integration tests (vitest.config.ts) and seed scripts +# (vitest.seed.config.ts). Copy this file to .env.local and fill in the key. +# Get an API key from the Twenty UI: Settings -> APIs & Webhooks. +# .env.local is gitignored; never commit a real key. +TWENTY_API_URL=http://localhost:2020 +TWENTY_API_KEY= diff --git a/packages/twenty-apps/internal/twenty-partners/.github/workflows/cd.yml b/packages/twenty-apps/internal/twenty-partners/.github/workflows/cd.yml new file mode 100644 index 00000000000..0ec483fc577 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/.github/workflows/cd.yml @@ -0,0 +1,42 @@ +name: CD + +on: + push: + branches: + - main + pull_request: + types: [labeled] + +permissions: + contents: read + +env: + TWENTY_DEPLOY_URL: http://localhost:2020 + +concurrency: + group: cd-${{ github.ref }} + cancel-in-progress: true + +jobs: + deploy-and-install: + if: >- + github.event_name == 'push' || + (github.event_name == 'pull_request' && github.event.label.name == 'deploy') + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Deploy + uses: twentyhq/twenty/.github/actions/deploy-twenty-app@main + with: + api-url: ${{ env.TWENTY_DEPLOY_URL }} + api-key: ${{ secrets.TWENTY_DEPLOY_API_KEY }} + + - name: Install + uses: twentyhq/twenty/.github/actions/install-twenty-app@main + with: + api-url: ${{ env.TWENTY_DEPLOY_URL }} + api-key: ${{ secrets.TWENTY_DEPLOY_API_KEY }} diff --git a/packages/twenty-apps/internal/twenty-partners/.github/workflows/ci.yml b/packages/twenty-apps/internal/twenty-partners/.github/workflows/ci.yml new file mode 100644 index 00000000000..9112ecb495c --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: CI + +on: + push: + branches: + - main + pull_request: {} + +permissions: + contents: read + +env: + TWENTY_VERSION: latest + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Spawn Twenty test instance + id: twenty + uses: twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main + with: + twenty-version: ${{ env.TWENTY_VERSION }} + + - name: Enable Corepack + run: corepack enable + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: yarn + + - name: Install dependencies + run: yarn install --immutable + + - name: Run integration tests + run: yarn test + env: + TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }} + TWENTY_API_KEY: ${{ steps.twenty.outputs.api-key }} diff --git a/packages/twenty-apps/internal/twenty-partners/.gitignore b/packages/twenty-apps/internal/twenty-partners/.gitignore new file mode 100644 index 00000000000..ff704a82b05 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/.gitignore @@ -0,0 +1,43 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn + +# codegen +generated + +# testing +/coverage + +# dev +/dist/ + +.twenty + +# production +/build + +# misc +.DS_Store + +# internal planning docs +docs/superpowers/ +**/docs/superpowers/ +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* +!.env.example + +# typescript +*.tsbuildinfo +*.d.ts diff --git a/packages/twenty-apps/internal/twenty-partners/.nvmrc b/packages/twenty-apps/internal/twenty-partners/.nvmrc new file mode 100644 index 00000000000..341cb50613a --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/.nvmrc @@ -0,0 +1 @@ +24.5.0 diff --git a/packages/twenty-apps/internal/twenty-partners/.oxlintrc.json b/packages/twenty-apps/internal/twenty-partners/.oxlintrc.json new file mode 100644 index 00000000000..369b9cfd762 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/.oxlintrc.json @@ -0,0 +1,37 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript"], + "categories": { + "correctness": "off" + }, + "ignorePatterns": ["node_modules", "dist"], + "rules": { + "no-unused-vars": "off", + + "typescript/no-unused-vars": [ + "warn", + { + "argsIgnorePattern": "^_" + } + ], + "typescript/no-explicit-any": "off" + }, + "overrides": [ + { + "files": ["**/*.logic-function.ts", "**/logic-functions/**/*.ts"], + "rules": { + "no-restricted-imports": [ + "error", + { + "patterns": [ + { + "group": ["twenty-shared", "twenty-shared/*"], + "message": "Logic functions must not import from twenty-shared directly. Import runtime types and helpers from `twenty-sdk/logic-function` instead so the logic-function bundle stays minimal." + } + ] + } + ] + } + } + ] +} diff --git a/packages/twenty-apps/internal/twenty-partners/.yarnrc.yml b/packages/twenty-apps/internal/twenty-partners/.yarnrc.yml new file mode 100644 index 00000000000..3186f3f0795 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/packages/twenty-apps/internal/twenty-partners/README.md b/packages/twenty-apps/internal/twenty-partners/README.md new file mode 100644 index 00000000000..f41fe44bbd2 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/README.md @@ -0,0 +1,101 @@ +# twenty-partners + +A Twenty app that turns the CRM into the operating system for the Twenty partner program: +intake partner-eligible deals, match them to vetted marketplace partners, and track the +matching pipeline end-to-end. + +Built on [Twenty](https://twenty.com) with [`twenty-sdk`](https://www.npmjs.com/package/twenty-sdk) v2.5. + +## What's inside + +- **Custom object: `Partner`** — slug, status, availability, served geos, languages spoken, + deployment expertise, Calendly link, last-match timestamp. See `src/objects/partner.object.ts`. +- **Opportunity extensions** — `matchStatus`, `designDocStatus`, + `introSentAt`, `lastRelanceSentAt`, `tftId`, plus a `partner` relation. +- **Logic functions** + - `on-opportunity-auto-match` — fires when `matchStatus` is set to `AUTO_MATCH`. Assigns the longest-idle available partner and flips status to `MATCHED`. If no partner is available, hands off to `MANUAL_MATCH` with an audit Note explaining why. + - `list-available-partners` — surfaces matchable partners for a given opportunity. + - `post-install` — first-run setup. +- **Roles** (`src/roles/`) + - **Twenty Partner Ops** — internal team role, full CRUD on Partner/Company/Person/Opportunity. + - **Partner** — placeholder external-partner role. *Do not assign until Twenty ships + row-level permissions* — it currently grants access to every record. +- **Views** (`src/views/`) + - `Waiting for match` — opportunities awaiting human action (`matchStatus` is `TO_BE_MATCHED` or `MANUAL_MATCH`). + - `Matches overview` — full matching funnel grouped by `matchStatus` (configure Kanban + grouping manually in the UI). + - `Opportunities` — replacement of the native opportunities view with the partner columns. + - `Partners` and `All matched deals` — partner-side index and deal log. +- **Sidebar nav** — surfaced in workflow order: `Waiting for match`, `All partner deals`, + `Matches overview`, `Partners`, `Opportunities`. +- **Seed scripts** (`src/scripts/`) — populate a fresh workspace with realistic demo data. + +## Match status pipeline + +`matchStatus` is a non-nullable SELECT field with a default of `TO_BE_MATCHED`. The 10 states follow the deal lifecycle: + +| Status | Meaning | +| --- | --- | +| `TO_BE_MATCHED` | Default — deal entered, awaiting assignment | +| `MANUAL_MATCH` | Needs a human to pick a partner | +| `AUTO_MATCH` | Triggers automatic partner assignment | +| `MATCHED` | Partner assigned | +| `INTRODUCED_TO_A_PARTNER` | Customer intro sent | +| `WORKING_WITH_A_PARTNER` | Engagement underway | +| `IMPLEMENTING` | Active implementation | +| `WON` | Deal closed won | +| `RECONNECT_LATER` | Paused — reconnect in future | +| `LOST` | Deal closed lost | + +## Getting started + +Requires a local Twenty server at `http://localhost:2020` and Node `^24.5`. + +```bash +yarn install +yarn twenty dev +``` + +Default dev credentials: `tim@apple.dev` / `tim@apple.dev`. + +Run `yarn twenty help` for the full CLI reference. + +## Common commands + +| Command | What it does | +| --- | --- | +| `yarn twenty dev` | Start the dev server and sync the app on file changes | +| `yarn twenty server status` | Check the local Twenty server | +| `yarn lint` / `yarn lint:fix` | Run oxlint | +| `yarn test` | Run integration tests (`vitest.config.ts`) | + +## Seeding demo data + +Two idempotent seed scripts. Both run via the `vitest.seed.config.ts` config that skips +the global app uninstall/reinstall. + +```bash +# 1. Marketplace partners (run first — pipeline seed wires opportunities to these by slug) +yarn vitest run --config vitest.seed.config.ts src/scripts/seed-marketplace-partners.ts + +# 2. Pipeline demo: 3 companies, 3 people, 15 opportunities spread across matchStatus values +yarn vitest run --config vitest.seed.config.ts src/scripts/seed-pipeline-demo.ts +``` + +Both scripts skip records that already exist (by `slug`, `name`, or `firstName+lastName`), +so they are safe to re-run. + +## Known limitations + +Current SDK gaps blocking further polish: + +- Custom Partner record page layout (RECORD_TABLE has no relation scoping). +- Native Opportunities view column-order override. +- Kanban view configuration from app code (`ViewType.KANBAN` is currently ignored). +- App and field descriptions. + +## Learn more + +- [Twenty Apps documentation](https://docs.twenty.com/developers/extend/apps/getting-started/quick-start) +- [`twenty-sdk` on npm](https://www.npmjs.com/package/twenty-sdk) +- [Discord](https://discord.gg/cx5n4Jzs57) diff --git a/packages/twenty-apps/internal/twenty-partners/package.json b/packages/twenty-apps/internal/twenty-partners/package.json new file mode 100644 index 00000000000..93c2f18d261 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/package.json @@ -0,0 +1,38 @@ +{ + "name": "twenty-partners", + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": "^24.5.0", + "npm": "please-use-yarn", + "yarn": ">=4.0.2" + }, + "keywords": [], + "packageManager": "yarn@4.9.2", + "scripts": { + "twenty": "twenty", + "lint": "oxlint -c .oxlintrc.json .", + "lint:fix": "oxlint --fix -c .oxlintrc.json .", + "test": "vitest run", + "test:watch": "vitest", + "seed": "tsx src/scripts/seed.ts", + "import:dryrun": "tsx src/scripts/import-from-tft.ts", + "import:apply": "IMPORT_APPLY=1 tsx src/scripts/import-from-tft.ts" + }, + "dependencies": { + "twenty-client-sdk": "2.4.0", + "twenty-sdk": "2.4.0" + }, + "devDependencies": { + "@types/node": "^24.7.2", + "@types/react": "^19.0.0", + "dotenv": "^16.0.0", + "oxlint": "^0.16.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tsx": "^4.0.0", + "typescript": "^5.9.3", + "vite-tsconfig-paths": "^4.2.1", + "vitest": "^3.1.1" + } +} diff --git a/packages/twenty-apps/internal/twenty-partners/src/__tests__/00-seed-partners.integration-test.ts b/packages/twenty-apps/internal/twenty-partners/src/__tests__/00-seed-partners.integration-test.ts new file mode 100644 index 00000000000..5a1063f9aae --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/__tests__/00-seed-partners.integration-test.ts @@ -0,0 +1,74 @@ +// Seed file: ensures at least one ACTIVE + AVAILABLE partner exists before +// the matching integration tests run. Idempotent — skips if already seeded. +import { CoreApiClient } from 'twenty-client-sdk/core'; +import { beforeAll, describe, it } from 'vitest'; + +describe('seed: marketplace partners', () => { + let client: CoreApiClient; + + beforeAll(() => { + client = new CoreApiClient(); + }); + + it('ensures at least one ACTIVE AVAILABLE partner exists', async () => { + const existingResult = await client.query({ + partners: { + __args: { + filter: { validationStage: { eq: 'VALIDATED' }, availability: { eq: 'AVAILABLE' } }, + first: 1, + }, + edges: { node: { id: true } }, + }, + } as any); + + const existing = (existingResult as any).partners.edges; + if (existing.length > 0) { + console.log('[seed] partner already exists, skipping'); + return; + } + + const PARTNERS = [ + { + slug: 'nine-dots-ventures', + name: 'Nine Dots Ventures', + introduction: 'Boutique CRM implementer specialising in real-estate workflows.', + calendlyLink: 'https://calendly.com/placeholder', + deploymentExpertise: ['CLOUD', 'SELF_HOST'], + servedGeos: ['EUROPE', 'MENA'], + languagesSpoken: ['ENGLISH', 'FRENCH'], + }, + { + slug: 'elevate-consulting', + name: 'Elevate Consulting', + introduction: 'Revenue-operations partner for B2B SaaS teams.', + calendlyLink: 'https://calendly.com/placeholder', + deploymentExpertise: ['CLOUD'], + servedGeos: ['US', 'LATAM'], + languagesSpoken: ['ENGLISH', 'SPANISH'], + }, + ]; + + for (const p of PARTNERS) { + const r = await client.mutation({ + createPartner: { + __args: { + data: { + name: p.name, + slug: p.slug, + introduction: p.introduction, + calendarLink: { primaryLinkUrl: p.calendlyLink }, + deploymentExpertise: p.deploymentExpertise, + region: p.servedGeos, + languagesSpoken: p.languagesSpoken, + validationStage: 'VALIDATED', + availability: 'AVAILABLE', + }, + }, + id: true, + name: true, + }, + } as any); + console.log('[seed] created', (r as any).createPartner.name); + } + }); +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/__tests__/global-setup.ts b/packages/twenty-apps/internal/twenty-partners/src/__tests__/global-setup.ts new file mode 100644 index 00000000000..76d5993737c --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/__tests__/global-setup.ts @@ -0,0 +1,87 @@ +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; + +import { appDevOnce, appUninstall } from 'twenty-sdk/cli'; + +const APP_PATH = process.cwd(); +const CONFIG_DIR = path.join(os.homedir(), '.twenty'); + +function validateEnv(): { apiUrl: string; apiKey: string } { + const apiUrl = process.env.TWENTY_API_URL; + const apiKey = process.env.TWENTY_API_KEY; + + if (!apiUrl || !apiKey) { + throw new Error( + 'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' + + 'Start a local server: yarn twenty server start\n' + + 'Or set them in vitest env config.', + ); + } + + return { apiUrl, apiKey }; +} + +async function checkServer(apiUrl: string) { + let response: Response; + + try { + response = await fetch(`${apiUrl}/healthz`); + } catch { + throw new Error( + `Twenty server is not reachable at ${apiUrl}. ` + + 'Make sure the server is running before executing integration tests.', + ); + } + + if (!response.ok) { + throw new Error(`Server at ${apiUrl} returned ${response.status}`); + } +} + +function writeConfig(apiUrl: string, apiKey: string) { + const payload = JSON.stringify( + { + remotes: { + local: { apiUrl, apiKey, accessToken: apiKey }, + }, + defaultRemote: 'local', + }, + null, + 2, + ); + + fs.mkdirSync(CONFIG_DIR, { recursive: true }); + fs.writeFileSync(path.join(CONFIG_DIR, 'config.test.json'), payload); +} + +export async function setup() { + const { apiUrl, apiKey } = validateEnv(); + + await checkServer(apiUrl); + + writeConfig(apiUrl, apiKey); + + await appUninstall({ appPath: APP_PATH }).catch(() => {}); + + const result = await appDevOnce({ + appPath: APP_PATH, + onProgress: (message: string) => console.log(`[dev] ${message}`), + }); + + if (!result.success) { + throw new Error( + `Dev sync failed: ${result.error?.message ?? 'Unknown error'}`, + ); + } +} + +export async function teardown() { + const uninstallResult = await appUninstall({ appPath: APP_PATH }); + + if (!uninstallResult.success) { + console.warn( + `App uninstall failed: ${uninstallResult.error?.message ?? 'Unknown error'}`, + ); + } +} diff --git a/packages/twenty-apps/internal/twenty-partners/src/__tests__/schema.integration-test.ts b/packages/twenty-apps/internal/twenty-partners/src/__tests__/schema.integration-test.ts new file mode 100644 index 00000000000..31007e28e95 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/__tests__/schema.integration-test.ts @@ -0,0 +1,46 @@ +import { CoreApiClient } from 'twenty-client-sdk/core'; +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; +import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { describe, expect, it } from 'vitest'; + +describe('App installation', () => { + it('should find the installed app in the applications list', async () => { + const client = new MetadataApiClient(); + + const result = await client.query({ + findManyApplications: { + id: true, + name: true, + universalIdentifier: true, + }, + }); + + const app = result.findManyApplications.find( + (a: { universalIdentifier: string }) => + a.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER, + ); + + expect(app).toBeDefined(); + }); +}); + +describe('CoreApiClient', () => { + it('should support CRUD on standard objects', async () => { + const client = new CoreApiClient(); + + const created = await client.mutation({ + createNote: { + __args: { data: { title: 'Integration test note' } }, + id: true, + }, + }); + expect(created.createNote.id).toBeDefined(); + + await client.mutation({ + destroyNote: { + __args: { id: created.createNote.id }, + id: true, + }, + }); + }); +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/application-config.ts b/packages/twenty-apps/internal/twenty-partners/src/application-config.ts new file mode 100644 index 00000000000..088f9d1ea94 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/application-config.ts @@ -0,0 +1,13 @@ +import { defineApplication } from 'twenty-sdk/define'; + +import { + APP_DESCRIPTION, + APP_DISPLAY_NAME, + APPLICATION_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineApplication({ + universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, + displayName: APP_DISPLAY_NAME, + description: APP_DESCRIPTION, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/constants/universal-identifiers.ts b/packages/twenty-apps/internal/twenty-partners/src/constants/universal-identifiers.ts new file mode 100644 index 00000000000..a5c5d00384c --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/constants/universal-identifiers.ts @@ -0,0 +1,41 @@ +export const APP_DISPLAY_NAME = 'Twenty partners'; +export const APP_DESCRIPTION = ''; +export const APPLICATION_UNIVERSAL_IDENTIFIER = 'e662fc1f-02c1-41ff-b8ba-c95a447b3965'; +export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = 'ee18c3f3-ebe7-4c56-ad6d-aad555cc32db'; +export const PARTNER_OBJECT_UNIVERSAL_IDENTIFIER = '39101b39-1c16-4148-9e82-45dc271bb90d'; +export const PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER = '65172140-d377-41c1-a2ae-190e96fb79dd'; +export const ALL_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER = '379b11d5-44d5-476b-ba7d-31d5f515c9b4'; +export const ALL_MATCHED_DEALS_VIEW_UNIVERSAL_IDENTIFIER = '7a34da39-a8e1-44c7-88b4-91ceaa064920'; +export const PARTNERS_NAV_UNIVERSAL_IDENTIFIER = '3fe15ab5-e38b-4914-af17-2270b210aeb2'; +export const PARTNER_DEALS_NAV_UNIVERSAL_IDENTIFIER = 'c5e4ac36-bede-4f4b-bfe8-bbd09518abae'; +export const ON_OPP_AUTO_MATCH_FN_UNIVERSAL_IDENTIFIER = 'eb8d4d26-8103-4b66-9026-6a86556f7ca5'; +export const POST_INSTALL_FN_UNIVERSAL_IDENTIFIER = 'f92bad2e-5905-4757-96ee-af9869d4ca0c'; +export const MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER = 'd8dd0623-3a4c-4ab3-a1e0-4ece7df24fb2'; +export const INTRO_SENT_AT_FIELD_UNIVERSAL_IDENTIFIER = 'fcf39b0c-0547-415e-806d-b238131ad7cc'; + +// Roles (Task 2) +export const TWENTY_PARTNER_OPS_ROLE_UNIVERSAL_IDENTIFIER = '3340ca65-863d-4cdc-95c9-8abdec13d0f6'; +export const PARTNER_ROLE_UNIVERSAL_IDENTIFIER = 'c3c1dc2e-1a08-4de5-abb7-2139b3d99343'; + +// Views (Task 3) +export const WAITING_FOR_MATCH_VIEW_UNIVERSAL_IDENTIFIER = 'fe11e738-6bf3-4714-929c-51c76a3fd050'; +export const MATCHES_OVERVIEW_VIEW_UNIVERSAL_IDENTIFIER = '5a8fd51a-cf9e-4a6a-b1b4-b833b215fc1c'; + +// Nav items (Task 5) +export const WAITING_FOR_MATCH_NAV_UNIVERSAL_IDENTIFIER = '00be7449-8927-47c8-a6a1-212d9106587f'; +export const MATCHES_OVERVIEW_NAV_UNIVERSAL_IDENTIFIER = '0cf349c9-fcbf-40f8-8e91-142c02bbde9c'; + +// Page layout (Task 6) +export const PARTNER_RECORD_PAGE_UNIVERSAL_IDENTIFIER = 'a888b39e-d64a-48ba-a044-d8cb685fad74'; + +// All Opportunities view + nav +export const ALL_OPPORTUNITIES_VIEW_UNIVERSAL_IDENTIFIER = '6ce1300b-6e91-4c28-83bb-6f692dbc7a98'; +export const ALL_OPPORTUNITIES_NAV_UNIVERSAL_IDENTIFIER = '37944f52-cbe5-4814-a1e6-be5b21425870'; + +// Partner views + nav (harmonization) +export const PARTNER_APPLICATIONS_VIEW_UNIVERSAL_IDENTIFIER = 'b57a84ed-d7c1-420d-b0eb-348db0dac612'; +export const VALIDATED_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER = '13cca6a7-b9f1-4103-b011-ea2e39430899'; +export const PARTNER_QUOTES_VIEW_UNIVERSAL_IDENTIFIER = 'd9db705c-795a-4a14-b891-6201149510b3'; +export const PARTNER_APPLICATIONS_NAV_UNIVERSAL_IDENTIFIER = '13e2334a-6b1e-4080-8c74-d11109990cc1'; +export const VALIDATED_PARTNERS_NAV_UNIVERSAL_IDENTIFIER = '6aed30c6-d80f-4ac6-aab0-db5bc59e5c4b'; +export const PARTNER_QUOTES_NAV_UNIVERSAL_IDENTIFIER = '3543723d-80c1-466a-ac35-86f7b284917b'; diff --git a/packages/twenty-apps/internal/twenty-partners/src/default-role.ts b/packages/twenty-apps/internal/twenty-partners/src/default-role.ts new file mode 100644 index 00000000000..a6abf654453 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/default-role.ts @@ -0,0 +1,16 @@ +import { defineApplicationRole } from 'twenty-sdk/define'; + +import { + APP_DISPLAY_NAME, + DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineApplicationRole({ + universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, + label: `${APP_DISPLAY_NAME} default function role`, + description: `${APP_DISPLAY_NAME} default function role`, + canReadAllObjectRecords: true, + canUpdateAllObjectRecords: true, + canSoftDeleteAllObjectRecords: true, + canDestroyAllObjectRecords: false, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunities-on-partner.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunities-on-partner.field.ts new file mode 100644 index 00000000000..3a88674b3b3 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunities-on-partner.field.ts @@ -0,0 +1,18 @@ +import { FieldType, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +import { PARTNER_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { OPPORTUNITIES_ON_PARTNER_FIELD_ID, PARTNER_ON_OPPORTUNITY_FIELD_ID } from './partner-on-opportunity.field'; + +export default defineField({ + universalIdentifier: OPPORTUNITIES_ON_PARTNER_FIELD_ID, + objectUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'opportunities', + label: 'Opportunities', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + relationTargetFieldMetadataUniversalIdentifier: PARTNER_ON_OPPORTUNITY_FIELD_ID, + universalSettings: { + relationType: RelationType.ONE_TO_MANY, + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-design-doc-status.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-design-doc-status.field.ts new file mode 100644 index 00000000000..b5ca6a39e5a --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-design-doc-status.field.ts @@ -0,0 +1,15 @@ +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: 'cc6b8a59-f860-493f-8b9a-f138c078fbf1', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.SELECT, + name: 'designDocStatus', + label: 'Design Doc Status', + defaultValue: "'DRAFT'", + options: [ + { id: '1901c790-22af-4149-a792-09374d67acfd', value: 'DRAFT', label: 'Draft', position: 0, color: 'gray' }, + { id: '02cbe191-cc96-4b42-9d8e-f85cd47bed24', value: 'DONE', label: 'Done', position: 1, color: 'green' }, + { id: '943e1389-12cd-4605-8066-db27ba68a50a', value: 'SHARED_WITH_PARTNER', label: 'Shared with Partner', position: 2, color: 'blue' }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-design-doc-url.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-design-doc-url.field.ts new file mode 100644 index 00000000000..9d1869a427a --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-design-doc-url.field.ts @@ -0,0 +1,10 @@ +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: '37e5428c-6c8c-4616-b626-f0ea1caa443d', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.LINKS, + name: 'designDocUrl', + label: 'Design Doc URL', + isNullable: true, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-hosting-type.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-hosting-type.field.ts new file mode 100644 index 00000000000..21e21547c9a --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-hosting-type.field.ts @@ -0,0 +1,14 @@ +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: '7ac7517f-bbca-4b4c-8996-6f864f71219b', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.SELECT, + name: 'hostingType', + label: 'Hosting Type', + isNullable: true, + options: [ + { id: '42c108d7-a874-4d1f-be4c-e87edd08f3c7', value: 'CLOUD', label: 'Cloud', position: 0, color: 'sky' }, + { id: '0fe995f4-42de-4160-96af-b3e7d542dfdd', value: 'SELF_HOSTING', label: 'Self-hosting', position: 1, color: 'purple' }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-intro-sent-at.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-intro-sent-at.field.ts new file mode 100644 index 00000000000..acc5097d840 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-intro-sent-at.field.ts @@ -0,0 +1,11 @@ +import { INTRO_SENT_AT_FIELD_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: INTRO_SENT_AT_FIELD_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.DATE_TIME, + name: 'introSentAt', + label: 'Intro Sent At', + isNullable: true, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-lost-reason.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-lost-reason.field.ts new file mode 100644 index 00000000000..570a3d107bc --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-lost-reason.field.ts @@ -0,0 +1,10 @@ +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: '834e233d-b171-409e-825f-77ac49b0f19d', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.TEXT, + name: 'lostReason', + label: 'Lost Reason', + isNullable: true, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-match-status.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-match-status.field.ts new file mode 100644 index 00000000000..5ef62648d34 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-match-status.field.ts @@ -0,0 +1,26 @@ +import { MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.SELECT, + name: 'matchStatus', + label: 'Match Status', + isNullable: false, + defaultValue: "'TO_BE_MATCHED'", + options: [ + // Pre-match (new). NEW UUIDs generated with `uuidgen` (v4). + { id: '8b3a1c0e-2f64-4a87-9d2b-1e3c4f5a6b78', value: 'TO_BE_MATCHED', label: 'To Be Matched', position: 0, color: 'grey' }, + { id: '4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f', value: 'MANUAL_MATCH', label: 'Manual Match', position: 1, color: 'grey' }, + { id: '7e8f9a0b-1c2d-4e3f-8a4b-5c6d7e8f9a0b', value: 'AUTO_MATCH', label: 'Auto Match', position: 2, color: 'yellow' }, + // Post-match. Reuse DELIVERED's UUID for MATCHED so existing rows auto-relabel. + { id: '095428d8-4680-4a2c-af83-7809dcb3f194', value: 'MATCHED', label: 'Matched', position: 3, color: 'blue' }, + { id: '2f1c79a1-ca91-4937-a4c0-6422f6534d34', value: 'INTRODUCED_TO_A_PARTNER', label: 'Introduced to a partner', position: 4, color: 'sky' }, + { id: '45cdf6ef-8672-40d5-b71f-1e5687ba5776', value: 'WORKING_WITH_A_PARTNER', label: 'Working with a partner', position: 5, color: 'turquoise' }, + { id: '7189b18d-b0f7-435a-9272-f812cba5d13d', value: 'IMPLEMENTING', label: 'Implementing', position: 6, color: 'green' }, + { id: '54cd33bc-11ea-42f1-87c8-cd9d32d2c266', value: 'WON', label: 'Won', position: 7, color: 'purple' }, + { id: '505433e8-5367-4dfb-a89a-708d5182165b', value: 'RECONNECT_LATER', label: 'Reconnect later', position: 8, color: 'orange' }, + { id: '572a9ad2-e0a6-49f2-b1f5-e36c75cc5176', value: 'LOST', label: 'Lost', position: 9, color: 'red' }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-number-of-seats.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-number-of-seats.field.ts new file mode 100644 index 00000000000..a087f1ee903 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-number-of-seats.field.ts @@ -0,0 +1,10 @@ +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: '90c683ec-2365-4533-a187-7b9ae162b753', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.NUMBER, + name: 'numberOfSeats', + label: 'Number of Seats', + isNullable: true, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-subscription-frequency.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-subscription-frequency.field.ts new file mode 100644 index 00000000000..accee28b0cb --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-subscription-frequency.field.ts @@ -0,0 +1,14 @@ +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: '59d5de53-202f-4913-a417-8a08970d87cc', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.SELECT, + name: 'subscriptionFrequency', + label: 'Subscription Frequency', + isNullable: true, + options: [ + { id: 'e53a9ebb-11d6-40de-93f2-6bcb6ab7141c', value: 'MONTHLY', label: 'Monthly', position: 0, color: 'blue' }, + { id: '6eca9f01-f891-4c9d-bef6-9238c8e67392', value: 'ANNUAL', label: 'Annual', position: 1, color: 'green' }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-subscription-type.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-subscription-type.field.ts new file mode 100644 index 00000000000..c11bd2f9209 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-subscription-type.field.ts @@ -0,0 +1,15 @@ +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: 'a58214e9-38f9-4faf-8927-09b3980fd8c3', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.SELECT, + name: 'subscriptionType', + label: 'Subscription Type', + isNullable: true, + options: [ + { id: '6d91b477-5bf1-4f5c-8aef-577b6c21fe45', value: 'PRO', label: 'Pro', position: 0, color: 'blue' }, + { id: '7b64f281-3445-4429-a5f0-af9484dff8b4', value: 'ORG', label: 'Org', position: 1, color: 'green' }, + { id: 'bf202f8f-caf9-45bf-a80c-65b344b2798d', value: 'ENT', label: 'Enterprise', position: 2, color: 'purple' }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-tft-id.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-tft-id.field.ts new file mode 100644 index 00000000000..1d1ff67ce1c --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-tft-id.field.ts @@ -0,0 +1,10 @@ +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: '2e3e1d04-2719-4e0d-9a6b-ec73acf896c5', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.TEXT, + name: 'tftOpportunityId', + label: 'TfT Opportunity ID', + isNullable: true, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-use-case.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-use-case.field.ts new file mode 100644 index 00000000000..a72a471e5b5 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/opportunity-use-case.field.ts @@ -0,0 +1,10 @@ +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: '1bc57f52-a621-4243-ae3e-05c3f504b90c', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.TEXT, + name: 'useCase', + label: 'Use Case', + isNullable: true, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/partner-company.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-company.field.ts new file mode 100644 index 00000000000..4c045570e1c --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-company.field.ts @@ -0,0 +1,22 @@ +import { FieldType, OnDeleteAction, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +import { PARTNER_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; + +export const PARTNER_COMPANY_FIELD_ID = '2779015b-28fa-4117-8ce1-b0c98cf16de2'; +export const PARTNERS_ON_COMPANY_FIELD_ID = '2896d888-a4ab-4c29-bf63-e8bfdbd1924f'; + +export default defineField({ + universalIdentifier: PARTNER_COMPANY_FIELD_ID, + objectUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'company', + label: 'Company', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier, + relationTargetFieldMetadataUniversalIdentifier: PARTNERS_ON_COMPANY_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.SET_NULL, + joinColumnName: 'companyId', + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/partner-on-opportunity.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-on-opportunity.field.ts new file mode 100644 index 00000000000..d6c03742079 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-on-opportunity.field.ts @@ -0,0 +1,22 @@ +import { FieldType, OnDeleteAction, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +import { PARTNER_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; + +export const PARTNER_ON_OPPORTUNITY_FIELD_ID = 'd9eeacaa-2f9e-44cc-b5f6-5e1526256d49'; +export const OPPORTUNITIES_ON_PARTNER_FIELD_ID = '8c04a5d4-c423-487e-bd78-7142a75b2896'; + +export default defineField({ + universalIdentifier: PARTNER_ON_OPPORTUNITY_FIELD_ID, + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.RELATION, + name: 'partner', + label: 'Partner', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: OPPORTUNITIES_ON_PARTNER_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.SET_NULL, + joinColumnName: 'partnerId', + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/partner-on-person.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-on-person.field.ts new file mode 100644 index 00000000000..55563651e28 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-on-person.field.ts @@ -0,0 +1,22 @@ +import { FieldType, OnDeleteAction, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +import { PARTNER_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; + +export const PARTNER_ON_PERSON_FIELD_ID = 'b49eeaa3-c7ef-4a5c-8c47-d2c234b5122f'; +export const PERSONS_ON_PARTNER_FIELD_ID = '2c0e2035-db52-434b-9706-cd2210009a86'; + +export default defineField({ + universalIdentifier: PARTNER_ON_PERSON_FIELD_ID, + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier, + type: FieldType.RELATION, + name: 'partner', + label: 'Partner', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: PERSONS_ON_PARTNER_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.SET_NULL, + joinColumnName: 'partnerId', + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quote-opportunity.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quote-opportunity.field.ts new file mode 100644 index 00000000000..ebb76ce893e --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quote-opportunity.field.ts @@ -0,0 +1,22 @@ +import { FieldType, OnDeleteAction, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +import { PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; + +export const PARTNER_QUOTE_OPPORTUNITY_FIELD_ID = '36684f3e-f01a-4270-8f34-78966747dd64'; +export const PARTNER_QUOTES_ON_OPPORTUNITY_FIELD_ID = '4857f339-a051-4cdb-bd8a-6219b933d1ce'; + +export default defineField({ + universalIdentifier: PARTNER_QUOTE_OPPORTUNITY_FIELD_ID, + objectUniversalIdentifier: PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'opportunity', + label: 'Opportunity', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + relationTargetFieldMetadataUniversalIdentifier: PARTNER_QUOTES_ON_OPPORTUNITY_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.SET_NULL, + joinColumnName: 'opportunityId', + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quote-partner.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quote-partner.field.ts new file mode 100644 index 00000000000..5be6eaf4f80 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quote-partner.field.ts @@ -0,0 +1,22 @@ +import { FieldType, OnDeleteAction, RelationType, defineField } from 'twenty-sdk/define'; + +import { PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; + +export const PARTNER_QUOTE_PARTNER_FIELD_ID = '1774738e-96a1-43e2-aca4-d3c7cc794c50'; +export const PARTNER_QUOTES_ON_PARTNER_FIELD_ID = 'ac7995f1-7ccf-4607-827b-0788eeacaee0'; + +export default defineField({ + universalIdentifier: PARTNER_QUOTE_PARTNER_FIELD_ID, + objectUniversalIdentifier: PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'partner', + label: 'Partner', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: PARTNER_QUOTES_ON_PARTNER_FIELD_ID, + universalSettings: { + relationType: RelationType.MANY_TO_ONE, + onDelete: OnDeleteAction.SET_NULL, + joinColumnName: 'partnerId', + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quotes-on-opportunity.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quotes-on-opportunity.field.ts new file mode 100644 index 00000000000..7fca6961cd2 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quotes-on-opportunity.field.ts @@ -0,0 +1,18 @@ +import { FieldType, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +import { PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { PARTNER_QUOTES_ON_OPPORTUNITY_FIELD_ID, PARTNER_QUOTE_OPPORTUNITY_FIELD_ID } from './partner-quote-opportunity.field'; + +export default defineField({ + universalIdentifier: PARTNER_QUOTES_ON_OPPORTUNITY_FIELD_ID, + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: FieldType.RELATION, + name: 'partnerQuotes', + label: 'Partner Quotes', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: PARTNER_QUOTE_OPPORTUNITY_FIELD_ID, + universalSettings: { + relationType: RelationType.ONE_TO_MANY, + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quotes-on-partner.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quotes-on-partner.field.ts new file mode 100644 index 00000000000..afcf1f33868 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/partner-quotes-on-partner.field.ts @@ -0,0 +1,18 @@ +import { FieldType, RelationType, defineField } from 'twenty-sdk/define'; + +import { PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { PARTNER_QUOTES_ON_PARTNER_FIELD_ID, PARTNER_QUOTE_PARTNER_FIELD_ID } from './partner-quote-partner.field'; + +export default defineField({ + universalIdentifier: PARTNER_QUOTES_ON_PARTNER_FIELD_ID, + objectUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'partnerQuotes', + label: 'Partner Quotes', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: PARTNER_QUOTE_PARTNER_FIELD_ID, + universalSettings: { + relationType: RelationType.ONE_TO_MANY, + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/partners-on-company.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/partners-on-company.field.ts new file mode 100644 index 00000000000..f326e29ad85 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/partners-on-company.field.ts @@ -0,0 +1,18 @@ +import { FieldType, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +import { PARTNER_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { PARTNERS_ON_COMPANY_FIELD_ID, PARTNER_COMPANY_FIELD_ID } from './partner-company.field'; + +export default defineField({ + universalIdentifier: PARTNERS_ON_COMPANY_FIELD_ID, + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier, + type: FieldType.RELATION, + name: 'partners', + label: 'Partners', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetFieldMetadataUniversalIdentifier: PARTNER_COMPANY_FIELD_ID, + universalSettings: { + relationType: RelationType.ONE_TO_MANY, + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/person-discord.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/person-discord.field.ts new file mode 100644 index 00000000000..86f3dec2ef0 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/person-discord.field.ts @@ -0,0 +1,10 @@ +import { FieldType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +export default defineField({ + universalIdentifier: 'c6862035-bf2e-42ea-86c4-bf46636f7859', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier, + type: FieldType.TEXT, + name: 'discord', + label: 'Discord', + isNullable: true, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/fields/persons-on-partner.field.ts b/packages/twenty-apps/internal/twenty-partners/src/fields/persons-on-partner.field.ts new file mode 100644 index 00000000000..c93b4a01971 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/fields/persons-on-partner.field.ts @@ -0,0 +1,18 @@ +import { FieldType, RelationType, STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineField } from 'twenty-sdk/define'; + +import { PARTNER_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { PARTNER_ON_PERSON_FIELD_ID, PERSONS_ON_PARTNER_FIELD_ID } from './partner-on-person.field'; + +export default defineField({ + universalIdentifier: PERSONS_ON_PARTNER_FIELD_ID, + objectUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + type: FieldType.RELATION, + name: 'persons', + label: 'Persons', + isNullable: true, + relationTargetObjectMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier, + relationTargetFieldMetadataUniversalIdentifier: PARTNER_ON_PERSON_FIELD_ID, + universalSettings: { + relationType: RelationType.ONE_TO_MANY, + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/logic-functions/__tests__/on-opportunity-auto-match.integration-test.ts b/packages/twenty-apps/internal/twenty-partners/src/logic-functions/__tests__/on-opportunity-auto-match.integration-test.ts new file mode 100644 index 00000000000..762f15f2456 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/logic-functions/__tests__/on-opportunity-auto-match.integration-test.ts @@ -0,0 +1,220 @@ +import { CoreApiClient } from 'twenty-client-sdk/core'; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'; + +// Helpers +async function createOpp(client: CoreApiClient, name: string) { + const r = await client.mutation({ + createOpportunity: { + __args: { data: { name } }, + id: true, + matchStatus: true, + }, + } as any); + return (r as any).createOpportunity as { id: string; matchStatus: string }; +} + +async function destroyOpp(client: CoreApiClient, id: string) { + await client + .mutation({ destroyOpportunity: { __args: { id }, id: true } } as any) + .catch(() => {}); +} + +async function getOpp(client: CoreApiClient, id: string) { + const r = await client.query({ + opportunity: { + __args: { filter: { id: { eq: id } } }, + id: true, + matchStatus: true, + partner: { id: true }, + }, + } as any); + return (r as any).opportunity as { + id: string; + matchStatus: string; + partner: { id: string } | null; + }; +} + +async function sleep(ms: number) { + return new Promise((r) => setTimeout(r, ms)); +} + +describe('on-opportunity-auto-match (success path)', () => { + let client: CoreApiClient; + const created: string[] = []; + let seededPartnerId: string | null = null; + + beforeAll(async () => { + client = new CoreApiClient(); + // Ensure at least one ACTIVE+AVAILABLE partner exists for the function to pick. + const existing = await client.query({ + partners: { + __args: { + filter: { validationStage: { eq: 'VALIDATED' }, availability: { eq: 'AVAILABLE' } }, + first: 1, + }, + edges: { node: { id: true } }, + }, + } as any); + if ((existing as any).partners.edges.length === 0) { + const r = await client.mutation({ + createPartner: { + __args: { + data: { + name: '[test] auto-match seed partner', + slug: 'test-auto-match-seed', + validationStage: 'VALIDATED', + availability: 'AVAILABLE', + }, + }, + id: true, + }, + } as any); + seededPartnerId = (r as any).createPartner.id; + } + }); + + afterAll(async () => { + if (seededPartnerId) { + await client + .mutation({ destroyPartner: { __args: { id: seededPartnerId }, id: true } } as any) + .catch(() => {}); + } + }); + + beforeEach(() => { + client = new CoreApiClient(); + }); + + afterEach(async () => { + for (const id of created) await destroyOpp(client, id); + created.length = 0; + }); + + it('defaults a new opportunity to TO_BE_MATCHED', async () => { + const opp = await createOpp(client, `[test] default status ${Date.now()}`); + created.push(opp.id); + expect(opp.matchStatus).toBe('TO_BE_MATCHED'); + }); + + it('assigns a partner and flips to MATCHED when set to AUTO_MATCH', async () => { + const opp = await createOpp(client, `[test] auto match ${Date.now()}`); + created.push(opp.id); + + await client.mutation({ + updateOpportunity: { + __args: { id: opp.id, data: { matchStatus: 'AUTO_MATCH' } }, + id: true, + }, + } as any); + + // Logic function runs async (~2s per spec). Poll up to 10s. + let final = await getOpp(client, opp.id); + for (let i = 0; i < 20 && final.matchStatus === 'AUTO_MATCH'; i++) { + await sleep(500); + final = await getOpp(client, opp.id); + } + + expect(final.matchStatus).toBe('MATCHED'); + expect(final.partner?.id).toBeDefined(); + }); +}); + +describe('on-opportunity-auto-match (failure path)', () => { + let client: CoreApiClient; + const createdOpps: string[] = []; + const flippedPartners: Array<{ id: string; prevAvailability: string }> = []; + + beforeEach(() => { + client = new CoreApiClient(); + }); + + afterEach(async () => { + // Restore partner availabilities first so other tests find a partner. + for (const p of flippedPartners) { + await client + .mutation({ + updatePartner: { + __args: { id: p.id, data: { availability: p.prevAvailability } }, + id: true, + }, + } as any) + .catch(() => {}); + } + flippedPartners.length = 0; + + for (const id of createdOpps) await destroyOpp(client, id); + createdOpps.length = 0; + }); + + it('hands off to MANUAL_MATCH with a Note when no partner is available', async () => { + // Make every AVAILABLE partner UNAVAILABLE for the duration of this test. + const all = await client.query({ + partners: { + __args: { + filter: { availability: { eq: 'AVAILABLE' } }, + first: 100, + }, + edges: { node: { id: true, availability: true } }, + }, + } as any); + + const edges = ((all as any)?.partners?.edges ?? []) as Array<{ + node: { id: string; availability: string }; + }>; + + for (const e of edges) { + flippedPartners.push({ id: e.node.id, prevAvailability: e.node.availability }); + await client.mutation({ + updatePartner: { + __args: { id: e.node.id, data: { availability: 'UNAVAILABLE' } }, + id: true, + }, + } as any); + } + + const opp = await createOpp(client, `[test] no-partner ${Date.now()}`); + createdOpps.push(opp.id); + + await client.mutation({ + updateOpportunity: { + __args: { id: opp.id, data: { matchStatus: 'AUTO_MATCH' } }, + id: true, + }, + } as any); + + let final = await getOpp(client, opp.id); + for (let i = 0; i < 20 && final.matchStatus === 'AUTO_MATCH'; i++) { + await sleep(500); + final = await getOpp(client, opp.id); + } + + expect(final.matchStatus).toBe('MANUAL_MATCH'); + expect(final.partner).toBeNull(); + + // Confirm a Note was attached. + const notes = await client.query({ + noteTargets: { + __args: { + filter: { targetOpportunityId: { eq: opp.id } }, + first: 10, + }, + edges: { + node: { + id: true, + note: { id: true, title: true, bodyV2: { markdown: true } }, + }, + }, + }, + } as any); + const noteEdges = ((notes as any)?.noteTargets?.edges ?? []) as Array<{ + node: { note: { title: string; bodyV2: { markdown: string } } }; + }>; + + const autoMatchNote = noteEdges.find((e) => + e.node.note.title.toLowerCase().includes('auto-match'), + ); + expect(autoMatchNote).toBeDefined(); + expect(autoMatchNote!.node.note.bodyV2.markdown).toContain('No partners'); + }); +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/logic-functions/list-available-partners.logic-function.ts b/packages/twenty-apps/internal/twenty-partners/src/logic-functions/list-available-partners.logic-function.ts new file mode 100644 index 00000000000..ad0ea3f92ae --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/logic-functions/list-available-partners.logic-function.ts @@ -0,0 +1,75 @@ +import { CoreApiClient } from 'twenty-client-sdk/core'; +import { defineLogicFunction } from 'twenty-sdk/define'; + +export const LIST_AVAILABLE_PARTNERS_LOGIC_FUNCTION_ID = + '0f91164f-f492-41e8-9bb0-481be5a3d5b9'; + +type Partner = { + id: string; + name: string | null; + slug: string | null; + introduction: string | null; + languagesSpoken: string[] | null; + deploymentExpertise: string[] | null; + region: string[] | null; + calendarLink: { primaryLinkUrl: string | null } | null; +}; + +type ListAvailablePartnersResult = + | { ok: true; count: number; partners: Partner[] } + | { ok: false; reason: string }; + +const handler = async (): Promise => { + try { + const client = new CoreApiClient(); + + const result = await client.query({ + partners: { + __args: { + filter: { + validationStage: { eq: 'VALIDATED' }, + availability: { eq: 'AVAILABLE' }, + }, + orderBy: [{ name: 'AscNullsLast' }], + first: 100, + }, + edges: { + node: { + id: true, + name: true, + slug: true, + introduction: true, + languagesSpoken: true, + deploymentExpertise: true, + region: true, + calendarLink: { primaryLinkUrl: true }, + }, + }, + }, + } as any); + + const partners = ( + (result?.partners?.edges ?? []) as Array<{ node: Partner }> + ).map((edge) => edge.node); + + return { ok: true, count: partners.length, partners }; + } catch (err) { + return { + ok: false, + reason: err instanceof Error ? err.message : String(err), + }; + } +}; + +export default defineLogicFunction({ + universalIdentifier: LIST_AVAILABLE_PARTNERS_LOGIC_FUNCTION_ID, + name: 'list-available-partners', + description: 'Returns all partners with validationStage=VALIDATED and availability=AVAILABLE.', + timeoutSeconds: 10, + handler, + httpRouteTriggerSettings: { + path: '/partners', + httpMethod: 'GET', + isAuthRequired: false, + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/logic-functions/on-opportunity-auto-match.ts b/packages/twenty-apps/internal/twenty-partners/src/logic-functions/on-opportunity-auto-match.ts new file mode 100644 index 00000000000..5825c4b8a26 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/logic-functions/on-opportunity-auto-match.ts @@ -0,0 +1,105 @@ +import { DatabaseEventPayload, defineLogicFunction } from 'twenty-sdk/define'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +import { ON_OPP_AUTO_MATCH_FN_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; + +// Fires when an opportunity's matchStatus is set to 'AUTO_MATCH'. +// Happy path: picks the longest-idle ACTIVE+AVAILABLE partner, assigns it, flips to 'MATCHED'. +// Failure path: no partner available → flips to 'MANUAL_MATCH' and creates an audit Note. +const handler = async (payload: DatabaseEventPayload) => { + const props = payload.properties as { + after?: { id: string; matchStatus?: string; partnerId?: string | null }; + before?: { matchStatus?: string }; + updatedFields?: string[]; + }; + + if (!props.updatedFields?.includes('matchStatus')) return {}; + if (props.after?.matchStatus !== 'AUTO_MATCH') return {}; + if (props.before?.matchStatus === 'AUTO_MATCH') return {}; + if (props.after.partnerId) return {}; + + const client = new CoreApiClient(); + + const partnersResult = await client.query({ + partners: { + __args: { + filter: { + validationStage: { eq: 'VALIDATED' }, + availability: { eq: 'AVAILABLE' }, + }, + orderBy: [{ lastMatchAt: 'AscNullsFirst' }], + first: 1, + }, + edges: { node: { id: true, lastMatchAt: true } }, + }, + } as any); + + const topPartner = (partnersResult.partners as any).edges[0]?.node; + if (!topPartner) { + const noteBody = + `Auto-match attempted ${new Date().toISOString()}.\n` + + `No partners matched (status=ACTIVE, availability=AVAILABLE).\n` + + `Status moved to Manual Match — pick a partner manually or ` + + `update partner availability and retry by setting status back to Auto Match.`; + + try { + const noteResult = await client.mutation({ + createNote: { + __args: { data: { title: 'Auto-match failed', bodyV2: { markdown: noteBody } } }, + id: true, + }, + } as any); + const noteId = (noteResult as any).createNote.id as string; + + await client.mutation({ + createNoteTarget: { + __args: { data: { noteId, targetOpportunityId: props.after.id } }, + id: true, + }, + } as any); + } catch { + // Note creation is best-effort; status flip is the critical action. + } + + await client.mutation({ + updateOpportunity: { + __args: { id: props.after.id, data: { matchStatus: 'MANUAL_MATCH' } }, + id: true, + }, + } as any); + + return { matched: false, reason: 'no_partner_available' }; + } + + await client.mutation({ + updateOpportunity: { + __args: { + id: props.after.id, + data: { partnerId: topPartner.id, matchStatus: 'MATCHED' }, + }, + id: true, + }, + } as any); + + await client.mutation({ + updatePartner: { + __args: { + id: topPartner.id, + data: { lastMatchAt: new Date().toISOString() }, + }, + id: true, + }, + } as any); + + return { matched: true, partnerId: topPartner.id }; +}; + +export default defineLogicFunction({ + universalIdentifier: ON_OPP_AUTO_MATCH_FN_UNIVERSAL_IDENTIFIER, + name: 'on-opportunity-auto-match', + timeoutSeconds: 10, + handler, + databaseEventTriggerSettings: { + eventName: 'opportunity.updated', + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/logic-functions/post-install.ts b/packages/twenty-apps/internal/twenty-partners/src/logic-functions/post-install.ts new file mode 100644 index 00000000000..87f57f0424c --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/logic-functions/post-install.ts @@ -0,0 +1,48 @@ +import { InstallPayload, definePostInstallLogicFunction } from 'twenty-sdk/define'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const handler = async (_payload: InstallPayload) => { + const client = new CoreApiClient(); + + const partnerResult = await client.mutation({ + createPartner: { + __args: { + data: { + name: 'Test Partner Alpha', + validationStage: 'VALIDATED', + availability: 'AVAILABLE', + languagesSpoken: ['ENGLISH', 'FRENCH'], + deploymentExpertise: ['CLOUD', 'SELF_HOST'], + region: 'EUROPE', + }, + }, + id: true, + }, + } as any); + + const partnerId = (partnerResult.createPartner as any).id; + + await client.mutation({ + createPerson: { + __args: { + data: { + name: { + firstName: 'Test Partner', + lastName: 'Contact', + }, + partnerId, + }, + }, + id: true, + }, + } as any); + + return { seeded: true, partnerId }; +}; + +export default definePostInstallLogicFunction({ + universalIdentifier: 'f92bad2e-5905-4757-96ee-af9869d4ca0c', + name: 'post-install', + handler, + shouldRunSynchronously: true, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/all-opportunities.navigation-menu-item.ts b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/all-opportunities.navigation-menu-item.ts new file mode 100644 index 00000000000..6728a99188b --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/all-opportunities.navigation-menu-item.ts @@ -0,0 +1,15 @@ +import { NavigationMenuItemType, defineNavigationMenuItem } from 'twenty-sdk/define'; + +import { + ALL_OPPORTUNITIES_NAV_UNIVERSAL_IDENTIFIER, + ALL_OPPORTUNITIES_VIEW_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineNavigationMenuItem({ + universalIdentifier: ALL_OPPORTUNITIES_NAV_UNIVERSAL_IDENTIFIER, + type: NavigationMenuItemType.VIEW, + icon: 'IconTargetArrow', + position: 3, + folderUniversalIdentifier: '0b2e499a-ae74-45e0-af08-243e19fc56aa', + viewUniversalIdentifier: ALL_OPPORTUNITIES_VIEW_UNIVERSAL_IDENTIFIER, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/matches-overview.navigation-menu-item.ts b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/matches-overview.navigation-menu-item.ts new file mode 100644 index 00000000000..c08a8f8c5f7 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/matches-overview.navigation-menu-item.ts @@ -0,0 +1,15 @@ +import { NavigationMenuItemType, defineNavigationMenuItem } from 'twenty-sdk/define'; + +import { + MATCHES_OVERVIEW_NAV_UNIVERSAL_IDENTIFIER, + MATCHES_OVERVIEW_VIEW_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineNavigationMenuItem({ + universalIdentifier: MATCHES_OVERVIEW_NAV_UNIVERSAL_IDENTIFIER, + type: NavigationMenuItemType.VIEW, + icon: 'IconLayoutKanban', + position: 1, + folderUniversalIdentifier: '0b2e499a-ae74-45e0-af08-243e19fc56aa', + viewUniversalIdentifier: MATCHES_OVERVIEW_VIEW_UNIVERSAL_IDENTIFIER, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partner-applications.navigation-menu-item.ts b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partner-applications.navigation-menu-item.ts new file mode 100644 index 00000000000..fc1e3dea274 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partner-applications.navigation-menu-item.ts @@ -0,0 +1,15 @@ +import { NavigationMenuItemType, defineNavigationMenuItem } from 'twenty-sdk/define'; + +import { + PARTNER_APPLICATIONS_NAV_UNIVERSAL_IDENTIFIER, + PARTNER_APPLICATIONS_VIEW_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineNavigationMenuItem({ + universalIdentifier: PARTNER_APPLICATIONS_NAV_UNIVERSAL_IDENTIFIER, + type: NavigationMenuItemType.VIEW, + icon: 'IconUserPlus', + position: 1, + folderUniversalIdentifier: '857be3b5-82c6-45f7-b546-e20a8a97be8d', + viewUniversalIdentifier: PARTNER_APPLICATIONS_VIEW_UNIVERSAL_IDENTIFIER, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partner-deals.navigation-menu-item.ts b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partner-deals.navigation-menu-item.ts new file mode 100644 index 00000000000..1eaf616b496 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partner-deals.navigation-menu-item.ts @@ -0,0 +1,15 @@ +import { NavigationMenuItemType, defineNavigationMenuItem } from 'twenty-sdk/define'; + +import { + ALL_MATCHED_DEALS_VIEW_UNIVERSAL_IDENTIFIER, + PARTNER_DEALS_NAV_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineNavigationMenuItem({ + universalIdentifier: PARTNER_DEALS_NAV_UNIVERSAL_IDENTIFIER, + type: NavigationMenuItemType.VIEW, + icon: 'IconHandshake', + position: 2, + folderUniversalIdentifier: '0b2e499a-ae74-45e0-af08-243e19fc56aa', + viewUniversalIdentifier: ALL_MATCHED_DEALS_VIEW_UNIVERSAL_IDENTIFIER, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partner-quotes.navigation-menu-item.ts b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partner-quotes.navigation-menu-item.ts new file mode 100644 index 00000000000..187ead55f66 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partner-quotes.navigation-menu-item.ts @@ -0,0 +1,15 @@ +import { NavigationMenuItemType, defineNavigationMenuItem } from 'twenty-sdk/define'; + +import { + PARTNER_QUOTES_NAV_UNIVERSAL_IDENTIFIER, + PARTNER_QUOTES_VIEW_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineNavigationMenuItem({ + universalIdentifier: PARTNER_QUOTES_NAV_UNIVERSAL_IDENTIFIER, + type: NavigationMenuItemType.VIEW, + icon: 'IconFileDollar', + position: 3, + folderUniversalIdentifier: '857be3b5-82c6-45f7-b546-e20a8a97be8d', + viewUniversalIdentifier: PARTNER_QUOTES_VIEW_UNIVERSAL_IDENTIFIER, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partners-folder.navigation-menu-item.ts b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partners-folder.navigation-menu-item.ts new file mode 100644 index 00000000000..aba7ffed0d4 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partners-folder.navigation-menu-item.ts @@ -0,0 +1,10 @@ +import { NavigationMenuItemType, defineNavigationMenuItem } from 'twenty-sdk/define'; + +export default defineNavigationMenuItem({ + universalIdentifier: '857be3b5-82c6-45f7-b546-e20a8a97be8d', + type: NavigationMenuItemType.FOLDER, + name: 'Partners', + icon: 'IconBuildingStore', + color: 'purple', + position: -1, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partners.navigation-menu-item.ts b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partners.navigation-menu-item.ts new file mode 100644 index 00000000000..9d30a43473d --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/partners.navigation-menu-item.ts @@ -0,0 +1,15 @@ +import { NavigationMenuItemType, defineNavigationMenuItem } from 'twenty-sdk/define'; + +import { + ALL_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER, + PARTNERS_NAV_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineNavigationMenuItem({ + universalIdentifier: PARTNERS_NAV_UNIVERSAL_IDENTIFIER, + type: NavigationMenuItemType.VIEW, + icon: 'IconBuildingStore', + position: 0, + folderUniversalIdentifier: '857be3b5-82c6-45f7-b546-e20a8a97be8d', + viewUniversalIdentifier: ALL_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/pipeline-folder.navigation-menu-item.ts b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/pipeline-folder.navigation-menu-item.ts new file mode 100644 index 00000000000..820a293f450 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/pipeline-folder.navigation-menu-item.ts @@ -0,0 +1,9 @@ +import { NavigationMenuItemType, defineNavigationMenuItem } from 'twenty-sdk/define'; + +export default defineNavigationMenuItem({ + universalIdentifier: '0b2e499a-ae74-45e0-af08-243e19fc56aa', + type: NavigationMenuItemType.FOLDER, + name: 'Pipeline', + icon: 'IconTargetArrow', + position: -2, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/validated-partners.navigation-menu-item.ts b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/validated-partners.navigation-menu-item.ts new file mode 100644 index 00000000000..01ebd1c20a7 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/validated-partners.navigation-menu-item.ts @@ -0,0 +1,15 @@ +import { NavigationMenuItemType, defineNavigationMenuItem } from 'twenty-sdk/define'; + +import { + VALIDATED_PARTNERS_NAV_UNIVERSAL_IDENTIFIER, + VALIDATED_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineNavigationMenuItem({ + universalIdentifier: VALIDATED_PARTNERS_NAV_UNIVERSAL_IDENTIFIER, + type: NavigationMenuItemType.VIEW, + icon: 'IconCircleCheck', + position: 2, + folderUniversalIdentifier: '857be3b5-82c6-45f7-b546-e20a8a97be8d', + viewUniversalIdentifier: VALIDATED_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/waiting-for-match.navigation-menu-item.ts b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/waiting-for-match.navigation-menu-item.ts new file mode 100644 index 00000000000..e4ae610ab33 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/navigation-menu-items/waiting-for-match.navigation-menu-item.ts @@ -0,0 +1,15 @@ +import { NavigationMenuItemType, defineNavigationMenuItem } from 'twenty-sdk/define'; + +import { + WAITING_FOR_MATCH_NAV_UNIVERSAL_IDENTIFIER, + WAITING_FOR_MATCH_VIEW_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineNavigationMenuItem({ + universalIdentifier: WAITING_FOR_MATCH_NAV_UNIVERSAL_IDENTIFIER, + type: NavigationMenuItemType.VIEW, + icon: 'IconClockHour4', + position: 0, + folderUniversalIdentifier: '0b2e499a-ae74-45e0-af08-243e19fc56aa', + viewUniversalIdentifier: WAITING_FOR_MATCH_VIEW_UNIVERSAL_IDENTIFIER, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/objects/partner-quote.object.ts b/packages/twenty-apps/internal/twenty-partners/src/objects/partner-quote.object.ts new file mode 100644 index 00000000000..7fa789948f0 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/objects/partner-quote.object.ts @@ -0,0 +1,65 @@ +import { FieldType, defineObject } from 'twenty-sdk/define'; + +import { PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; + +export default defineObject({ + universalIdentifier: PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER, + nameSingular: 'partnerQuote', + namePlural: 'partnerQuotes', + labelSingular: 'Partner Quote', + labelPlural: 'Partner Quotes', + description: 'A quote a partner submitted for a customer deal', + icon: 'IconFileDollar', + isSearchable: true, + labelIdentifierFieldMetadataUniversalIdentifier: '9e688624-83d2-4715-8b18-80492a6de2b6', + fields: [ + { + universalIdentifier: '9e688624-83d2-4715-8b18-80492a6de2b6', + type: FieldType.TEXT, + name: 'name', + label: 'Name', + icon: 'IconTag', + defaultValue: "''", + }, + { + universalIdentifier: 'a0fe09c4-c1f4-4b96-93c6-d7ec38f1166a', + type: FieldType.SELECT, + name: 'status', + label: 'Status', + icon: 'IconProgressCheck', + defaultValue: "'WIP'", + options: [ + { id: '5d41450b-8efb-41e8-81b7-b534429ec1b4', value: 'WIP', label: 'WIP', position: 0, color: 'gray' }, + { id: '6611ae9a-4253-4638-9c47-d8bf2ec54368', value: 'INTERVIEW_SCHEDULED', label: 'Interview scheduled', position: 1, color: 'blue' }, + { id: 'ae3d91e4-96ea-4687-b71f-d2d80088f28a', value: 'UNDER_CUSTOMER_PARTNER_REVIEW', label: 'Under review', position: 2, color: 'orange' }, + { id: '91fe48a3-7950-4cdd-8c52-8d9b2cc03f0b', value: 'APPROVED', label: 'Approved', position: 3, color: 'green' }, + { id: 'fa640b71-862e-4c64-80fa-37ab8b50d254', value: 'REJECTED', label: 'Rejected', position: 4, color: 'red' }, + ], + }, + { + universalIdentifier: 'b52d263e-423e-40b0-b82c-29214597c005', + type: FieldType.DATE_TIME, + name: 'approvalDate', + label: 'Approval Date', + icon: 'IconCalendarCheck', + isNullable: true, + }, + { + universalIdentifier: '2cbe67e3-24ec-421b-bab5-50f3306c2391', + type: FieldType.CURRENCY, + name: 'amount', + label: 'Amount', + icon: 'IconCoin', + isNullable: true, + }, + { + universalIdentifier: 'f303369e-288c-4a48-9920-c1de0ad9a159', + type: FieldType.FILES, + name: 'quoteFile', + label: 'Quote File', + icon: 'IconPaperclip', + isNullable: true, + universalSettings: { maxNumberOfValues: 1 }, + }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/objects/partner.object.ts b/packages/twenty-apps/internal/twenty-partners/src/objects/partner.object.ts new file mode 100644 index 00000000000..aa7d0982859 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/objects/partner.object.ts @@ -0,0 +1,483 @@ +import { FieldType, defineObject } from 'twenty-sdk/define'; + +import { PARTNER_OBJECT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; + +export default defineObject({ + universalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + nameSingular: 'partner', + namePlural: 'partners', + labelSingular: 'Partner', + labelPlural: 'Partners', + description: 'A certified implementation partner', + icon: 'IconBuildingStore', + isSearchable: true, + labelIdentifierFieldMetadataUniversalIdentifier: 'a0000001-0000-4000-8000-000000000001', + fields: [ + { + universalIdentifier: 'a0000001-0000-4000-8000-000000000001', + type: FieldType.TEXT, + name: 'name', + label: 'Name', + icon: 'IconTag', + defaultValue: "''", + }, + { + universalIdentifier: 'a0000002-0000-4000-8000-000000000002', + type: FieldType.TEXT, + name: 'slug', + label: 'Slug', + icon: 'IconLink', + isNullable: true, + }, + { + universalIdentifier: '2ca9856f-f54a-4326-9ff3-668fd7da0b50', + type: FieldType.SELECT, + name: 'validationStage', + label: 'Validation Stage', + icon: 'IconProgressCheck', + defaultValue: "'APPLICATION'", + options: [ + { id: '1b4f8b0b-68ec-4c02-a7e6-fbd0cda2f3b9', value: 'APPLICATION', label: 'Application', position: 0, color: 'gray' }, + { id: '901181af-b2cb-4052-9ac8-3c84281671f8', value: 'POTENTIAL', label: 'Potential partner', position: 1, color: 'blue' }, + { id: '3ebbc425-e5e3-412d-93df-5f70fefa1798', value: 'VALIDATED', label: 'Validated partner', position: 2, color: 'green' }, + { id: '3d0ec027-0d51-4df7-932e-13c60d4b4382', value: 'FORMER', label: 'Former partner', position: 3, color: 'orange' }, + { id: '19dc4929-735a-4b64-8f57-f34f3eaa3a73', value: 'REJECTED', label: 'Rejected', position: 4, color: 'red' }, + ], + }, + { + universalIdentifier: '5af4e57e-7fa7-4c4f-b40f-37549361459a', + type: FieldType.BOOLEAN, + name: 'reviewed', + label: 'Reviewed', + icon: 'IconChecks', + isNullable: true, + }, + { + universalIdentifier: '5412e4ca-cc96-4be8-8652-b73dace7673b', + type: FieldType.RATING, + name: 'ranking', + label: 'Ranking', + icon: 'IconStar', + isNullable: true, + options: [ + { id: 'e4c784f0-7e2b-4eca-94dc-fc447266f252', value: 'RATING_1', label: '1', position: 0 }, + { id: '79d033bd-4189-4f69-a9d1-a54d7cbbc5bc', value: 'RATING_2', label: '2', position: 1 }, + { id: '1206bdcc-a804-4649-a3ce-bd001a3abc2c', value: 'RATING_3', label: '3', position: 2 }, + { id: '1c195738-d823-415e-b4db-44a6cdc09ec5', value: 'RATING_4', label: '4', position: 3 }, + { id: '92cd3a72-8d5b-4077-a369-ddfe429aa2aa', value: 'RATING_5', label: '5', position: 4 }, + ], + }, + { + universalIdentifier: 'd4fa6461-37b6-49ee-9181-dd482e74a70b', + type: FieldType.SELECT, + name: 'partnerTier', + label: 'Partner Tier', + icon: 'IconMedal', + isNullable: true, + options: [ + { id: '63806cc0-02a8-4fe9-b2ba-0efe43a33109', value: 'NEW', label: 'New', position: 0, color: 'gray' }, + { id: '1ea6f5ba-6a4a-4e64-9b56-11e9ae56c97e', value: 'INTERMEDIATE', label: 'Intermediate', position: 1, color: 'blue' }, + { id: 'b44de8b4-caea-4ca6-94cd-fb67221b5fdb', value: 'ADVANCED', label: 'Advanced', position: 2, color: 'green' }, + ], + }, + { + universalIdentifier: '500021ad-ca42-4fd3-8727-392dd26b722a', + type: FieldType.MULTI_SELECT, + name: 'partnerScope', + label: 'Partner Scope', + icon: 'IconListCheck', + isNullable: true, + options: [ + { id: '00f05e1c-0fd9-4214-a461-554b7c9e7eb5', value: 'APPS', label: 'Apps', position: 0, color: 'blue' }, + { id: 'e0f789d5-a3bc-4ecb-8bc2-5aa018468d83', value: 'DATA_MODEL', label: 'Data model', position: 1, color: 'green' }, + { id: 'c88bf189-8be4-4431-aa03-85928f8b2a52', value: 'DATA_MIGRATION', label: 'Data migration', position: 2, color: 'turquoise' }, + { id: 'a7fd9429-c26f-49ab-bf52-4d591f5ca7a0', value: 'HOSTING_ENVIRONMENT', label: 'Hosting environment', position: 3, color: 'purple' }, + { id: '9e416a39-05c6-4c80-9f36-99b5b60c26ec', value: 'WORKFLOWS', label: 'Workflows', position: 4, color: 'orange' }, + ], + }, + { + universalIdentifier: 'a451e557-a488-470a-8b35-6f9b8cfb1a10', + type: FieldType.SELECT, + name: 'typeOfTeam', + label: 'Type of Team', + icon: 'IconUsersGroup', + isNullable: true, + options: [ + { id: '0f59b781-5a81-4025-92ad-4b93b2c62df0', value: 'SOLO', label: 'Solo', position: 0, color: 'blue' }, + { id: 'db47bc44-93db-4cbf-8d0f-3057cf01d11e', value: 'AGENCY', label: 'Agency', position: 1, color: 'green' }, + ], + }, + { + universalIdentifier: '6f260cc0-a860-41e6-ad40-a2ef32ecffbe', + type: FieldType.ARRAY, + name: 'skills', + label: 'Skills', + icon: 'IconTags', + isNullable: true, + }, + { + universalIdentifier: 'a0000004-0000-4000-8000-000000000004', + type: FieldType.SELECT, + name: 'availability', + label: 'Availability', + icon: 'IconCalendarCheck', + defaultValue: "'AVAILABLE'", + options: [ + { id: '639d74c3-5d38-407b-b0e4-81f3930db451', value: 'AVAILABLE', label: 'Available', position: 0, color: 'green' }, + { id: '6a921479-559a-4446-892a-d4a1b5a3abb4', value: 'UNAVAILABLE', label: 'Unavailable', position: 1, color: 'gray' }, + ], + }, + { + universalIdentifier: 'a0000005-0000-4000-8000-000000000005', + type: FieldType.MULTI_SELECT, + name: 'deploymentExpertise', + label: 'Deployment Expertise', + icon: 'IconCloud', + isNullable: true, + options: [ + { id: 'f2f53365-d909-4a97-bb81-efab43f0a17e', value: 'CLOUD', label: 'Cloud', position: 0, color: 'sky' }, + { id: '5e47226b-4e60-413f-80ee-b8f1d9ee6ad6', value: 'SELF_HOST', label: 'Self-host', position: 1, color: 'purple' }, + ], + }, + { + universalIdentifier: 'a0000007-0000-4000-8000-000000000007', + type: FieldType.MULTI_SELECT, + name: 'languagesSpoken', + label: 'Languages Spoken', + icon: 'IconLanguage', + isNullable: true, + options: [ + { id: '3fe94a92-5f64-46f4-8697-1a44e06e939f', value: 'ENGLISH', label: 'English', position: 0, color: 'blue' }, + { id: 'fdfe8adf-b2f1-4ef9-97c1-449302932698', value: 'FRENCH', label: 'French', position: 1, color: 'red' }, + { id: '820c120a-17ba-4e9a-8dd7-9724ac41d7ca', value: 'GERMAN', label: 'German', position: 2, color: 'yellow' }, + { id: '28171507-753d-4a8e-b9c2-44c698c2a26b', value: 'CHINESE', label: 'Chinese', position: 3, color: 'orange' }, + { id: 'd9111720-26c1-47c3-85bb-a30699737cce', value: 'SPANISH', label: 'Spanish', position: 4, color: 'green' }, + { id: '7dbfcb38-4255-4fe8-91dc-ce0686191521', value: 'ARABIC', label: 'Arabic', position: 5, color: 'sky' }, + { id: 'd4e0e54b-2c57-498a-9ccc-9cfe64f09a36', value: 'BENGALI', label: 'Bengali', position: 6, color: 'purple' }, + { id: '34e765d2-5b03-4a63-a6e6-6c3c75fd8d75', value: 'CATALAN', label: 'Catalan', position: 7, color: 'pink' }, + { id: 'b042b1d3-19d6-4094-b53e-85f9727360b1', value: 'CZECH', label: 'Czech', position: 8, color: 'turquoise' }, + { id: '0a8ebe74-dcfa-44f5-abde-1af3a8dd8879', value: 'DANISH', label: 'Danish', position: 9, color: 'gray' }, + { id: '385e0db5-ab16-4a59-94fd-98921fa367d5', value: 'DUTCH', label: 'Dutch', position: 10, color: 'blue' }, + { id: 'f8ec118f-6652-4fe4-a7dc-a77c079f473e', value: 'FARSI', label: 'Farsi', position: 11, color: 'red' }, + { id: 'cd58db19-6be6-4deb-8f1b-4cd9bdbcacdf', value: 'FINNISH', label: 'Finnish', position: 12, color: 'yellow' }, + { id: 'c20be129-8a93-479c-99f1-89dad2177c0a', value: 'GREEK', label: 'Greek', position: 13, color: 'orange' }, + { id: '7faaec05-22d1-4b37-a700-9dc4733effb6', value: 'HINDI', label: 'Hindi', position: 14, color: 'green' }, + { id: '3400d7db-312f-4d54-ac19-04a2f6fed6e0', value: 'INDONESIAN', label: 'Indonesian', position: 15, color: 'sky' }, + { id: 'd1eb3870-2938-4c87-9596-3980b31df293', value: 'ITALIAN', label: 'Italian', position: 16, color: 'purple' }, + { id: '7a854dd9-6c81-4d16-8b76-79b3571638ec', value: 'JAPANESE', label: 'Japanese', position: 17, color: 'pink' }, + { id: '51be3b14-195c-45e2-815e-a6591d0946b1', value: 'KOREAN', label: 'Korean', position: 18, color: 'turquoise' }, + { id: 'c31168a0-276c-45dc-bd39-a498d39a44d8', value: 'MALAY', label: 'Malay', position: 19, color: 'gray' }, + { id: '7719d197-9e91-41d3-a66f-728cfce32c33', value: 'NORWEGIAN', label: 'Norwegian', position: 20, color: 'blue' }, + { id: '115c5e11-2462-4371-92ca-c4835649de15', value: 'POLISH', label: 'Polish', position: 21, color: 'red' }, + { id: '018e3663-3de8-4110-aceb-48c8083ebb9a', value: 'PORTUGUESE', label: 'Portuguese', position: 22, color: 'yellow' }, + { id: 'ca97987f-4f43-43d0-b7a0-490bc566b42f', value: 'PUNJABI', label: 'Punjabi', position: 23, color: 'orange' }, + { id: '05215653-4d32-49e2-9ff3-73e66e14d97d', value: 'ROMANIAN', label: 'Romanian', position: 24, color: 'green' }, + { id: 'a14e341e-a266-407f-b4c7-936cfa608500', value: 'RUSSIAN', label: 'Russian', position: 25, color: 'sky' }, + { id: '1190a9eb-bd06-4d77-ab59-c619f4e2faa3', value: 'SWAHILI', label: 'Swahili', position: 26, color: 'purple' }, + { id: '0fedffb0-c0d7-47b7-ac5f-0b166d24d670', value: 'SWEDISH', label: 'Swedish', position: 27, color: 'pink' }, + { id: '8205bc44-b2a0-4211-9fe5-1bc64efaa844', value: 'TAGALOG', label: 'Tagalog', position: 28, color: 'turquoise' }, + { id: '866b6438-c172-4c72-b69e-b1ca89342526', value: 'TAMIL', label: 'Tamil', position: 29, color: 'gray' }, + { id: 'a2c4851b-302b-4420-aff2-6d7dc614fa6c', value: 'THAI', label: 'Thai', position: 30, color: 'blue' }, + { id: '0a5672dd-ebe5-48bc-b8ab-7d2bf6a605e2', value: 'TURKISH', label: 'Turkish', position: 31, color: 'red' }, + { id: '36324fb0-5ea2-4e9f-845c-ac65b977ab20', value: 'UKRAINIAN', label: 'Ukrainian', position: 32, color: 'yellow' }, + { id: 'b1aad243-fc85-4df3-a83e-07796ae01d6c', value: 'URDU', label: 'Urdu', position: 33, color: 'orange' }, + { id: '922895f8-3a8a-45b8-a70e-0ae7b0c8ff32', value: 'VIETNAMESE', label: 'Vietnamese', position: 34, color: 'green' }, + ], + }, + { + universalIdentifier: '0a1ce916-4df5-469a-b793-30f19e45b38d', + type: FieldType.TEXT, + name: 'city', + label: 'City', + icon: 'IconMapPin', + isNullable: true, + }, + { + universalIdentifier: 'a77d7fa6-c398-47db-af0f-036a5c719f20', + type: FieldType.SELECT, + name: 'country', + label: 'Country', + icon: 'IconFlag', + isNullable: true, + options: [ + { id: '03332728-50b1-49ec-bdec-a938cb97ca74', value: 'AFGHANISTAN', label: 'Afghanistan 🇦🇫', position: 0, color: 'blue' }, + { id: 'c39c7baa-6f52-4349-bc28-97b59f2a9a3a', value: 'ALBANIA', label: 'Albania 🇦🇱', position: 1, color: 'turquoise' }, + { id: '1bce0427-d693-451a-9a6b-a50c0c502c85', value: 'ALGERIA', label: 'Algeria 🇩🇿', position: 2, color: 'green' }, + { id: '634fb580-4f12-4a5b-b43c-5787330d4b60', value: 'ANDORRA', label: 'Andorra 🇦🇩', position: 3, color: 'sky' }, + { id: '545797b9-16df-40d5-ab9b-037d3d875263', value: 'ANGOLA', label: 'Angola 🇦🇴', position: 4, color: 'purple' }, + { id: '83b58c61-8b0d-42e1-9d4f-9a530ace7899', value: 'ANTIGUA_AND_BARBUDA', label: 'Antigua & Barbuda 🇦🇬', position: 5, color: 'orange' }, + { id: '58fd4e0d-b48d-4b22-81d8-de6db6bd22ed', value: 'ARGENTINA', label: 'Argentina 🇦🇷', position: 6, color: 'yellow' }, + { id: '7091a2e3-2095-4693-bd53-366df4ba5c75', value: 'ARMENIA', label: 'Armenia 🇦🇲', position: 7, color: 'red' }, + { id: '479725e7-f185-46c6-9e8b-605fbbb392f8', value: 'AUSTRALIA', label: 'Australia 🇦🇺', position: 8, color: 'pink' }, + { id: 'b7993e1b-5f7d-47b9-a84d-41795160393a', value: 'AUSTRIA', label: 'Austria 🇦🇹', position: 9, color: 'gray' }, + { id: 'e2d324df-2baa-49a2-a016-2c7cab0d0e95', value: 'AZERBAIJAN', label: 'Azerbaijan 🇦🇿', position: 10, color: 'blue' }, + { id: '87dc4b54-c4f7-4813-a0e4-d2214e659a77', value: 'BAHAMAS', label: 'Bahamas 🇧🇸', position: 11, color: 'turquoise' }, + { id: '32149c4f-3319-42a6-a0ca-6afb42bdf3b9', value: 'BAHRAIN', label: 'Bahrain 🇧🇭', position: 12, color: 'green' }, + { id: 'c74d1f90-e17f-4e9a-9341-5c6ffda2d3ac', value: 'BANGLADESH', label: 'Bangladesh 🇧🇩', position: 13, color: 'sky' }, + { id: '61c2e8c4-06af-4daa-ac52-34f4b47e2aad', value: 'BARBADOS', label: 'Barbados 🇧🇧', position: 14, color: 'purple' }, + { id: '1c7dfa46-72e0-4ba3-8af5-3b2d1d46fd4b', value: 'BELARUS', label: 'Belarus 🇧🇾', position: 15, color: 'orange' }, + { id: '771258e5-b16f-47d4-aaed-47a4c931b72d', value: 'BELGIUM', label: 'Belgium 🇧🇪', position: 16, color: 'yellow' }, + { id: 'f3910b68-3f3f-4afb-9f90-5561376e5098', value: 'BELIZE', label: 'Belize 🇧🇿', position: 17, color: 'red' }, + { id: 'b06f1bc7-2058-44fc-b868-631559de7d11', value: 'BENIN', label: 'Benin 🇧🇯', position: 18, color: 'pink' }, + { id: 'acff20b8-b865-47de-9049-c660866f5218', value: 'BHUTAN', label: 'Bhutan 🇧🇹', position: 19, color: 'gray' }, + { id: 'bee20647-c283-40af-9733-37753e42b464', value: 'BOLIVIA', label: 'Bolivia 🇧🇴', position: 20, color: 'blue' }, + { id: '4dd91769-c713-47af-bf48-aed0251115b1', value: 'BOSNIA_AND_HERZEGOVINA', label: 'Bosnia & Herzegovina 🇧🇦', position: 21, color: 'turquoise' }, + { id: 'a6997427-007f-42b4-b6ea-892f7ce58dff', value: 'BOTSWANA', label: 'Botswana 🇧🇼', position: 22, color: 'green' }, + { id: '3ff1f170-d3f0-4ce6-9490-bfc20aadf470', value: 'BRAZIL', label: 'Brazil 🇧🇷', position: 23, color: 'sky' }, + { id: '7b2d6911-4676-433d-93f1-6eca2530d89d', value: 'BRUNEI', label: 'Brunei 🇧🇳', position: 24, color: 'purple' }, + { id: '767c72f3-f951-4b00-958d-856d2a8bda7a', value: 'BULGARIA', label: 'Bulgaria 🇧🇬', position: 25, color: 'orange' }, + { id: 'e16ce6af-86b0-4448-8008-c5efa2ce3fe9', value: 'BURKINA_FASO', label: 'Burkina Faso 🇧🇫', position: 26, color: 'yellow' }, + { id: 'dd7e7b2a-8367-482d-99d4-7877d883c2e3', value: 'BURUNDI', label: 'Burundi 🇧🇮', position: 27, color: 'red' }, + { id: 'd9d6eea9-7724-4c65-be48-e861075121f1', value: 'CAMBODIA', label: 'Cambodia 🇰🇭', position: 28, color: 'pink' }, + { id: '49178472-0bdd-4797-be6c-e0a1ac6e4147', value: 'CAMEROON', label: 'Cameroon 🇨🇲', position: 29, color: 'gray' }, + { id: '849b6361-a83b-48fb-a3e5-2ac6e9089c7f', value: 'CANADA', label: 'Canada 🇨🇦', position: 30, color: 'blue' }, + { id: 'ca22a230-01b1-4099-b7c2-29fd3b8d09de', value: 'CAPE_VERDE', label: 'Cape Verde 🇨🇻', position: 31, color: 'turquoise' }, + { id: '360bef1e-34a9-4467-9ffe-5ebeb6427711', value: 'CENTRAL_AFRICAN_REPUBLIC', label: 'Central African Republic 🇨🇫', position: 32, color: 'green' }, + { id: 'deba0ee2-3027-4646-afc6-24410bf68198', value: 'CHAD', label: 'Chad 🇹🇩', position: 33, color: 'sky' }, + { id: 'd0bf04d5-a40c-45f0-8e82-4d5905c00131', value: 'CHILE', label: 'Chile 🇨🇱', position: 34, color: 'purple' }, + { id: '60522b37-dded-4f18-a25b-05b51a8c9aa7', value: 'CHINA', label: 'China 🇨🇳', position: 35, color: 'orange' }, + { id: '3b2c58f1-4efc-4c0f-9d43-f3a5b5b577cf', value: 'COLOMBIA', label: 'Colombia 🇨🇴', position: 36, color: 'yellow' }, + { id: 'cbea73ba-8a9d-47ee-a38b-b7c3d89d3922', value: 'COMOROS', label: 'Comoros 🇰🇲', position: 37, color: 'red' }, + { id: '895ed0d4-6a00-482a-840e-44edef034d2b', value: 'CONGO', label: 'Congo 🇨🇬', position: 38, color: 'pink' }, + { id: '13db3209-398c-474c-b5f9-269ccf27adc5', value: 'DR_CONGO', label: 'DR Congo 🇨🇩', position: 39, color: 'gray' }, + { id: '150c4227-ce67-420b-bb6c-82d5a4fb9c70', value: 'COSTA_RICA', label: 'Costa Rica 🇨🇷', position: 40, color: 'blue' }, + { id: 'ad1853e2-753d-4245-b9a7-88a29281ca0a', value: 'CROATIA', label: 'Croatia 🇭🇷', position: 41, color: 'turquoise' }, + { id: 'e42cad17-f7ea-450b-8c7f-a0ac0dc31dac', value: 'CUBA', label: 'Cuba 🇨🇺', position: 42, color: 'green' }, + { id: '9035a680-5962-48c3-8d79-ee805a2c8b1c', value: 'CYPRUS', label: 'Cyprus 🇨🇾', position: 43, color: 'sky' }, + { id: 'facb14c9-40e9-47f6-bc4b-b29178761210', value: 'CZECH_REPUBLIC', label: 'Czech Republic 🇨🇿', position: 44, color: 'purple' }, + { id: '47026695-ee4b-47f2-97c1-9d1ea9854cd5', value: 'DENMARK', label: 'Denmark 🇩🇰', position: 45, color: 'orange' }, + { id: '335e3877-1f8b-4f2b-a409-ec74596dd779', value: 'DJIBOUTI', label: 'Djibouti 🇩🇯', position: 46, color: 'yellow' }, + { id: 'ee17957f-f692-495b-a664-61284fa723f2', value: 'DOMINICA', label: 'Dominica 🇩🇲', position: 47, color: 'red' }, + { id: '4114c313-27d7-4f5a-a381-38daca143642', value: 'DOMINICAN_REPUBLIC', label: 'Dominican Republic 🇩🇴', position: 48, color: 'pink' }, + { id: 'ffcfae44-e1b7-4739-87dd-0d957062646d', value: 'ECUADOR', label: 'Ecuador 🇪🇨', position: 49, color: 'gray' }, + { id: '1f4e8ee4-3be1-42cd-96d4-3e5686dee153', value: 'EGYPT', label: 'Egypt 🇪🇬', position: 50, color: 'blue' }, + { id: 'cf98813f-2a7e-4426-b97f-d3f1ac044bb8', value: 'EL_SALVADOR', label: 'El Salvador 🇸🇻', position: 51, color: 'turquoise' }, + { id: '0e787d6e-e864-4ee3-b4c7-20fa873e8649', value: 'EQUATORIAL_GUINEA', label: 'Equatorial Guinea 🇬🇶', position: 52, color: 'green' }, + { id: 'f68b6554-4ed5-4161-933f-a321b41391ea', value: 'ERITREA', label: 'Eritrea 🇪🇷', position: 53, color: 'sky' }, + { id: '7a4387dc-198c-46db-9536-7bc73de4e1b0', value: 'ESTONIA', label: 'Estonia 🇪🇪', position: 54, color: 'purple' }, + { id: '7e0fec86-e04f-499c-a814-72ac9b4cc5ce', value: 'ESWATINI', label: 'Eswatini 🇸🇿', position: 55, color: 'orange' }, + { id: 'a7768d57-5bf2-401e-a82d-ec36e3f3a419', value: 'ETHIOPIA', label: 'Ethiopia 🇪🇹', position: 56, color: 'yellow' }, + { id: '1b9e15c3-4fa5-4cd8-9bfb-8262518665fa', value: 'FIJI', label: 'Fiji 🇫🇯', position: 57, color: 'red' }, + { id: '828a6fe2-3c50-4da3-97c9-11f3b80fbb0d', value: 'FINLAND', label: 'Finland 🇫🇮', position: 58, color: 'pink' }, + { id: '03a1c597-65cf-440f-a20d-9d6bef0540c2', value: 'FRANCE', label: 'France 🇫🇷', position: 59, color: 'gray' }, + { id: 'ab2edd1f-6353-4de8-863f-4b28e774be8e', value: 'GABON', label: 'Gabon 🇬🇦', position: 60, color: 'blue' }, + { id: '12a50b82-c2f8-4d0b-b5f4-e52dfe01ba13', value: 'GAMBIA', label: 'Gambia 🇬🇲', position: 61, color: 'turquoise' }, + { id: '2adf673e-8367-4eaf-83f0-41199718f0ee', value: 'GEORGIA', label: 'Georgia 🇬🇪', position: 62, color: 'green' }, + { id: 'bd61021e-c25b-453e-b0cc-afb3942b063f', value: 'GERMANY', label: 'Germany 🇩🇪', position: 63, color: 'sky' }, + { id: '1c45b7ae-9489-4714-b21e-8c717e6599ab', value: 'GHANA', label: 'Ghana 🇬🇭', position: 64, color: 'purple' }, + { id: 'cc47d864-cd78-4cdd-a711-d679f0cbc133', value: 'GREECE', label: 'Greece 🇬🇷', position: 65, color: 'orange' }, + { id: '2a2c3ca6-611e-49a4-9639-e8f60172c1b3', value: 'GRENADA', label: 'Grenada 🇬🇩', position: 66, color: 'yellow' }, + { id: 'da2024e5-a1a0-49ab-8cf0-0d9e3f10ff9d', value: 'GUATEMALA', label: 'Guatemala 🇬🇹', position: 67, color: 'red' }, + { id: '086c9969-1911-4aa4-8d67-3abdc12b2ce7', value: 'GUINEA', label: 'Guinea 🇬🇳', position: 68, color: 'pink' }, + { id: 'a648d1d3-4d3f-4fc6-a1ea-40f98283df75', value: 'GUINEA_BISSAU', label: 'Guinea-Bissau 🇬🇼', position: 69, color: 'gray' }, + { id: 'b567e119-4dcb-4f8a-b012-febe72262d85', value: 'GUYANA', label: 'Guyana 🇬🇾', position: 70, color: 'blue' }, + { id: '26801d38-c0e6-468a-8c78-a0aed06c16fd', value: 'HAITI', label: 'Haiti 🇭🇹', position: 71, color: 'turquoise' }, + { id: '2e31c5ff-da0a-4c82-b7bc-7a8c18b54f1e', value: 'HONDURAS', label: 'Honduras 🇭🇳', position: 72, color: 'green' }, + { id: 'a78bd322-ac40-4ab3-a46f-6204e42b0712', value: 'HUNGARY', label: 'Hungary 🇭🇺', position: 73, color: 'sky' }, + { id: '0026e951-225d-4e8c-8e64-289ead339bd3', value: 'ICELAND', label: 'Iceland 🇮🇸', position: 74, color: 'purple' }, + { id: 'f5101d7d-9b81-4d2e-a9aa-e20dfab52c58', value: 'INDIA', label: 'India 🇮🇳', position: 75, color: 'orange' }, + { id: '11b30046-58f3-42e5-a3e2-5d4761675056', value: 'INDONESIA', label: 'Indonesia 🇮🇩', position: 76, color: 'yellow' }, + { id: '7345db23-db33-45cb-b8cc-e4d4a1f1b90e', value: 'IRAN', label: 'Iran 🇮🇷', position: 77, color: 'red' }, + { id: '899d0eb1-9f04-4a88-b7f3-351e998b5979', value: 'IRAQ', label: 'Iraq 🇮🇶', position: 78, color: 'pink' }, + { id: 'e45e8143-14c1-4184-84db-c11212cb8e25', value: 'IRELAND', label: 'Ireland 🇮🇪', position: 79, color: 'gray' }, + { id: 'a208dac3-455e-41ed-9db0-aa7352d420ab', value: 'ISRAEL', label: 'Israel 🇮🇱', position: 80, color: 'blue' }, + { id: '4537862d-0565-47a0-8759-ef522996881f', value: 'ITALY', label: 'Italy 🇮🇹', position: 81, color: 'turquoise' }, + { id: '988630a1-dd19-476b-a0dc-cf8904ec5f68', value: 'IVORY_COAST', label: 'Ivory Coast 🇨🇮', position: 82, color: 'green' }, + { id: '30c755e0-61f2-4590-9969-f3150c3b5aff', value: 'JAMAICA', label: 'Jamaica 🇯🇲', position: 83, color: 'sky' }, + { id: '9ca39ab6-e0fb-4bbb-80bc-cbffa48682da', value: 'JAPAN', label: 'Japan 🇯🇵', position: 84, color: 'purple' }, + { id: 'f71aefd9-1dc0-4370-8a31-2a329d8ab15d', value: 'JORDAN', label: 'Jordan 🇯🇴', position: 85, color: 'orange' }, + { id: '7f230c15-2745-4e78-ab35-8ae5bec7bcc6', value: 'KAZAKHSTAN', label: 'Kazakhstan 🇰🇿', position: 86, color: 'yellow' }, + { id: '9e745903-4ac7-432f-bfd2-d0d78e76f3e9', value: 'KENYA', label: 'Kenya 🇰🇪', position: 87, color: 'red' }, + { id: 'ee448c0f-8b3b-4666-bbbb-19e8b62ecbcc', value: 'KIRIBATI', label: 'Kiribati 🇰🇮', position: 88, color: 'pink' }, + { id: '6de78d0a-7a2b-48bb-97b5-2a86c8bab3b6', value: 'KOSOVO', label: 'Kosovo 🇽🇰', position: 89, color: 'gray' }, + { id: '48e160eb-1fdf-4456-99f7-4e97e7d16cfa', value: 'KUWAIT', label: 'Kuwait 🇰🇼', position: 90, color: 'blue' }, + { id: '7d318293-6378-4a02-af49-23d48d887a8f', value: 'KYRGYZSTAN', label: 'Kyrgyzstan 🇰🇬', position: 91, color: 'turquoise' }, + { id: 'dc540210-83bb-40c5-ab0d-585f4648df1b', value: 'LAOS', label: 'Laos 🇱🇦', position: 92, color: 'green' }, + { id: 'fe2c5828-330f-45b8-991c-180723d5bed6', value: 'LATVIA', label: 'Latvia 🇱🇻', position: 93, color: 'sky' }, + { id: 'cff1390a-7548-401b-bfa1-5e062e1e2cd3', value: 'LEBANON', label: 'Lebanon 🇱🇧', position: 94, color: 'purple' }, + { id: '2a1d40fd-a633-4bb0-890a-1009a2126ff9', value: 'LESOTHO', label: 'Lesotho 🇱🇸', position: 95, color: 'orange' }, + { id: 'dee0303d-11c9-4bf9-913b-c5fedfc93766', value: 'LIBERIA', label: 'Liberia 🇱🇷', position: 96, color: 'yellow' }, + { id: '57713732-bd4e-4888-a4e3-ad7761e76dd9', value: 'LIBYA', label: 'Libya 🇱🇾', position: 97, color: 'red' }, + { id: '5ed4e5fd-31dc-4adc-a875-b5036ef5eac6', value: 'LIECHTENSTEIN', label: 'Liechtenstein 🇱🇮', position: 98, color: 'pink' }, + { id: '8facad69-2b22-47a4-a324-75773bd7d4f8', value: 'LITHUANIA', label: 'Lithuania 🇱🇹', position: 99, color: 'gray' }, + { id: 'fb1b4543-7f22-4c44-a953-7c68e2790e27', value: 'LUXEMBOURG', label: 'Luxembourg 🇱🇺', position: 100, color: 'blue' }, + { id: '80009115-d98c-4e63-8401-285f82d94921', value: 'MADAGASCAR', label: 'Madagascar 🇲🇬', position: 101, color: 'turquoise' }, + { id: 'b32ed1f2-3d12-4255-a39a-3a5ab64b425c', value: 'MALAWI', label: 'Malawi 🇲🇼', position: 102, color: 'green' }, + { id: 'd7c744b0-23d2-4dd4-a9f9-3298cfce0d1b', value: 'MALAYSIA', label: 'Malaysia 🇲🇾', position: 103, color: 'sky' }, + { id: 'a878865c-a73f-47d7-bf52-29c1b7b947d7', value: 'MALDIVES', label: 'Maldives 🇲🇻', position: 104, color: 'purple' }, + { id: '2d5e9644-11b6-4b55-8f9a-ed33da2de6ac', value: 'MALI', label: 'Mali 🇲🇱', position: 105, color: 'orange' }, + { id: 'e2fa091a-0e78-41da-937b-42b55e501226', value: 'MALTA', label: 'Malta 🇲🇹', position: 106, color: 'yellow' }, + { id: 'e36301e0-1d79-4540-87c7-4de2bfeface6', value: 'MARSHALL_ISLANDS', label: 'Marshall Islands 🇲🇭', position: 107, color: 'red' }, + { id: '8dc782e6-3578-4d7a-a6ba-a29e48f3cc11', value: 'MAURITANIA', label: 'Mauritania 🇲🇷', position: 108, color: 'pink' }, + { id: 'edf63c87-d621-40fe-b653-a0f7764c8961', value: 'MAURITIUS', label: 'Mauritius 🇲🇺', position: 109, color: 'gray' }, + { id: '86233df1-6c71-4ffb-9298-c61c65dcc79c', value: 'MEXICO', label: 'Mexico 🇲🇽', position: 110, color: 'blue' }, + { id: 'c41c35ff-118b-4119-b12f-93be635fbae3', value: 'MICRONESIA', label: 'Micronesia 🇫🇲', position: 111, color: 'turquoise' }, + { id: '8466a06e-a34f-4dd4-b660-8e9ad2aa5998', value: 'MOLDOVA', label: 'Moldova 🇲🇩', position: 112, color: 'green' }, + { id: '948e9117-486d-40c1-847c-de6cc64b9730', value: 'MONACO', label: 'Monaco 🇲🇨', position: 113, color: 'sky' }, + { id: 'ee33ddb8-3219-4fdb-8a71-8a6f8fdf93d3', value: 'MONGOLIA', label: 'Mongolia 🇲🇳', position: 114, color: 'purple' }, + { id: 'b5c1fa55-0b1a-43d8-a871-ebd004952d0b', value: 'MONTENEGRO', label: 'Montenegro 🇲🇪', position: 115, color: 'orange' }, + { id: '9e98cb68-666e-4116-b35f-bb9711fd7546', value: 'MOROCCO', label: 'Morocco 🇲🇦', position: 116, color: 'yellow' }, + { id: '86c4b5f8-d4bc-4639-a5bf-00a84601b911', value: 'MOZAMBIQUE', label: 'Mozambique 🇲🇿', position: 117, color: 'red' }, + { id: '8b3bcfa4-1797-492e-83d7-7d939e479b1d', value: 'MYANMAR', label: 'Myanmar 🇲🇲', position: 118, color: 'pink' }, + { id: 'b8d01fef-4061-4e8c-bc38-f8226b4be6be', value: 'NAMIBIA', label: 'Namibia 🇳🇦', position: 119, color: 'gray' }, + { id: '71dd1ad1-af94-4cfb-973d-33f87b4ef0cd', value: 'NAURU', label: 'Nauru 🇳🇷', position: 120, color: 'blue' }, + { id: 'dc692171-3adc-4a73-8b45-a25cf659c546', value: 'NEPAL', label: 'Nepal 🇳🇵', position: 121, color: 'turquoise' }, + { id: '3e161b4b-be95-4a6a-910d-628ad32691e1', value: 'NETHERLANDS', label: 'Netherlands 🇳🇱', position: 122, color: 'green' }, + { id: 'b86be59e-a1ae-4df4-937f-ad97c641e0df', value: 'NEW_ZEALAND', label: 'New Zealand 🇳🇿', position: 123, color: 'sky' }, + { id: '4674abdd-0548-4317-8af5-865e4d11327d', value: 'NICARAGUA', label: 'Nicaragua 🇳🇮', position: 124, color: 'purple' }, + { id: '85ff2ce7-ddd8-4be6-b740-d79e70a3d103', value: 'NIGER', label: 'Niger 🇳🇪', position: 125, color: 'orange' }, + { id: 'a69ab8cf-5a59-4879-92da-7cd2dea6b9e6', value: 'NIGERIA', label: 'Nigeria 🇳🇬', position: 126, color: 'yellow' }, + { id: '28736977-b21f-4678-8e22-363be28a8e94', value: 'NORTH_KOREA', label: 'North Korea 🇰🇵', position: 127, color: 'red' }, + { id: '11f87414-633d-48f8-a99d-0b407c281dbd', value: 'NORTH_MACEDONIA', label: 'North Macedonia 🇲🇰', position: 128, color: 'pink' }, + { id: '9637c5aa-d405-4414-a56b-b983ed0bfa51', value: 'NORWAY', label: 'Norway 🇳🇴', position: 129, color: 'gray' }, + { id: '70f79652-7f95-4836-ab2d-4373631910ec', value: 'OMAN', label: 'Oman 🇴🇲', position: 130, color: 'blue' }, + { id: '50e4ae80-79d8-4cdf-aa0a-a2675e6e26fb', value: 'PAKISTAN', label: 'Pakistan 🇵🇰', position: 131, color: 'turquoise' }, + { id: '08d27180-4861-4937-b04e-6cbcd33f507d', value: 'PALAU', label: 'Palau 🇵🇼', position: 132, color: 'green' }, + { id: '6aa50196-ea4b-42cd-b890-8d7d56cc9731', value: 'PALESTINE', label: 'Palestine 🇵🇸', position: 133, color: 'sky' }, + { id: '5663124d-f2fc-4374-8734-f3c53f9ea4f5', value: 'PANAMA', label: 'Panama 🇵🇦', position: 134, color: 'purple' }, + { id: '8bde7cc7-fa6c-4f52-b9df-16c99928f50f', value: 'PAPUA_NEW_GUINEA', label: 'Papua New Guinea 🇵🇬', position: 135, color: 'orange' }, + { id: '3eed4b21-1009-4dd2-bdac-53b11df8400c', value: 'PARAGUAY', label: 'Paraguay 🇵🇾', position: 136, color: 'yellow' }, + { id: 'fbc734c9-63ae-4d2e-af0f-8e2abd874dda', value: 'PERU', label: 'Peru 🇵🇪', position: 137, color: 'red' }, + { id: '211555bc-8f26-4d4d-94f1-c83884c32e20', value: 'PHILIPPINES', label: 'Philippines 🇵🇭', position: 138, color: 'pink' }, + { id: '6d69e1a3-270e-4e36-9a55-40daed5b10ef', value: 'POLAND', label: 'Poland 🇵🇱', position: 139, color: 'gray' }, + { id: '23b1ae16-f00f-4844-8871-ed2b46c1cb51', value: 'PORTUGAL', label: 'Portugal 🇵🇹', position: 140, color: 'blue' }, + { id: '4dc69f6b-b0ee-48ea-95d3-517dc4ec7b91', value: 'QATAR', label: 'Qatar 🇶🇦', position: 141, color: 'turquoise' }, + { id: '8dcd82ce-98b2-45cb-a76d-f1503509035f', value: 'ROMANIA', label: 'Romania 🇷🇴', position: 142, color: 'green' }, + { id: '7e18e241-4fd6-4303-b390-0a9dea9c16bd', value: 'RUSSIA', label: 'Russia 🇷🇺', position: 143, color: 'sky' }, + { id: '966e45fb-7ed8-478e-9c26-25b7d59ae4f4', value: 'RWANDA', label: 'Rwanda 🇷🇼', position: 144, color: 'purple' }, + { id: '22ddf2d7-f1f8-46e9-8eb9-248c02652ad0', value: 'SAINT_KITTS_AND_NEVIS', label: 'Saint Kitts & Nevis 🇰🇳', position: 145, color: 'orange' }, + { id: '3312359a-b9fd-4a95-9db2-ad3e1f67f2b2', value: 'SAINT_LUCIA', label: 'Saint Lucia 🇱🇨', position: 146, color: 'yellow' }, + { id: '159c06c1-14a5-407f-af16-b84ba14f16be', value: 'SAINT_VINCENT', label: 'Saint Vincent 🇻🇨', position: 147, color: 'red' }, + { id: '0dd1cf79-89e2-4442-bd83-c65b43a3a544', value: 'SAMOA', label: 'Samoa 🇼🇸', position: 148, color: 'pink' }, + { id: 'e16851a7-97de-4e17-aeac-0cb828655406', value: 'SAN_MARINO', label: 'San Marino 🇸🇲', position: 149, color: 'gray' }, + { id: '6415861d-5fa9-46b5-a2cd-d58587c9dd03', value: 'SAO_TOME_AND_PRINCIPE', label: 'São Tomé & Príncipe 🇸🇹', position: 150, color: 'blue' }, + { id: '5831720a-3ce0-409c-9694-1b1d12394599', value: 'SAUDI_ARABIA', label: 'Saudi Arabia 🇸🇦', position: 151, color: 'turquoise' }, + { id: '89155d75-e4d0-4115-916c-8b77b9ddc5f3', value: 'SENEGAL', label: 'Senegal 🇸🇳', position: 152, color: 'green' }, + { id: 'f0c3e0e0-8106-4921-a02e-0497c1318efa', value: 'SERBIA', label: 'Serbia 🇷🇸', position: 153, color: 'sky' }, + { id: 'd8633c5d-1c65-44c5-bc71-7eb65cb988c3', value: 'SEYCHELLES', label: 'Seychelles 🇸🇨', position: 154, color: 'purple' }, + { id: '64b9fd47-a748-4add-bacf-a7dc1f1aa0c7', value: 'SIERRA_LEONE', label: 'Sierra Leone 🇸🇱', position: 155, color: 'orange' }, + { id: '5bd24cd5-a233-4d8f-930d-27f15d493c92', value: 'SINGAPORE', label: 'Singapore 🇸🇬', position: 156, color: 'yellow' }, + { id: '63c5df12-b203-4627-91b2-0526a56452d4', value: 'SLOVAKIA', label: 'Slovakia 🇸🇰', position: 157, color: 'red' }, + { id: 'b0deeef1-767a-4730-8743-588bfeb856e6', value: 'SLOVENIA', label: 'Slovenia 🇸🇮', position: 158, color: 'pink' }, + { id: '9274cd87-3400-4ccd-9192-3239b5126691', value: 'SOLOMON_ISLANDS', label: 'Solomon Islands 🇸🇧', position: 159, color: 'gray' }, + { id: '67dc7930-eaa4-4ef0-9fc4-af996eaaae09', value: 'SOMALIA', label: 'Somalia 🇸🇴', position: 160, color: 'blue' }, + { id: '19d96578-107c-4286-9504-5d59077b8290', value: 'SOUTH_AFRICA', label: 'South Africa 🇿🇦', position: 161, color: 'turquoise' }, + { id: '11cb5cb6-d67d-47ae-adf1-ecba3635fe20', value: 'SOUTH_KOREA', label: 'South Korea 🇰🇷', position: 162, color: 'green' }, + { id: '8293e893-0d7d-4002-9929-fea536a9a73c', value: 'SOUTH_SUDAN', label: 'South Sudan 🇸🇸', position: 163, color: 'sky' }, + { id: 'e0b9ba99-d891-4f6c-95df-1f92a0a7e12e', value: 'SPAIN', label: 'Spain 🇪🇸', position: 164, color: 'purple' }, + { id: '18378ce8-e480-4fd9-b90d-379d5daf7ecb', value: 'SRI_LANKA', label: 'Sri Lanka 🇱🇰', position: 165, color: 'orange' }, + { id: '10943324-5ae5-47db-bb2f-5d4bdeafd0b0', value: 'SUDAN', label: 'Sudan 🇸🇩', position: 166, color: 'yellow' }, + { id: 'bfd244e4-87c0-4dca-89b0-a1b581f1aa65', value: 'SURINAME', label: 'Suriname 🇸🇷', position: 167, color: 'red' }, + { id: '6a8f9119-c413-4ece-b432-2ce2e80580a1', value: 'SWEDEN', label: 'Sweden 🇸🇪', position: 168, color: 'pink' }, + { id: '866a8e2d-60a7-40a0-8541-35a9cfa20c09', value: 'SWITZERLAND', label: 'Switzerland 🇨🇭', position: 169, color: 'gray' }, + { id: '93758334-0ebc-4884-bfac-c0dbeec23a35', value: 'SYRIA', label: 'Syria 🇸🇾', position: 170, color: 'blue' }, + { id: 'c785e424-7a6f-4f27-91bc-aadb617525b0', value: 'TAIWAN', label: 'Taiwan 🇹🇼', position: 171, color: 'turquoise' }, + { id: '15ab8a5b-5989-4e3b-92cb-00b88cf7b7f6', value: 'TAJIKISTAN', label: 'Tajikistan 🇹🇯', position: 172, color: 'green' }, + { id: '476a55dd-8c69-4711-8e40-54cbe41d5c3a', value: 'TANZANIA', label: 'Tanzania 🇹🇿', position: 173, color: 'sky' }, + { id: 'bfcbef16-36c4-4350-a614-7ad065276104', value: 'THAILAND', label: 'Thailand 🇹🇭', position: 174, color: 'purple' }, + { id: '6872c804-9baa-498f-9dc6-b5894b64dece', value: 'TIMOR_LESTE', label: 'Timor-Leste 🇹🇱', position: 175, color: 'orange' }, + { id: 'b6429782-b6e1-4404-a5c4-8a44f3818147', value: 'TOGO', label: 'Togo 🇹🇬', position: 176, color: 'yellow' }, + { id: 'f4a7b7f9-aa58-4d23-adac-a96ed2c61ba1', value: 'TONGA', label: 'Tonga 🇹🇴', position: 177, color: 'red' }, + { id: 'a1377467-7a21-40af-861f-4b362b30fcb4', value: 'TRINIDAD_AND_TOBAGO', label: 'Trinidad & Tobago 🇹🇹', position: 178, color: 'pink' }, + { id: '7779c7d6-c7e1-4d6c-8282-31f659988c22', value: 'TUNISIA', label: 'Tunisia 🇹🇳', position: 179, color: 'gray' }, + { id: '5858f81e-629a-4b04-98e8-6441f3984044', value: 'TURKEY', label: 'Turkey 🇹🇷', position: 180, color: 'blue' }, + { id: 'cfdae353-6782-4dde-b229-d4e2c577fbed', value: 'TURKMENISTAN', label: 'Turkmenistan 🇹🇲', position: 181, color: 'turquoise' }, + { id: '755d859a-a8d4-45e0-aa6e-77afcedb29d0', value: 'TUVALU', label: 'Tuvalu 🇹🇻', position: 182, color: 'green' }, + { id: '3b325d46-b8e8-4cb9-9318-53f20baf50bd', value: 'UGANDA', label: 'Uganda 🇺🇬', position: 183, color: 'sky' }, + { id: '10f32cd9-0a6b-4829-913d-50f519dcfc3a', value: 'UKRAINE', label: 'Ukraine 🇺🇦', position: 184, color: 'purple' }, + { id: '0776427b-0dc1-49ef-b1c8-bb7dbba5a1e9', value: 'UNITED_ARAB_EMIRATES', label: 'UAE 🇦🇪', position: 185, color: 'orange' }, + { id: 'edaf21dc-60d4-4550-8b58-2f7a19eedc92', value: 'UNITED_KINGDOM', label: 'UK 🇬🇧', position: 186, color: 'yellow' }, + { id: '553004cd-aeed-4025-aa71-574efcade200', value: 'UNITED_STATES', label: 'USA 🇺🇸', position: 187, color: 'red' }, + { id: 'f925d857-5cb7-467d-bc65-e5c04ae5a238', value: 'URUGUAY', label: 'Uruguay 🇺🇾', position: 188, color: 'pink' }, + { id: '9b50ea41-2b8f-44d1-beeb-f4f4428289c4', value: 'UZBEKISTAN', label: 'Uzbekistan 🇺🇿', position: 189, color: 'gray' }, + { id: 'a7a7cbfd-6999-42e8-874e-c2bfbe6ca567', value: 'VANUATU', label: 'Vanuatu 🇻🇺', position: 190, color: 'blue' }, + { id: '7de0a670-8f04-444c-86dc-d7be00cc8b4e', value: 'VATICAN', label: 'Vatican 🇻🇦', position: 191, color: 'turquoise' }, + { id: '9acc531c-7f17-4b67-804a-035c3c873cce', value: 'VENEZUELA', label: 'Venezuela 🇻🇪', position: 192, color: 'green' }, + { id: '540b908e-f6b6-4f70-bb9c-c86dc5becbe7', value: 'VIETNAM', label: 'Vietnam 🇻🇳', position: 193, color: 'sky' }, + { id: 'a70a3610-9a9b-4fe0-9e97-10d3e60a3d92', value: 'YEMEN', label: 'Yemen 🇾🇪', position: 194, color: 'purple' }, + { id: 'fde30bd3-b2f1-4331-a255-9601259e807e', value: 'ZAMBIA', label: 'Zambia 🇿🇲', position: 195, color: 'orange' }, + { id: 'a83060bf-f55b-4efe-9e0c-b856ad5446c4', value: 'ZIMBABWE', label: 'Zimbabwe 🇿🇼', position: 196, color: 'yellow' }, + ], + }, + { + universalIdentifier: '560503de-6330-4c1d-af97-a8dee125f2ad', + type: FieldType.MULTI_SELECT, + name: 'region', + label: 'Region', + icon: 'IconWorld', + isNullable: true, + options: [ + { id: '735d7a75-c032-48b3-8480-c46e15b7e511', value: 'EUROPE', label: 'Europe', position: 0, color: 'blue' }, + { id: '78eb8d0c-841e-48dd-8425-d8546cbcfe25', value: 'US', label: 'US', position: 1, color: 'red' }, + { id: 'b0339081-52dd-4101-b7e7-83ed6ad6ca4f', value: 'LATAM', label: 'LATAM', position: 2, color: 'green' }, + { id: 'f02bd583-87b8-48d1-97db-9395056c96e2', value: 'MENA', label: 'MENA', position: 3, color: 'orange' }, + { id: '5205c25f-a300-4967-b5f3-033c0515a28a', value: 'APAC', label: 'APAC', position: 4, color: 'yellow' }, + { id: '7f55cf70-8c81-4f68-83bc-270d215f0a4e', value: 'AFRICA', label: 'Africa', position: 5, color: 'pink' }, + ], + }, + { + universalIdentifier: '243e9808-c070-4163-8603-ded12b03923c', + type: FieldType.CURRENCY, + name: 'projectBudgetMin', + label: 'Project Budget Min', + icon: 'IconCoin', + isNullable: true, + }, + { + universalIdentifier: 'ced87a97-cb2a-43cb-a6fc-4a1eff2892ba', + type: FieldType.CURRENCY, + name: 'projectBudgetTypical', + label: 'Project Budget Typical', + icon: 'IconCoins', + isNullable: true, + }, + { + universalIdentifier: '6a095709-7620-495f-b6e0-790743e412d5', + type: FieldType.CURRENCY, + name: 'hourlyRate', + label: 'Hourly Rate', + icon: 'IconCurrencyDollar', + isNullable: true, + }, + { + universalIdentifier: '640bbf33-45d7-4174-a862-dbe611ab8d1a', + type: FieldType.LINKS, + name: 'linkedin', + label: 'LinkedIn', + icon: 'IconBrandLinkedin', + isNullable: true, + }, + { + universalIdentifier: '40d730e3-2785-45c8-aa5f-cc724b1b08e0', + type: FieldType.LINKS, + name: 'profilePicture', + label: 'Profile Picture', + icon: 'IconPhoto', + isNullable: true, + }, + { + universalIdentifier: 'a0000008-0000-4000-8000-000000000008', + type: FieldType.LINKS, + name: 'calendarLink', + label: 'Calendar Link', + icon: 'IconCalendar', + isNullable: true, + }, + { + universalIdentifier: 'a0000009-0000-4000-8000-000000000009', + type: FieldType.TEXT, + name: 'introduction', + label: 'Introduction', + icon: 'IconFileText', + isNullable: true, + }, + { + universalIdentifier: 'a0000010-0000-4000-8000-000000000010', + type: FieldType.DATE_TIME, + name: 'lastMatchAt', + label: 'Last Match At', + icon: 'IconClock', + isNullable: true, + }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/roles/partner.role.ts b/packages/twenty-apps/internal/twenty-partners/src/roles/partner.role.ts new file mode 100644 index 00000000000..c4c193d20fe --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/roles/partner.role.ts @@ -0,0 +1,56 @@ +import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineRole } from 'twenty-sdk/define'; + +import { + PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + PARTNER_ROLE_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +// PLACEHOLDER role for external partners. Twenty has no row-level filtering yet, +// so anyone assigned this role currently sees every record — do not hand out +// until RLP ships and we can scope to records owned by the assigned user. +export default defineRole({ + universalIdentifier: PARTNER_ROLE_UNIVERSAL_IDENTIFIER, + label: 'Partner', + description: + 'PLACEHOLDER. External partner self-service role. Sees ALL Partner/Opportunity records today because Twenty does not yet support row-level record filtering. When RLP ships, scope these permissions to records owned by the assigned user. DO NOT assign to real external partners until then.', + icon: 'IconBuildingStore', + canBeAssignedToUsers: true, + canUpdateAllSettings: false, + canReadAllObjectRecords: false, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + objectPermissions: [ + { + objectUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, + }, + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + canReadObjectRecords: true, + canUpdateObjectRecords: false, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, + }, + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier, + canReadObjectRecords: true, + canUpdateObjectRecords: false, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, + }, + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier, + canReadObjectRecords: true, + canUpdateObjectRecords: false, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, + }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/roles/twenty-partner-ops.role.ts b/packages/twenty-apps/internal/twenty-partners/src/roles/twenty-partner-ops.role.ts new file mode 100644 index 00000000000..8cc8d16121f --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/roles/twenty-partner-ops.role.ts @@ -0,0 +1,55 @@ +import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, defineRole } from 'twenty-sdk/define'; + +import { + PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + TWENTY_PARTNER_OPS_ROLE_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +// Internal role for the Twenty partner-ops team. Scoped strictly to the CRM +// objects needed to run the matching workflow — no settings, no destroy. +export default defineRole({ + universalIdentifier: TWENTY_PARTNER_OPS_ROLE_UNIVERSAL_IDENTIFIER, + label: 'Twenty Partner Ops', + description: + 'Internal Twenty teammate role for managing partners and matched deals. Full read/write on Partner, Company, Person, Opportunity. No access to Tasks/Notes/Workflows. No settings access.', + icon: 'IconUsersGroup', + canBeAssignedToUsers: true, + canUpdateAllSettings: false, + canReadAllObjectRecords: false, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + objectPermissions: [ + { + objectUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: true, + canDestroyObjectRecords: false, + }, + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, + }, + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, + }, + { + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, + }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/scripts/import-from-tft.ts b/packages/twenty-apps/internal/twenty-partners/src/scripts/import-from-tft.ts new file mode 100644 index 00000000000..f60cc9c7ac6 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/scripts/import-from-tft.ts @@ -0,0 +1,420 @@ +// Import partners, opportunities and partner quotes FROM the TFT workspace INTO +// this (local) workspace. Idempotent: re-running upserts by natural key +// (partner=slug, opportunity=tftOpportunityId, quote=name). +// +// TFT (source) is read over RAW GraphQL fetch: its custom fields/filters are not +// in the SDK's generated (local) genql schema, so CoreApiClient cannot build +// queries for them. The local (target) workspace is written via CoreApiClient, +// exactly like seed.ts. Two separate credential sets, no collision: +// TFT -> TFT_API_URL / TFT_API_KEY (raw fetch) +// local-> TWENTY_PARTNERS_API_URL / TWENTY_PARTNERS_API_KEY (CoreApiClient) +// +// The local server rate-limits API calls (~100 / 60s), so existence checks are +// batched into one `in` query per object (like seed.ts) and writes are paced. +// +// DRY-RUN BY DEFAULT: reads everything, writes nothing, and reports both what it +// WOULD upsert and the distinct TFT SELECT values it saw (flagging any value not +// covered by a local field option, which would otherwise fail a real write). +// Set IMPORT_APPLY=1 to actually write to the local workspace. +// +// TFT_API_URL=https://twentyfortwenty.twenty.com TFT_API_KEY= \ +// TWENTY_PARTNERS_API_URL=http://localhost:2020 TWENTY_PARTNERS_API_KEY= \ +// [IMPORT_APPLY=1] \ +// tsx src/scripts/import-from-tft.ts + +import { config } from 'dotenv'; +config({ path: '.env.local' }); + +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const requireEnv = (name: string): string => { + const value = process.env[name]; + if (!value) throw new Error(`Missing ${name} env var`); + return value; +}; + +const APPLY = process.env.IMPORT_APPLY === '1'; + +// Raw GraphQL against TFT. The generated CoreApiClient is bound to the LOCAL +// schema and cannot serialise TFT's custom fields/filters, so read TFT untyped. +const tftQuery = async (query: string): Promise => { + const url = `${requireEnv('TFT_API_URL').replace(/\/$/, '')}/graphql`; + const response = await fetch(url, { + method: 'POST', + headers: { + Authorization: `Bearer ${requireEnv('TFT_API_KEY')}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ query }), + }); + const json: any = await response.json(); + if (json.errors?.length) { + throw new Error(`TFT query failed: ${JSON.stringify(json.errors)}`); + } + return json.data; +}; + +// TFT opportunity.stage -> our matchStatus (the 4 early stages collapse to TO_BE_MATCHED) +const STAGE_TO_MATCH_STATUS: Record = { + INTRODUCED_TO_A_PARTNER: 'INTRODUCED_TO_A_PARTNER', + WORKING_WITH_A_PARTNER: 'WORKING_WITH_A_PARTNER', + WON: 'WON', + LOST: 'LOST', + RECONNECT_LATER: 'RECONNECT_LATER', + IDENTIFIED: 'TO_BE_MATCHED', + MET: 'TO_BE_MATCHED', + SOLUTIONING: 'TO_BE_MATCHED', + ADVANCED: 'TO_BE_MATCHED', +}; + +// TFT person.partnerStage -> our Partner.validationStage +const PARTNER_STAGE_TO_VALIDATION: Record = { + APPLICATION: 'APPLICATION', + POTENTIAL_PARTNER: 'POTENTIAL', + PARTNER: 'VALIDATED', + FORMER_PARTNER: 'FORMER', + REJECTED: 'REJECTED', +}; + +// Local SELECT option sets, for preflight coverage checks (a TFT value not in +// these would fail a real write). Keep in sync with src/objects + src/fields. +const LOCAL_OPTIONS: Record> = { + partnerTier: new Set(['NEW', 'INTERMEDIATE', 'ADVANCED']), + partnerScope: new Set(['APPS', 'DATA_MODEL', 'DATA_MIGRATION', 'HOSTING_ENVIRONMENT', 'WORKFLOWS']), + typeOfTeam: new Set(['SOLO', 'AGENCY']), + hostingType: new Set(['CLOUD', 'SELF_HOSTING']), + subscriptionType: new Set(['PRO', 'ORG', 'ENT']), + subscriptionFrequency: new Set(['MONTHLY', 'ANNUAL']), + quoteStatus: new Set(['WIP', 'INTERVIEW_SCHEDULED', 'UNDER_CUSTOMER_PARTNER_REVIEW', 'APPROVED', 'REJECTED']), +}; + +const slugify = (s: string): string => + s + .toLowerCase() + .trim() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); + +const edges = (result: any, key: string): any[] => + (result?.[key]?.edges ?? []).map((e: any) => e.node); + +const uniq = (values: (string | undefined | null)[]): string[] => + [...new Set(values.filter((v): v is string => !!v))]; + +async function main() { + console.log(`[import] mode: ${APPLY ? 'APPLY (writing to local)' : 'DRY-RUN (no writes)'}`); + const local = new CoreApiClient({ + url: `${requireEnv('TWENTY_PARTNERS_API_URL').replace(/\/$/, '')}/graphql`, + headers: { Authorization: `Bearer ${requireEnv('TWENTY_PARTNERS_API_KEY')}` }, + }); + + // Pace writes to stay under the local server's ~100 req/60s limit. + let writes = 0; + const pace = async () => { + if (APPLY && writes++ > 0) await new Promise((r) => setTimeout(r, 750)); + }; + + // Distinct TFT values seen, for the preflight coverage report. + const seen: Record> = { + partnerStage: new Set(), + partnerTier: new Set(), + partnerScope: new Set(), + typeOfTeam: new Set(), + oppStage: new Set(), + hostingType: new Set(), + subscriptionType: new Set(), + subscriptionFrequency: new Set(), + quoteStatus: new Set(), + typeCustom: new Set(), + }; + const note = (bucket: string, value?: string | null) => { + if (value) seen[bucket].add(value); + }; + + // --------------------------------------------------------------------- + // 1. Read all three TFT datasets (raw fetch; not rate-limited by local). + // --------------------------------------------------------------------- + const tftPeople = edges( + await tftQuery(`query { + people(first: 500, filter: { partnerStage: { in: ["APPLICATION","POTENTIAL_PARTNER","PARTNER","FORMER_PARTNER","REJECTED"] } }) { + edges { node { + id + name { firstName lastName } + emails { primaryEmail } + city jobTitle + linkedinLink { primaryLinkUrl } + partnerStage partnerTier partnerScope partnerTypeOfTeam partnerIsAvailable partnerSkills + partnerBudgetMinimum { amountMicros currencyCode } + partnerBudgetAverage { amountMicros currencyCode } + company { id name domainName { primaryLinkUrl } } + } } + } + }`), + 'people', + ); + const tftOpps = edges( + await tftQuery(`query { + opportunities(first: 500) { + edges { node { + id name numberOfSeats useCase hostingType subscriptionType subscriptionFrequence lostReason stage + amount { amountMicros currencyCode } + closeDate + company { id name domainName { primaryLinkUrl } } + partner { id } + } } + } + }`), + 'opportunities', + ); + const tftContent = edges( + await tftQuery(`query { + customerContents(first: 500) { + edges { node { id name status approvalDate typeCustom partnerPerson { id } } } + } + }`), + 'customerContents', + ); + const quotes = tftContent.filter((c: any) => Array.isArray(c.typeCustom) && c.typeCustom.includes('PARTNER_QUOTE')); + console.log(`[import] TFT: ${tftPeople.length} partner-people, ${tftOpps.length} opportunities, ${quotes.length} partner quotes`); + + const personSlug = (p: any): string => + slugify([p.name?.firstName, p.name?.lastName].filter(Boolean).join(' ').trim() || 'Unknown partner') || p.id; + + // --------------------------------------------------------------------- + // 2. Batched existence lookups against local (one `in` query per object). + // --------------------------------------------------------------------- + const partnerSlugs = uniq(tftPeople.map(personSlug)); + const partnerIdBySlug = new Map( + partnerSlugs.length + ? edges( + await local.query({ + partners: { __args: { filter: { slug: { in: partnerSlugs } }, first: 500 }, edges: { node: { id: true, slug: true } } }, + } as any), + 'partners', + ).map((n: any) => [n.slug, n.id]) + : [], + ); + + const companyNames = uniq([...tftPeople.map((p: any) => p.company?.name), ...tftOpps.map((o: any) => o.company?.name)]); + const companyIdByName = new Map( + companyNames.length + ? edges( + await local.query({ + companies: { __args: { filter: { name: { in: companyNames } }, first: 500 }, edges: { node: { id: true, name: true } } }, + } as any), + 'companies', + ).map((n: any) => [n.name, n.id]) + : [], + ); + + const oppTftIds = uniq(tftOpps.filter((o: any) => o.name).map((o: any) => o.id)); + const oppIdByTftId = new Map( + oppTftIds.length + ? edges( + await local.query({ + opportunities: { __args: { filter: { tftOpportunityId: { in: oppTftIds } }, first: 500 }, edges: { node: { id: true, tftOpportunityId: true } } }, + } as any), + 'opportunities', + ).map((n: any) => [n.tftOpportunityId, n.id]) + : [], + ); + + const quoteNames = uniq(quotes.map((q: any) => q.name || 'Partner quote')); + const quoteIdByName = new Map( + quoteNames.length + ? edges( + await local.query({ + partnerQuotes: { __args: { filter: { name: { in: quoteNames } }, first: 500 }, edges: { node: { id: true, name: true } } }, + } as any), + 'partnerQuotes', + ).map((n: any) => [n.name, n.id]) + : [], + ); + + // --------------------------------------------------------------------- + // 3. Upsert. Writes are APPLY-gated and paced; in dry-run we resolve new + // ids to synthetic placeholders so relation mapping still exercises. + // --------------------------------------------------------------------- + const upsertCompany = async (name?: string, domain?: string): Promise => { + if (!name) return undefined; + if (companyIdByName.has(name)) return companyIdByName.get(name); + let id = `dry:company:${name}`; + if (APPLY) { + await pace(); + const created: any = await local.mutation({ + createCompany: { + __args: { data: { name, ...(domain ? { domainName: { primaryLinkUrl: domain } } : {}) } }, + id: true, + }, + } as any); + id = created.createCompany.id; + } + companyIdByName.set(name, id); + return id; + }; + + const budgetCurrency = (amount: any) => + amount?.amountMicros != null ? { amountMicros: amount.amountMicros, currencyCode: amount.currencyCode ?? 'USD' } : undefined; + + // -- Partners (upsert by slug) -- + const localPartnerIdByTftPersonId = new Map(); + let partnersCreated = 0; + let partnersUpdated = 0; + for (const p of tftPeople) { + note('partnerStage', p.partnerStage); + note('partnerTier', p.partnerTier); + (Array.isArray(p.partnerScope) ? p.partnerScope : []).forEach((s: string) => note('partnerScope', s)); + note('typeOfTeam', p.partnerTypeOfTeam); + + const slug = personSlug(p); + const companyId = await upsertCompany(p.company?.name, p.company?.domainName?.primaryLinkUrl); + const data: Record = { + name: [p.name?.firstName, p.name?.lastName].filter(Boolean).join(' ').trim() || 'Unknown partner', + slug, + validationStage: PARTNER_STAGE_TO_VALIDATION[p.partnerStage] ?? 'APPLICATION', + availability: p.partnerIsAvailable ? 'AVAILABLE' : 'UNAVAILABLE', + ...(p.partnerTier ? { partnerTier: p.partnerTier } : {}), + ...(Array.isArray(p.partnerScope) && p.partnerScope.length ? { partnerScope: p.partnerScope } : {}), + ...(p.partnerTypeOfTeam ? { typeOfTeam: p.partnerTypeOfTeam } : {}), + ...(Array.isArray(p.partnerSkills) && p.partnerSkills.length ? { skills: p.partnerSkills } : {}), + ...(p.city ? { city: p.city } : {}), + ...(budgetCurrency(p.partnerBudgetMinimum) ? { projectBudgetMin: budgetCurrency(p.partnerBudgetMinimum) } : {}), + ...(budgetCurrency(p.partnerBudgetAverage) ? { projectBudgetTypical: budgetCurrency(p.partnerBudgetAverage) } : {}), + ...(p.linkedinLink?.primaryLinkUrl ? { linkedin: { primaryLinkUrl: p.linkedinLink.primaryLinkUrl } } : {}), + ...(companyId && APPLY ? { companyId } : {}), + }; + + let partnerId = partnerIdBySlug.get(slug); + if (partnerId) { + if (APPLY) { + await pace(); + await local.mutation({ updatePartner: { __args: { id: partnerId, data }, id: true } } as any); + } + partnersUpdated++; + } else { + if (APPLY) { + await pace(); + const created: any = await local.mutation({ createPartner: { __args: { data }, id: true } } as any); + partnerId = created.createPartner.id; + } else { + partnerId = `dry:partner:${slug}`; + } + partnerIdBySlug.set(slug, partnerId as string); + partnersCreated++; + } + localPartnerIdByTftPersonId.set(p.id, partnerId as string); + } + console.log(`[import] partners created=${partnersCreated} updated=${partnersUpdated}`); + + // -- Opportunities (upsert by tftOpportunityId) -- + let oppsCreated = 0; + let oppsUpdated = 0; + let oppsPartnerLinked = 0; + for (const o of tftOpps) { + if (!o.name) continue; + note('oppStage', o.stage); + note('hostingType', o.hostingType); + note('subscriptionType', o.subscriptionType); + note('subscriptionFrequency', o.subscriptionFrequence); + + const companyId = await upsertCompany(o.company?.name, o.company?.domainName?.primaryLinkUrl); + const partnerId = o.partner?.id ? localPartnerIdByTftPersonId.get(o.partner.id) : undefined; + if (partnerId) oppsPartnerLinked++; + + const data: Record = { + name: o.name, + tftOpportunityId: o.id, + matchStatus: STAGE_TO_MATCH_STATUS[o.stage] ?? 'TO_BE_MATCHED', + ...(o.numberOfSeats != null ? { numberOfSeats: o.numberOfSeats } : {}), + ...(o.useCase ? { useCase: o.useCase } : {}), + ...(o.hostingType ? { hostingType: o.hostingType } : {}), + ...(o.subscriptionType ? { subscriptionType: o.subscriptionType } : {}), + ...(o.subscriptionFrequence ? { subscriptionFrequency: o.subscriptionFrequence } : {}), + ...(o.lostReason ? { lostReason: o.lostReason } : {}), + ...(o.amount?.amountMicros != null ? { amount: { amountMicros: o.amount.amountMicros, currencyCode: o.amount.currencyCode ?? 'USD' } } : {}), + ...(o.closeDate ? { closeDate: o.closeDate } : {}), + ...(companyId && APPLY ? { companyId } : {}), + ...(partnerId && APPLY ? { partnerId } : {}), + }; + + const existingId = oppIdByTftId.get(o.id); + if (existingId) { + if (APPLY) { + await pace(); + await local.mutation({ updateOpportunity: { __args: { id: existingId, data }, id: true } } as any); + } + oppsUpdated++; + } else { + if (APPLY) { + await pace(); + await local.mutation({ createOpportunity: { __args: { data }, id: true } } as any); + } + oppsCreated++; + } + } + console.log(`[import] opportunities created=${oppsCreated} updated=${oppsUpdated} (partner-linked=${oppsPartnerLinked})`); + + // -- Partner quotes (upsert by name) -- + let quotesCreated = 0; + let quotesUpdated = 0; + tftContent.forEach((c: any) => (Array.isArray(c.typeCustom) ? c.typeCustom : []).forEach((t: string) => note('typeCustom', t))); + for (const q of quotes) { + note('quoteStatus', q.status); + const name = q.name || 'Partner quote'; + const partnerId = q.partnerPerson?.id ? localPartnerIdByTftPersonId.get(q.partnerPerson.id) : undefined; + const data: Record = { + name, + ...(q.status ? { status: q.status } : {}), + ...(q.approvalDate ? { approvalDate: q.approvalDate } : {}), + ...(partnerId && APPLY ? { partnerId } : {}), + }; + const existingId = quoteIdByName.get(name); + if (existingId) { + if (APPLY) { + await pace(); + await local.mutation({ updatePartnerQuote: { __args: { id: existingId, data }, id: true } } as any); + } + quotesUpdated++; + } else { + if (APPLY) { + await pace(); + await local.mutation({ createPartnerQuote: { __args: { data }, id: true } } as any); + } + quotesCreated++; + } + } + console.log(`[import] partner quotes created=${quotesCreated} updated=${quotesUpdated}`); + + // --------------------------------------------------------------------- + // 4. Preflight: distinct TFT values vs local option coverage. + // --------------------------------------------------------------------- + const report = (label: string, bucket: string, optionKey?: string) => { + const values = [...seen[bucket]].sort(); + const allowed = optionKey ? LOCAL_OPTIONS[optionKey] : undefined; + const uncovered = allowed ? values.filter((v) => !allowed.has(v)) : []; + console.log( + `[preflight] ${label}: ${values.join(', ') || '(none)'}` + + (uncovered.length ? ` ⚠️ NOT IN LOCAL OPTIONS: ${uncovered.join(', ')}` : ''), + ); + }; + console.log('--- preflight: distinct TFT values seen ---'); + report('partnerStage (-> validationStage map)', 'partnerStage'); + report('partnerTier', 'partnerTier', 'partnerTier'); + report('partnerScope', 'partnerScope', 'partnerScope'); + report('typeOfTeam', 'typeOfTeam', 'typeOfTeam'); + report('opp stage (-> matchStatus map)', 'oppStage'); + report('hostingType', 'hostingType', 'hostingType'); + report('subscriptionType', 'subscriptionType', 'subscriptionType'); + report('subscriptionFrequency', 'subscriptionFrequency', 'subscriptionFrequency'); + report('quote status', 'quoteStatus', 'quoteStatus'); + report('customerContent typeCustom', 'typeCustom'); + const unmappedStages = [...seen.partnerStage].filter((s) => !(s in PARTNER_STAGE_TO_VALIDATION)); + const unmappedOpps = [...seen.oppStage].filter((s) => !(s in STAGE_TO_MATCH_STATUS)); + if (unmappedStages.length) console.log(`[preflight] ⚠️ partnerStage not mapped: ${unmappedStages.join(', ')}`); + if (unmappedOpps.length) console.log(`[preflight] ⚠️ opp stage not mapped: ${unmappedOpps.join(', ')}`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/scripts/seed.ts b/packages/twenty-apps/internal/twenty-partners/src/scripts/seed.ts new file mode 100644 index 00000000000..524b984abb7 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/scripts/seed.ts @@ -0,0 +1,195 @@ +// Single demo seed for the twenty-partners app. Idempotent UPSERT by natural key. +// Covers: partners across ALL validationStage values, companies + people, +// opportunities across ALL 10 matchStatus values, and partner quotes across ALL +// 5 statuses (each linked to a partner + opportunity). +// +// Run from this app directory, against a running Twenty server with the app +// installed (deploy first; do NOT run `yarn test` after — its teardown wipes the app). +// Credentials from shell env or a gitignored .env.local (TWENTY_PARTNERS_API_URL/KEY). +// +// yarn twenty dev --once +// tsx src/scripts/seed.ts + +import { config } from 'dotenv'; +config({ path: '.env.local' }); + +import { CoreApiClient } from 'twenty-client-sdk/core'; + +const requireEnv = (name: string): string => { + const value = process.env[name]; + if (!value) throw new Error(`Missing ${name} env var`); + return value; +}; + +const CAL = 'https://calendly.com/placeholder'; + +type Partner = { + slug: string; + name: string; + validationStage: string; + availability: string; + introduction: string; + calendarLink: string; + deploymentExpertise: string[]; + region: string[]; + languagesSpoken: string[]; + partnerTier: string; + partnerScope: string[]; + typeOfTeam: string; + country: string; +}; + +const PARTNERS: Partner[] = [ + { slug: 'nine-dots-ventures', name: 'Nine Dots Ventures', validationStage: 'VALIDATED', availability: 'AVAILABLE', introduction: 'Boutique CRM implementer for real-estate workflows and WhatsApp automation.', calendarLink: CAL, deploymentExpertise: ['CLOUD', 'SELF_HOST'], region: ['EUROPE', 'MENA'], languagesSpoken: ['ENGLISH', 'FRENCH', 'ARABIC'], partnerTier: 'ADVANCED', partnerScope: ['DATA_MODEL', 'WORKFLOWS', 'APPS'], typeOfTeam: 'AGENCY', country: 'FRANCE' }, + { slug: 'elevate-consulting', name: 'Elevate Consulting', validationStage: 'VALIDATED', availability: 'AVAILABLE', introduction: 'Revenue-operations partner for B2B SaaS teams scaling seed to Series C.', calendarLink: CAL, deploymentExpertise: ['CLOUD'], region: ['US', 'LATAM'], languagesSpoken: ['ENGLISH', 'SPANISH'], partnerTier: 'INTERMEDIATE', partnerScope: ['DATA_MIGRATION', 'DATA_MODEL'], typeOfTeam: 'AGENCY', country: 'UNITED_STATES' }, + { slug: 'w3villa-technologies', name: 'W3Villa Technologies', validationStage: 'VALIDATED', availability: 'AVAILABLE', introduction: 'Engineering-heavy partner running large self-hosted Twenty deployments.', calendarLink: CAL, deploymentExpertise: ['CLOUD', 'SELF_HOST'], region: ['APAC', 'MENA'], languagesSpoken: ['ENGLISH', 'HINDI'], partnerTier: 'ADVANCED', partnerScope: ['HOSTING_ENVIRONMENT', 'APPS', 'WORKFLOWS'], typeOfTeam: 'AGENCY', country: 'INDIA' }, + { slug: 'act-education', name: 'Act Education', validationStage: 'VALIDATED', availability: 'UNAVAILABLE', introduction: 'CRM partner for European education providers; compliance-first self-hosting.', calendarLink: CAL, deploymentExpertise: ['SELF_HOST'], region: ['EUROPE'], languagesSpoken: ['ENGLISH', 'GERMAN'], partnerTier: 'NEW', partnerScope: ['HOSTING_ENVIRONMENT', 'DATA_MODEL'], typeOfTeam: 'SOLO', country: 'GERMANY' }, + { slug: 'netzero-systems', name: 'NetZero Systems', validationStage: 'VALIDATED', availability: 'AVAILABLE', introduction: 'LATAM go-to-market partner for climate-tech and renewable-energy companies.', calendarLink: CAL, deploymentExpertise: ['CLOUD'], region: ['LATAM', 'US'], languagesSpoken: ['ENGLISH', 'SPANISH', 'PORTUGUESE'], partnerTier: 'INTERMEDIATE', partnerScope: ['DATA_MODEL'], typeOfTeam: 'AGENCY', country: 'BRAZIL' }, + { slug: 'meridian-craft', name: 'Meridian Craft', validationStage: 'VALIDATED', availability: 'AVAILABLE', introduction: 'APAC implementation studio for fintech and logistics; English + Chinese.', calendarLink: CAL, deploymentExpertise: ['CLOUD', 'SELF_HOST'], region: ['APAC', 'AFRICA'], languagesSpoken: ['ENGLISH', 'CHINESE', 'MALAY'], partnerTier: 'ADVANCED', partnerScope: ['APPS', 'WORKFLOWS'], typeOfTeam: 'AGENCY', country: 'SINGAPORE' }, + { slug: 'applicant-studio', name: 'Applicant Studio', validationStage: 'APPLICATION', availability: 'UNAVAILABLE', introduction: 'New applicant; awaiting first review.', calendarLink: CAL, deploymentExpertise: ['CLOUD'], region: ['EUROPE'], languagesSpoken: ['ENGLISH', 'FRENCH'], partnerTier: 'NEW', partnerScope: ['DATA_MODEL'], typeOfTeam: 'SOLO', country: 'FRANCE' }, + { slug: 'rising-crm', name: 'Rising CRM', validationStage: 'POTENTIAL', availability: 'AVAILABLE', introduction: 'Promising applicant in evaluation.', calendarLink: CAL, deploymentExpertise: ['CLOUD', 'SELF_HOST'], region: ['US'], languagesSpoken: ['ENGLISH'], partnerTier: 'NEW', partnerScope: ['WORKFLOWS', 'APPS'], typeOfTeam: 'AGENCY', country: 'UNITED_STATES' }, + { slug: 'legacy-partners', name: 'Legacy Partners', validationStage: 'FORMER', availability: 'UNAVAILABLE', introduction: 'Former partner; no longer active in the program.', calendarLink: CAL, deploymentExpertise: ['SELF_HOST'], region: ['EUROPE'], languagesSpoken: ['ENGLISH', 'GERMAN'], partnerTier: 'INTERMEDIATE', partnerScope: ['HOSTING_ENVIRONMENT'], typeOfTeam: 'AGENCY', country: 'UNITED_KINGDOM' }, + { slug: 'declined-co', name: 'Declined Co', validationStage: 'REJECTED', availability: 'UNAVAILABLE', introduction: 'Application rejected after review.', calendarLink: CAL, deploymentExpertise: ['CLOUD'], region: ['MENA'], languagesSpoken: ['ENGLISH', 'ARABIC'], partnerTier: 'NEW', partnerScope: ['APPS'], typeOfTeam: 'SOLO', country: 'UNITED_ARAB_EMIRATES' }, +]; + +const COMPANIES = [ + { name: 'Acme Real Estate', domain: 'https://acmerealestate.example' }, + { name: 'Helix Bio', domain: 'https://helixbio.example' }, + { name: 'Sunrise Logistics', domain: 'https://sunriselogistics.example' }, +]; + +const PERSONS = [ + { firstName: 'Camille', lastName: 'Durand', companyName: 'Acme Real Estate', email: 'camille@acmerealestate.example', city: 'Paris' }, + { firstName: 'Maya', lastName: 'Patel', companyName: 'Helix Bio', email: 'maya@helixbio.example', city: 'Boston' }, + { firstName: 'Wei', lastName: 'Chen', companyName: 'Sunrise Logistics', email: 'wei@sunriselogistics.example', city: 'Singapore' }, +]; + +type Opp = { + name: string; + companyName: string; + matchStatus: string; + partnerSlug?: string; + numberOfSeats?: number; + hostingType?: string; + subscriptionType?: string; + subscriptionFrequency?: string; +}; + +// One+ opportunity for every matchStatus value (all 10 covered). +const OPPORTUNITIES: Opp[] = [ + { name: 'Acme RE — Q3 renewal', companyName: 'Acme Real Estate', matchStatus: 'TO_BE_MATCHED', numberOfSeats: 20, hostingType: 'CLOUD', subscriptionType: 'PRO', subscriptionFrequency: 'ANNUAL' }, + { name: 'Helix Bio — investor reporting', companyName: 'Helix Bio', matchStatus: 'MANUAL_MATCH', numberOfSeats: 12, hostingType: 'CLOUD', subscriptionType: 'ORG', subscriptionFrequency: 'MONTHLY' }, + { name: 'Helix Bio — pipeline review', companyName: 'Helix Bio', matchStatus: 'AUTO_MATCH', numberOfSeats: 8 }, + { name: 'Acme RE — CRM rollout', companyName: 'Acme Real Estate', matchStatus: 'MATCHED', partnerSlug: 'elevate-consulting', numberOfSeats: 30, hostingType: 'CLOUD', subscriptionType: 'ENT', subscriptionFrequency: 'ANNUAL' }, + { name: 'Sunrise — APAC fleet CRM', companyName: 'Sunrise Logistics', matchStatus: 'INTRODUCED_TO_A_PARTNER', partnerSlug: 'nine-dots-ventures', numberOfSeats: 50, hostingType: 'SELF_HOSTING', subscriptionType: 'ENT', subscriptionFrequency: 'ANNUAL' }, + { name: 'Helix Bio — clinical trials CRM', companyName: 'Helix Bio', matchStatus: 'WORKING_WITH_A_PARTNER', partnerSlug: 'netzero-systems', numberOfSeats: 25, hostingType: 'CLOUD', subscriptionType: 'ORG', subscriptionFrequency: 'MONTHLY' }, + { name: 'Helix Bio — self-host evaluation', companyName: 'Helix Bio', matchStatus: 'IMPLEMENTING', partnerSlug: 'meridian-craft', numberOfSeats: 40, hostingType: 'SELF_HOSTING', subscriptionType: 'ENT', subscriptionFrequency: 'ANNUAL' }, + { name: 'Sunrise — LATAM expansion', companyName: 'Sunrise Logistics', matchStatus: 'WON', partnerSlug: 'nine-dots-ventures', numberOfSeats: 60, hostingType: 'CLOUD', subscriptionType: 'ENT', subscriptionFrequency: 'ANNUAL' }, + { name: 'Acme RE — annual review', companyName: 'Acme Real Estate', matchStatus: 'RECONNECT_LATER', partnerSlug: 'w3villa-technologies', numberOfSeats: 15 }, + { name: 'Sunrise — vendor onboarding', companyName: 'Sunrise Logistics', matchStatus: 'LOST', numberOfSeats: 10, hostingType: 'CLOUD', subscriptionType: 'PRO', subscriptionFrequency: 'MONTHLY' }, +]; + +type Quote = { name: string; status: string; partnerSlug: string; oppName: string }; +const QUOTES: Quote[] = [ + { name: 'Sunrise APAC fleet — Nine Dots quote', status: 'WIP', partnerSlug: 'nine-dots-ventures', oppName: 'Sunrise — APAC fleet CRM' }, + { name: 'Helix clinical — NetZero quote', status: 'INTERVIEW_SCHEDULED', partnerSlug: 'netzero-systems', oppName: 'Helix Bio — clinical trials CRM' }, + { name: 'Acme rollout — Elevate quote', status: 'UNDER_CUSTOMER_PARTNER_REVIEW', partnerSlug: 'elevate-consulting', oppName: 'Acme RE — CRM rollout' }, + { name: 'Sunrise LATAM — Nine Dots quote', status: 'APPROVED', partnerSlug: 'nine-dots-ventures', oppName: 'Sunrise — LATAM expansion' }, + { name: 'Helix self-host — Meridian quote', status: 'REJECTED', partnerSlug: 'meridian-craft', oppName: 'Helix Bio — self-host evaluation' }, +]; + +const nodes = (r: any, key: string): any[] => (r?.[key]?.edges ?? []).map((e: any) => e.node); + +async function main() { + const client = new CoreApiClient({ + url: `${requireEnv('TWENTY_PARTNERS_API_URL').replace(/\/$/, '')}/graphql`, + headers: { Authorization: `Bearer ${requireEnv('TWENTY_PARTNERS_API_KEY')}` }, + }); + + // -- Partners (upsert by slug) -- + const existingPartners = nodes( + await client.query({ partners: { __args: { filter: { slug: { in: PARTNERS.map((p) => p.slug) } }, first: 100 }, edges: { node: { id: true, slug: true } } } } as any), + 'partners', + ); + const partnerIdBySlug = new Map(existingPartners.map((n: any) => [n.slug, n.id])); + for (const p of PARTNERS) { + const data = { + name: p.name, slug: p.slug, validationStage: p.validationStage, availability: p.availability, + introduction: p.introduction, calendarLink: { primaryLinkUrl: p.calendarLink }, + deploymentExpertise: p.deploymentExpertise, region: p.region, languagesSpoken: p.languagesSpoken, + partnerTier: p.partnerTier, partnerScope: p.partnerScope, typeOfTeam: p.typeOfTeam, + country: p.country, + }; + const id = partnerIdBySlug.get(p.slug); + if (id) { + await client.mutation({ updatePartner: { __args: { id, data }, id: true } } as any); + } else { + const r: any = await client.mutation({ createPartner: { __args: { data }, id: true } } as any); + partnerIdBySlug.set(p.slug, r.createPartner.id); + } + } + console.log(`[seed] partners: ${partnerIdBySlug.size}`); + + // -- Companies (upsert by name) -- + const companyIdByName = new Map(); + for (const c of COMPANIES) { + const existing = nodes(await client.query({ companies: { __args: { filter: { name: { eq: c.name } }, first: 1 }, edges: { node: { id: true } } } } as any), 'companies'); + let id = existing[0]?.id; + if (!id) { + const r: any = await client.mutation({ createCompany: { __args: { data: { name: c.name, domainName: { primaryLinkUrl: c.domain } } }, id: true } } as any); + id = r.createCompany.id; + } + companyIdByName.set(c.name, id); + } + + // -- People (upsert by firstName+lastName) -- + for (const person of PERSONS) { + const existing = nodes(await client.query({ people: { __args: { filter: { name: { firstName: { eq: person.firstName } } }, first: 10 }, edges: { node: { id: true, name: { firstName: true, lastName: true } } } } } as any), 'people'); + const match = existing.find((n: any) => n.name?.firstName === person.firstName && n.name?.lastName === person.lastName); + if (!match) { + await client.mutation({ createPerson: { __args: { data: { name: { firstName: person.firstName, lastName: person.lastName }, emails: { primaryEmail: person.email }, city: person.city, companyId: companyIdByName.get(person.companyName) } }, id: true } } as any); + } + } + + // -- Opportunities (upsert by name) -- + const oppIdByName = new Map(); + for (const o of OPPORTUNITIES) { + const data: Record = { + name: o.name, matchStatus: o.matchStatus, companyId: companyIdByName.get(o.companyName), + ...(o.partnerSlug ? { partnerId: partnerIdBySlug.get(o.partnerSlug) } : {}), + ...(o.numberOfSeats != null ? { numberOfSeats: o.numberOfSeats } : {}), + ...(o.hostingType ? { hostingType: o.hostingType } : {}), + ...(o.subscriptionType ? { subscriptionType: o.subscriptionType } : {}), + ...(o.subscriptionFrequency ? { subscriptionFrequency: o.subscriptionFrequency } : {}), + }; + const existing = nodes(await client.query({ opportunities: { __args: { filter: { name: { eq: o.name } }, first: 1 }, edges: { node: { id: true } } } } as any), 'opportunities'); + let id = existing[0]?.id; + if (id) { + await client.mutation({ updateOpportunity: { __args: { id, data }, id: true } } as any); + } else { + const r: any = await client.mutation({ createOpportunity: { __args: { data }, id: true } } as any); + id = r.createOpportunity.id; + } + oppIdByName.set(o.name, id); + } + console.log(`[seed] opportunities: ${oppIdByName.size}`); + + // -- Partner quotes (upsert by name) -- + let quoteCount = 0; + for (const q of QUOTES) { + const data = { name: q.name, status: q.status, partnerId: partnerIdBySlug.get(q.partnerSlug), opportunityId: oppIdByName.get(q.oppName) }; + const existing = nodes(await client.query({ partnerQuotes: { __args: { filter: { name: { eq: q.name } }, first: 1 }, edges: { node: { id: true } } } } as any), 'partnerQuotes'); + if (existing[0]?.id) { + await client.mutation({ updatePartnerQuote: { __args: { id: existing[0].id, data }, id: true } } as any); + } else { + await client.mutation({ createPartnerQuote: { __args: { data }, id: true } } as any); + } + quoteCount++; + } + console.log(`[seed] partner quotes: ${quoteCount}`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/views/all-matched-deals.view.ts b/packages/twenty-apps/internal/twenty-partners/src/views/all-matched-deals.view.ts new file mode 100644 index 00000000000..188ee478a60 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/views/all-matched-deals.view.ts @@ -0,0 +1,36 @@ +import { + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, + ViewFilterOperand, + ViewType, + defineView, +} from 'twenty-sdk/define'; + +import { + ALL_MATCHED_DEALS_VIEW_UNIVERSAL_IDENTIFIER, + MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +// Opportunities where a partner is engaged (MATCHED or later). Distinct from +// Matches overview, which also includes AUTO_MATCH (in-flight, no partner yet). +export default defineView({ + universalIdentifier: ALL_MATCHED_DEALS_VIEW_UNIVERSAL_IDENTIFIER, + name: 'All matched deals', + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: ViewType.TABLE, + fields: [ + { universalIdentifier: '76f6aea5-0e0b-4787-84f0-430d0799e913', fieldMetadataUniversalIdentifier: '20202020-8609-4f65-a2d9-44009eb422b5', position: 0, isVisible: true }, + { universalIdentifier: 'd9862d49-eff8-4103-9f48-a193cf8e1de2', fieldMetadataUniversalIdentifier: MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, position: 1, isVisible: true }, + { universalIdentifier: '91c42a01-4ec8-4527-b9ac-9bdeb58e7243', fieldMetadataUniversalIdentifier: 'd9eeacaa-2f9e-44cc-b5f6-5e1526256d49', position: 2, isVisible: true }, + { universalIdentifier: 'c9689260-86f5-4e19-a86c-7afc95d4d6fe', fieldMetadataUniversalIdentifier: 'cc6b8a59-f860-493f-8b9a-f138c078fbf1', position: 3, isVisible: true }, + { universalIdentifier: 'd51c2737-26f8-4f27-b078-7bb0cf58c662', fieldMetadataUniversalIdentifier: 'fcf39b0c-0547-415e-806d-b238131ad7cc', position: 4, isVisible: true }, + ], + filters: [ + { + universalIdentifier: '71de9b3a-e59b-4baf-99e6-84fe01e037ee', + fieldMetadataUniversalIdentifier: MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, + // See filter-syntax note in Task 8.1. + operand: ViewFilterOperand.IS, + value: ['MATCHED', 'INTRODUCED_TO_A_PARTNER', 'WORKING_WITH_A_PARTNER', 'IMPLEMENTING', 'WON', 'RECONNECT_LATER', 'LOST'], + }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/views/all-opportunities.view.ts b/packages/twenty-apps/internal/twenty-partners/src/views/all-opportunities.view.ts new file mode 100644 index 00000000000..ff8adff4c0b --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/views/all-opportunities.view.ts @@ -0,0 +1,31 @@ +import { + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, + ViewType, + defineView, +} from 'twenty-sdk/define'; + +import { + ALL_OPPORTUNITIES_VIEW_UNIVERSAL_IDENTIFIER, + MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +// Default Opportunities view replacement. Surfaces matchStatus + partner alongside +// the standard Opportunity columns. partnerEligible column was dropped as part of +// the match-status redesign. +export default defineView({ + universalIdentifier: ALL_OPPORTUNITIES_VIEW_UNIVERSAL_IDENTIFIER, + name: 'Opportunities', + icon: 'IconTargetArrow', + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: ViewType.TABLE, + fields: [ + { universalIdentifier: '62844317-546c-4b65-a292-917bf0b5bfce', fieldMetadataUniversalIdentifier: '20202020-8609-4f65-a2d9-44009eb422b5', position: 0, isVisible: true }, + { universalIdentifier: '295a5b86-0b37-475a-8645-26f7e7a3dd0a', fieldMetadataUniversalIdentifier: '20202020-cbac-457e-b565-adece5fc815f', position: 1, isVisible: true }, + { universalIdentifier: 'ce684df4-4456-427c-b33b-38a34368e380', fieldMetadataUniversalIdentifier: '20202020-6f76-477d-8551-28cd65b2b4b9', position: 2, isVisible: true }, + { universalIdentifier: '9f72d1ce-7c39-418c-95cb-480d1b176821', fieldMetadataUniversalIdentifier: 'd9eeacaa-2f9e-44cc-b5f6-5e1526256d49', position: 3, isVisible: true }, + { universalIdentifier: '5db9ee26-8688-4a5c-9fe8-f76b41d8e80b', fieldMetadataUniversalIdentifier: MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, position: 4, isVisible: true }, + { universalIdentifier: '3727d213-e3f5-43c7-ab05-b0fb2f211273', fieldMetadataUniversalIdentifier: '20202020-527e-44d6-b1ac-c4158d307b97', position: 5, isVisible: true }, + { universalIdentifier: 'c9ad9056-fd3a-448c-b4dc-e95e0c5d22e9', fieldMetadataUniversalIdentifier: '20202020-a63e-4a62-8e63-42a51828f831', position: 6, isVisible: true }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/views/all-partners.view.ts b/packages/twenty-apps/internal/twenty-partners/src/views/all-partners.view.ts new file mode 100644 index 00000000000..46ef85c3ca1 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/views/all-partners.view.ts @@ -0,0 +1,22 @@ +import { ViewType, defineView } from 'twenty-sdk/define'; + +import { + ALL_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER, + PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineView({ + universalIdentifier: ALL_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER, + name: 'Partners', + objectUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + type: ViewType.TABLE, + fields: [ + { universalIdentifier: '21afcc69-09c5-42eb-a609-26c062de3bd3', fieldMetadataUniversalIdentifier: 'a0000001-0000-4000-8000-000000000001', position: 0, isVisible: true }, + { universalIdentifier: '529912f0-38fb-4821-92d3-8a0a68b9f340', fieldMetadataUniversalIdentifier: '2ca9856f-f54a-4326-9ff3-668fd7da0b50', position: 1, isVisible: true }, + { universalIdentifier: '68c6b96d-8c3d-4a3e-b4cd-3751d035b085', fieldMetadataUniversalIdentifier: 'a0000004-0000-4000-8000-000000000004', position: 2, isVisible: true }, + { universalIdentifier: '8862e4a5-525a-4a0c-8381-93ff0d01ccf0', fieldMetadataUniversalIdentifier: 'a0000007-0000-4000-8000-000000000007', position: 3, isVisible: true }, + { universalIdentifier: '4ebe0b9d-0c2d-4416-b187-150b02473a01', fieldMetadataUniversalIdentifier: 'a0000010-0000-4000-8000-000000000010', position: 4, isVisible: true }, + { universalIdentifier: '52408b5f-5e13-4e3c-af2d-ce50033ec126', fieldMetadataUniversalIdentifier: '560503de-6330-4c1d-af97-a8dee125f2ad', position: 5, isVisible: true }, + { universalIdentifier: '02cc471b-e9ba-4643-b403-40299d6bbbdd', fieldMetadataUniversalIdentifier: 'a0000005-0000-4000-8000-000000000005', position: 6, isVisible: true }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/views/matches-overview.view.ts b/packages/twenty-apps/internal/twenty-partners/src/views/matches-overview.view.ts new file mode 100644 index 00000000000..f39b3e9de8f --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/views/matches-overview.view.ts @@ -0,0 +1,29 @@ +import { + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, + ViewType, + defineView, +} from 'twenty-sdk/define'; + +import { + MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, + MATCHES_OVERVIEW_VIEW_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +// Full matching funnel: a Kanban grouped by matchStatus, no filter (every +// opportunity appears in its matchStatus column). +export default defineView({ + universalIdentifier: MATCHES_OVERVIEW_VIEW_UNIVERSAL_IDENTIFIER, + name: 'Matches overview', + icon: 'IconLayoutKanban', + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: ViewType.KANBAN, + mainGroupByFieldMetadataUniversalIdentifier: MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, + fields: [ + { universalIdentifier: '7a6403c1-7ab9-4c3a-b833-3c028d43140e', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.fields.name.universalIdentifier, position: 0, isVisible: true }, + { universalIdentifier: '2e718b4b-fde8-4839-9cdf-deb09db0e6b6', fieldMetadataUniversalIdentifier: 'd9eeacaa-2f9e-44cc-b5f6-5e1526256d49', position: 1, isVisible: true }, + { universalIdentifier: '5ae8805c-3d71-4ccc-a2be-38368f32e3e1', fieldMetadataUniversalIdentifier: MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, position: 2, isVisible: true }, + { universalIdentifier: 'cb4e5d2b-7003-4f30-874c-acda310b250c', fieldMetadataUniversalIdentifier: 'fcf39b0c-0547-415e-806d-b238131ad7cc', position: 3, isVisible: true }, + { universalIdentifier: '0fc87e70-7aa1-4c85-9152-d0edff8ae8a4', fieldMetadataUniversalIdentifier: 'cc6b8a59-f860-493f-8b9a-f138c078fbf1', position: 4, isVisible: true }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/views/partner-applications.view.ts b/packages/twenty-apps/internal/twenty-partners/src/views/partner-applications.view.ts new file mode 100644 index 00000000000..21ac3280489 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/views/partner-applications.view.ts @@ -0,0 +1,29 @@ +import { ViewFilterOperand, ViewType, defineView } from 'twenty-sdk/define'; + +import { + PARTNER_APPLICATIONS_VIEW_UNIVERSAL_IDENTIFIER, + PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +// Partners still in the application stage (not yet validated). +export default defineView({ + universalIdentifier: PARTNER_APPLICATIONS_VIEW_UNIVERSAL_IDENTIFIER, + name: 'Partner applications', + icon: 'IconUserPlus', + objectUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + type: ViewType.TABLE, + fields: [ + { universalIdentifier: 'b4f505d7-3849-4a74-a27f-1c91733702b5', fieldMetadataUniversalIdentifier: 'a0000001-0000-4000-8000-000000000001', position: 0, isVisible: true }, + { universalIdentifier: '8a39e510-e533-4cd7-9b65-5e16b5f773d0', fieldMetadataUniversalIdentifier: '2ca9856f-f54a-4326-9ff3-668fd7da0b50', position: 1, isVisible: true }, + { universalIdentifier: 'b92d7fd4-4a24-4333-89fe-5d726634d428', fieldMetadataUniversalIdentifier: 'd4fa6461-37b6-49ee-9181-dd482e74a70b', position: 2, isVisible: true }, + { universalIdentifier: '2c34a120-b0f8-421b-9546-6483f1202d9f', fieldMetadataUniversalIdentifier: 'a77d7fa6-c398-47db-af0f-036a5c719f20', position: 3, isVisible: true }, + ], + filters: [ + { + universalIdentifier: '210deb57-cfca-4086-b9da-ca346fbd3126', + fieldMetadataUniversalIdentifier: '2ca9856f-f54a-4326-9ff3-668fd7da0b50', + operand: ViewFilterOperand.IS, + value: ['APPLICATION'], + }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/views/partner-quotes.view.ts b/packages/twenty-apps/internal/twenty-partners/src/views/partner-quotes.view.ts new file mode 100644 index 00000000000..268946b93b6 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/views/partner-quotes.view.ts @@ -0,0 +1,21 @@ +import { ViewType, defineView } from 'twenty-sdk/define'; + +import { + PARTNER_QUOTES_VIEW_UNIVERSAL_IDENTIFIER, + PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +// Index view for partner quotes. +export default defineView({ + universalIdentifier: PARTNER_QUOTES_VIEW_UNIVERSAL_IDENTIFIER, + name: 'Partner quotes', + icon: 'IconFileDollar', + objectUniversalIdentifier: PARTNER_QUOTE_OBJECT_UNIVERSAL_IDENTIFIER, + type: ViewType.TABLE, + fields: [ + { universalIdentifier: 'ad7b3702-b552-4355-afec-1e1e96d9f3df', fieldMetadataUniversalIdentifier: '9e688624-83d2-4715-8b18-80492a6de2b6', position: 0, isVisible: true }, + { universalIdentifier: '426e0c2d-449d-4a06-860b-0cfe0ed501e6', fieldMetadataUniversalIdentifier: 'a0fe09c4-c1f4-4b96-93c6-d7ec38f1166a', position: 1, isVisible: true }, + { universalIdentifier: '62bb1536-539c-4fb0-95bf-440c5c5da89f', fieldMetadataUniversalIdentifier: '2cbe67e3-24ec-421b-bab5-50f3306c2391', position: 2, isVisible: true }, + { universalIdentifier: 'fbd1f953-1dd2-4d0f-a239-148a0688fbff', fieldMetadataUniversalIdentifier: 'b52d263e-423e-40b0-b82c-29214597c005', position: 3, isVisible: true }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/views/validated-partners.view.ts b/packages/twenty-apps/internal/twenty-partners/src/views/validated-partners.view.ts new file mode 100644 index 00000000000..4fcd863a1eb --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/views/validated-partners.view.ts @@ -0,0 +1,30 @@ +import { ViewFilterOperand, ViewType, defineView } from 'twenty-sdk/define'; + +import { + PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + VALIDATED_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +// Validated partners — serves both partner intros and the public website list. +export default defineView({ + universalIdentifier: VALIDATED_PARTNERS_VIEW_UNIVERSAL_IDENTIFIER, + name: 'Validated partners', + icon: 'IconCircleCheck', + objectUniversalIdentifier: PARTNER_OBJECT_UNIVERSAL_IDENTIFIER, + type: ViewType.TABLE, + fields: [ + { universalIdentifier: '9463bf58-69d5-4309-bc6e-4835df346246', fieldMetadataUniversalIdentifier: 'a0000001-0000-4000-8000-000000000001', position: 0, isVisible: true }, + { universalIdentifier: 'c8fd88c5-ffa9-4944-b5b9-deec3acd3dff', fieldMetadataUniversalIdentifier: 'd4fa6461-37b6-49ee-9181-dd482e74a70b', position: 1, isVisible: true }, + { universalIdentifier: '9cb93542-2a91-4e75-a9f8-4a1866445322', fieldMetadataUniversalIdentifier: '500021ad-ca42-4fd3-8727-392dd26b722a', position: 2, isVisible: true }, + { universalIdentifier: 'd6df98ac-9c6c-46c2-8784-d4e1d6521f75', fieldMetadataUniversalIdentifier: '560503de-6330-4c1d-af97-a8dee125f2ad', position: 3, isVisible: true }, + { universalIdentifier: '375cc871-fdda-42d0-8308-e60174b6d467', fieldMetadataUniversalIdentifier: 'a0000004-0000-4000-8000-000000000004', position: 4, isVisible: true }, + ], + filters: [ + { + universalIdentifier: 'a3b7e215-693d-420c-b444-37d4109ca535', + fieldMetadataUniversalIdentifier: '2ca9856f-f54a-4326-9ff3-668fd7da0b50', + operand: ViewFilterOperand.IS, + value: ['VALIDATED'], + }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/src/views/waiting-for-match.view.ts b/packages/twenty-apps/internal/twenty-partners/src/views/waiting-for-match.view.ts new file mode 100644 index 00000000000..57cee76807c --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/src/views/waiting-for-match.view.ts @@ -0,0 +1,48 @@ +import { + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, + ViewFilterOperand, + ViewSortDirection, + ViewType, + defineView, +} from 'twenty-sdk/define'; + +import { + MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, + WAITING_FOR_MATCH_VIEW_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +// Ops inbox: opportunities awaiting a human matching decision. +// Includes TO_BE_MATCHED (default for new opps) and MANUAL_MATCH +// (opps that auto-match couldn't resolve or that ops opted to handle manually). +export default defineView({ + universalIdentifier: WAITING_FOR_MATCH_VIEW_UNIVERSAL_IDENTIFIER, + name: 'Waiting for match', + icon: 'IconClockHour4', + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier, + type: ViewType.KANBAN, + mainGroupByFieldMetadataUniversalIdentifier: MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, + fields: [ + { universalIdentifier: 'd74b5eb3-21ee-48fa-b703-4cfd629738b4', fieldMetadataUniversalIdentifier: '20202020-8609-4f65-a2d9-44009eb422b5', position: 0, isVisible: true }, + { universalIdentifier: '50822cf9-c238-4450-ba31-5807011afa65', fieldMetadataUniversalIdentifier: '20202020-cbac-457e-b565-adece5fc815f', position: 1, isVisible: true }, + { universalIdentifier: 'f432a71f-3bd0-495e-b5b1-8a78e155dc5a', fieldMetadataUniversalIdentifier: '20202020-d01b-4132-9b32-123456789abc', position: 2, isVisible: true }, + { universalIdentifier: '9e47592f-9965-4ee7-9c6a-303477b293f4', fieldMetadataUniversalIdentifier: 'cc6b8a59-f860-493f-8b9a-f138c078fbf1', position: 3, isVisible: true }, + // matchStatus column (replaces the dropped partnerEligible column) + { universalIdentifier: '909e1eee-077a-4f23-8c9b-4c8027623a78', fieldMetadataUniversalIdentifier: MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, position: 4, isVisible: true }, + ], + filters: [ + { + universalIdentifier: '93476207-1471-49d9-898c-f8a1d52f468f', + fieldMetadataUniversalIdentifier: MATCH_STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['TO_BE_MATCHED', 'MANUAL_MATCH'], + }, + ], + sorts: [ + { + universalIdentifier: 'a7c5a89e-d9d7-4cf6-a6d2-3ad9f12a7b1f', + fieldMetadataUniversalIdentifier: '20202020-d01b-4132-9b32-123456789abc', + direction: ViewSortDirection.ASC, + }, + ], +}); diff --git a/packages/twenty-apps/internal/twenty-partners/tsconfig.json b/packages/twenty-apps/internal/twenty-partners/tsconfig.json new file mode 100644 index 00000000000..d574c8c8100 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/tsconfig.json @@ -0,0 +1,42 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "sourceMap": true, + "declaration": true, + "outDir": "./dist", + "rootDir": ".", + "jsx": "react-jsx", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "importHelpers": true, + "allowUnreachableCode": false, + "strict": true, + "alwaysStrict": true, + "noImplicitAny": true, + "strictBindCallApply": false, + "target": "es2018", + "module": "esnext", + "lib": ["es2020", "dom"], + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "resolveJsonModule": true, + "paths": { + "src/*": ["./src/*"], + "~/*": ["./*"] + } + }, + "exclude": [ + "node_modules", + "dist", + "**/*.test.ts", + "**/*.spec.ts", + "**/*.integration-test.ts" + ], + "references": [ + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/packages/twenty-apps/internal/twenty-partners/tsconfig.spec.json b/packages/twenty-apps/internal/twenty-partners/tsconfig.spec.json new file mode 100644 index 00000000000..ea69a6c0105 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "composite": true, + "types": ["vitest/globals", "node"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/twenty-apps/internal/twenty-partners/vitest.config.ts b/packages/twenty-apps/internal/twenty-partners/vitest.config.ts new file mode 100644 index 00000000000..7ed938cad9c --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/vitest.config.ts @@ -0,0 +1,40 @@ +import { loadEnv } from 'vite'; +import tsconfigPaths from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +// Integration tests authenticate to a local Twenty server. Credentials are +// resolved from the shell env, then a gitignored .env.local in this directory +// (see .env.example). No API key is committed; if none is found, global-setup +// fails with a clear message. +const fileEnv = loadEnv('test', process.cwd(), 'TWENTY_'); + +const TWENTY_API_URL = + process.env.TWENTY_API_URL ?? fileEnv.TWENTY_API_URL ?? 'http://localhost:2020'; +const TWENTY_API_KEY = process.env.TWENTY_API_KEY ?? fileEnv.TWENTY_API_KEY; + +// Make env available to globalSetup (runs in the main process); test.env below +// covers the worker processes. +process.env.TWENTY_API_URL = TWENTY_API_URL; +if (TWENTY_API_KEY) { + process.env.TWENTY_API_KEY = TWENTY_API_KEY; +} + +export default defineConfig({ + plugins: [ + tsconfigPaths({ + projects: ['tsconfig.spec.json'], + ignoreConfigErrors: true, + }), + ], + test: { + testTimeout: 120_000, + hookTimeout: 120_000, + fileParallelism: false, + include: ['src/**/*.integration-test.ts'], + globalSetup: ['src/__tests__/global-setup.ts'], + env: { + TWENTY_API_URL, + ...(TWENTY_API_KEY ? { TWENTY_API_KEY } : {}), + }, + }, +}); diff --git a/packages/twenty-apps/internal/twenty-partners/yarn.lock b/packages/twenty-apps/internal/twenty-partners/yarn.lock new file mode 100644 index 00000000000..5db4798e5d9 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-partners/yarn.lock @@ -0,0 +1,4607 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@alcalzone/ansi-tokenize@npm:^0.2.4": + version: 0.2.5 + resolution: "@alcalzone/ansi-tokenize@npm:0.2.5" + dependencies: + ansi-styles: "npm:^6.2.1" + is-fullwidth-code-point: "npm:^5.0.0" + checksum: 10c0/dd8622288426b5b7dbf8b68d51d4b930cea591d0e3b9dbc3f523131464d78ac922c165fcb7ba5307f133d35dbcaa0e6648a1c3fb6a0dc2725546d6f867b70af4 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/aix-ppc64@npm:0.25.12" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/aix-ppc64@npm:0.27.7" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/aix-ppc64@npm:0.28.0" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm64@npm:0.25.12" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-arm64@npm:0.27.7" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/android-arm64@npm:0.28.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm@npm:0.25.12" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-arm@npm:0.27.7" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/android-arm@npm:0.28.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-x64@npm:0.25.12" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-x64@npm:0.27.7" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/android-x64@npm:0.28.0" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-arm64@npm:0.25.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/darwin-arm64@npm:0.27.7" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/darwin-arm64@npm:0.28.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-x64@npm:0.25.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/darwin-x64@npm:0.27.7" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/darwin-x64@npm:0.28.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-arm64@npm:0.25.12" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/freebsd-arm64@npm:0.27.7" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/freebsd-arm64@npm:0.28.0" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-x64@npm:0.25.12" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/freebsd-x64@npm:0.27.7" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/freebsd-x64@npm:0.28.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm64@npm:0.25.12" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-arm64@npm:0.27.7" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/linux-arm64@npm:0.28.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm@npm:0.25.12" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-arm@npm:0.27.7" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/linux-arm@npm:0.28.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ia32@npm:0.25.12" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-ia32@npm:0.27.7" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/linux-ia32@npm:0.28.0" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-loong64@npm:0.25.12" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-loong64@npm:0.27.7" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/linux-loong64@npm:0.28.0" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-mips64el@npm:0.25.12" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-mips64el@npm:0.27.7" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/linux-mips64el@npm:0.28.0" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ppc64@npm:0.25.12" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-ppc64@npm:0.27.7" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/linux-ppc64@npm:0.28.0" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-riscv64@npm:0.25.12" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-riscv64@npm:0.27.7" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/linux-riscv64@npm:0.28.0" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-s390x@npm:0.25.12" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-s390x@npm:0.27.7" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/linux-s390x@npm:0.28.0" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-x64@npm:0.25.12" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-x64@npm:0.27.7" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/linux-x64@npm:0.28.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-arm64@npm:0.25.12" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/netbsd-arm64@npm:0.27.7" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/netbsd-arm64@npm:0.28.0" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-x64@npm:0.25.12" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/netbsd-x64@npm:0.27.7" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/netbsd-x64@npm:0.28.0" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-arm64@npm:0.25.12" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openbsd-arm64@npm:0.27.7" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/openbsd-arm64@npm:0.28.0" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-x64@npm:0.25.12" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openbsd-x64@npm:0.27.7" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/openbsd-x64@npm:0.28.0" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openharmony-arm64@npm:0.25.12" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openharmony-arm64@npm:0.27.7" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/openharmony-arm64@npm:0.28.0" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/sunos-x64@npm:0.25.12" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/sunos-x64@npm:0.27.7" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/sunos-x64@npm:0.28.0" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-arm64@npm:0.25.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-arm64@npm:0.27.7" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/win32-arm64@npm:0.28.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-ia32@npm:0.25.12" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-ia32@npm:0.27.7" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/win32-ia32@npm:0.28.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-x64@npm:0.25.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-x64@npm:0.27.7" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.28.0": + version: 0.28.0 + resolution: "@esbuild/win32-x64@npm:0.28.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@fastify/busboy@npm:^2.0.0": + version: 2.1.1 + resolution: "@fastify/busboy@npm:2.1.1" + checksum: 10c0/6f8027a8cba7f8f7b736718b013f5a38c0476eea67034c94a0d3c375e2b114366ad4419e6a6fa7ffc2ef9c6d3e0435d76dd584a7a1cbac23962fda7650b579e3 + languageName: node + linkType: hard + +"@genql/cli@npm:^3.0.3": + version: 3.0.5 + resolution: "@genql/cli@npm:3.0.5" + dependencies: + "@graphql-tools/graphql-file-loader": "npm:^7.5.11" + "@graphql-tools/load": "npm:^7.8.6" + fs-extra: "npm:^10.1.0" + graphql: "npm:^16.6.0" + kleur: "npm:^4.1.5" + listr: "npm:^0.14.3" + lodash: "npm:^4.17.21" + mkdirp: "npm:^0.5.1" + native-fetch: "npm:^4.0.2" + prettier: "npm:^2.8.0" + qs: "npm:^6.11.0" + rimraf: "npm:^2.6.3" + undici: "npm:^5.18.0" + yargs: "npm:^15.3.1" + bin: + genql: dist/cli.js + checksum: 10c0/646d4da8986741a8f1b610bd8cfb5bc26cd9a856de671461e4ca87fb05443f37dd0edfb3b30ceaa34cd049077873afa07febfd4714dd2e0681fb07484e19a080 + languageName: node + linkType: hard + +"@genql/runtime@npm:^2.10.0": + version: 2.10.0 + resolution: "@genql/runtime@npm:2.10.0" + dependencies: + "@types/qs": "npm:^6.9.0" + "@types/ws": "npm:^6.0.1" + graphql-query-batcher: "npm:^1.0.1" + isomorphic-unfetch: "npm:^3.0.0" + lodash: "npm:^4.17.20" + subscriptions-transport-ws: "npm:^0.9.16" + tslib: "npm:^2.0.0" + utility-types: "npm:^3.10.0" + ws: "npm:^6.1.4" + zen-observable-ts: "npm:^0.8.21" + peerDependencies: + graphql: "*" + checksum: 10c0/e2a886c2469c933681e2b0ddd6a5b7f4cb12932251ba460e3cf2db4246817da79313ea4ba9769ec7cbe53ab9c1cb81ad8fcce6a969cd241185b79398d2a4f3c6 + languageName: node + linkType: hard + +"@graphql-tools/graphql-file-loader@npm:^7.5.11": + version: 7.5.17 + resolution: "@graphql-tools/graphql-file-loader@npm:7.5.17" + dependencies: + "@graphql-tools/import": "npm:6.7.18" + "@graphql-tools/utils": "npm:^9.2.1" + globby: "npm:^11.0.3" + tslib: "npm:^2.4.0" + unixify: "npm:^1.0.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10c0/f737f14357731ad01da57755e1cf26ce375b475209d6ab7e4b656b56191a8979d2ab7dd5d1c54a1f11e04374f7a373fa95ea5ec6a001d0cef913ea208fadc65b + languageName: node + linkType: hard + +"@graphql-tools/import@npm:6.7.18": + version: 6.7.18 + resolution: "@graphql-tools/import@npm:6.7.18" + dependencies: + "@graphql-tools/utils": "npm:^9.2.1" + resolve-from: "npm:5.0.0" + tslib: "npm:^2.4.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10c0/d33e37a1879dd43ac2851c9bac2f2873c58bb3687f1c06e159760dbb5e540ef074d688df70cc6dbd3ee5de48d437878df8f65a7c65ae80bd025bf98f2d615732 + languageName: node + linkType: hard + +"@graphql-tools/load@npm:^7.8.6": + version: 7.8.14 + resolution: "@graphql-tools/load@npm:7.8.14" + dependencies: + "@graphql-tools/schema": "npm:^9.0.18" + "@graphql-tools/utils": "npm:^9.2.1" + p-limit: "npm:3.1.0" + tslib: "npm:^2.4.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10c0/1fa036ac596ccf48f350aa545d108c173184d9b53247f9e21c0d4ba96c5cba4a0b44281f9154f122e1e8e9d9d6eab93a5b2618ca8a797969bde1e75c1d45e786 + languageName: node + linkType: hard + +"@graphql-tools/merge@npm:^8.4.1": + version: 8.4.2 + resolution: "@graphql-tools/merge@npm:8.4.2" + dependencies: + "@graphql-tools/utils": "npm:^9.2.1" + tslib: "npm:^2.4.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10c0/2df55222b48e010e683572f456cf265aabae5748c59f7c1260c66dec9794b7a076d3706f04da969b77f0a32c7ccb4551fee30125931d3fe9c98a8806aae9a3f4 + languageName: node + linkType: hard + +"@graphql-tools/schema@npm:^9.0.18": + version: 9.0.19 + resolution: "@graphql-tools/schema@npm:9.0.19" + dependencies: + "@graphql-tools/merge": "npm:^8.4.1" + "@graphql-tools/utils": "npm:^9.2.1" + tslib: "npm:^2.4.0" + value-or-promise: "npm:^1.0.12" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10c0/42fd8ca8d3c8d60b583077c201980518482ff0cd5ed0c1f14bd9b835a2689ad41d02cbd3478f7d7dea7aec1227f7639fd5deb5e6360852a2e542b96b44bfb7a4 + languageName: node + linkType: hard + +"@graphql-tools/utils@npm:^9.2.1": + version: 9.2.1 + resolution: "@graphql-tools/utils@npm:9.2.1" + dependencies: + "@graphql-typed-document-node/core": "npm:^3.1.1" + tslib: "npm:^2.4.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10c0/37a7bd7e14d28ff1bacc007dca84bc6cef2d7d7af9a547b5dbe52fcd134afddd6d4a7b2148cfbaff5ddba91a868453d597da77bd0457fb0be15928f916901606 + languageName: node + linkType: hard + +"@graphql-typed-document-node/core@npm:^3.1.1": + version: 3.2.0 + resolution: "@graphql-typed-document-node/core@npm:3.2.0" + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10c0/94e9d75c1f178bbae8d874f5a9361708a3350c8def7eaeb6920f2c820e82403b7d4f55b3735856d68e145e86c85cbfe2adc444fdc25519cd51f108697e99346c + languageName: node + linkType: hard + +"@inquirer/checkbox@npm:^2.5.0": + version: 2.5.0 + resolution: "@inquirer/checkbox@npm:2.5.0" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/figures": "npm:^1.0.5" + "@inquirer/type": "npm:^1.5.3" + ansi-escapes: "npm:^4.3.2" + yoctocolors-cjs: "npm:^2.1.2" + checksum: 10c0/679d17ffe3aef0825593f3bc8d193b6c37b860c6cf6e0e9a10d4e60cc254a2dfc5da4a982bf5b9b5147018e456fffcb0b0dadf93ee1914b9d600b0c814284e22 + languageName: node + linkType: hard + +"@inquirer/confirm@npm:^3.2.0": + version: 3.2.0 + resolution: "@inquirer/confirm@npm:3.2.0" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/type": "npm:^1.5.3" + checksum: 10c0/a2cbfc8ae9c880bba4cce1993f5c399fb0d12741fdd574917c87fceb40ece62ffa60e35aaadf4e62d7c114f54008e45aee5d6d90497bb62d493996c02725d243 + languageName: node + linkType: hard + +"@inquirer/core@npm:^9.1.0": + version: 9.2.1 + resolution: "@inquirer/core@npm:9.2.1" + dependencies: + "@inquirer/figures": "npm:^1.0.6" + "@inquirer/type": "npm:^2.0.0" + "@types/mute-stream": "npm:^0.0.4" + "@types/node": "npm:^22.5.5" + "@types/wrap-ansi": "npm:^3.0.0" + ansi-escapes: "npm:^4.3.2" + cli-width: "npm:^4.1.0" + mute-stream: "npm:^1.0.0" + signal-exit: "npm:^4.1.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^6.2.0" + yoctocolors-cjs: "npm:^2.1.2" + checksum: 10c0/11c14be77a9fa85831de799a585721b0a49ab2f3b7d8fd1780c48ea2b29229c6bdc94e7892419086d0f7734136c2ba87b6a32e0782571eae5bbd655b1afad453 + languageName: node + linkType: hard + +"@inquirer/editor@npm:^2.2.0": + version: 2.2.0 + resolution: "@inquirer/editor@npm:2.2.0" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/type": "npm:^1.5.3" + external-editor: "npm:^3.1.0" + checksum: 10c0/b8afc0790a7a5d82998bdfe469cbaa83b0cd0700be432cf95256c548e2a6a494997b5e93d65cbf94979c17b510758cf8494d85559f6b9508eb15d239a7f22aee + languageName: node + linkType: hard + +"@inquirer/expand@npm:^2.3.0": + version: 2.3.0 + resolution: "@inquirer/expand@npm:2.3.0" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/type": "npm:^1.5.3" + yoctocolors-cjs: "npm:^2.1.2" + checksum: 10c0/f2030cb482a715e4d5153c19b3f0fd8bf47c16cdc16e1c669e90985386edf4f7b0f3b0e97e2990bb228878b93716228eb067d94fc557c25d3c5ee58747c0a995 + languageName: node + linkType: hard + +"@inquirer/figures@npm:^1.0.5, @inquirer/figures@npm:^1.0.6": + version: 1.0.15 + resolution: "@inquirer/figures@npm:1.0.15" + checksum: 10c0/6e39a040d260ae234ae220180b7994ff852673e20be925f8aa95e78c7934d732b018cbb4d0ec39e600a410461bcb93dca771e7de23caa10630d255692e440f69 + languageName: node + linkType: hard + +"@inquirer/input@npm:^2.3.0": + version: 2.3.0 + resolution: "@inquirer/input@npm:2.3.0" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/type": "npm:^1.5.3" + checksum: 10c0/44c8cea38c9192f528cae556f38709135a00230132deab3b9bb9a925375fce0513fecf4e8c1df7c4319e1ed7aa31fb4dd2c4956c8bc9dd39af087aafff5b6f1f + languageName: node + linkType: hard + +"@inquirer/number@npm:^1.1.0": + version: 1.1.0 + resolution: "@inquirer/number@npm:1.1.0" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/type": "npm:^1.5.3" + checksum: 10c0/db472dab57c951c4a083b2a749ce58262b1efd9889e7603de6e9c3f9af7d8dce8fbdfa3859f65402d3587470e0397a076e5fb4ed775db33310f17a42c9faeb20 + languageName: node + linkType: hard + +"@inquirer/password@npm:^2.2.0": + version: 2.2.0 + resolution: "@inquirer/password@npm:2.2.0" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/type": "npm:^1.5.3" + ansi-escapes: "npm:^4.3.2" + checksum: 10c0/fa4b335164b2c9c3304d29a7214ef93bac8d3da6788146603ea3d0485b8d811151e49bf66cb0dcc729a9dc21406c3a8c2718c5beec572a91d07026d22842c13f + languageName: node + linkType: hard + +"@inquirer/prompts@npm:^5.5.0": + version: 5.5.0 + resolution: "@inquirer/prompts@npm:5.5.0" + dependencies: + "@inquirer/checkbox": "npm:^2.5.0" + "@inquirer/confirm": "npm:^3.2.0" + "@inquirer/editor": "npm:^2.2.0" + "@inquirer/expand": "npm:^2.3.0" + "@inquirer/input": "npm:^2.3.0" + "@inquirer/number": "npm:^1.1.0" + "@inquirer/password": "npm:^2.2.0" + "@inquirer/rawlist": "npm:^2.3.0" + "@inquirer/search": "npm:^1.1.0" + "@inquirer/select": "npm:^2.5.0" + checksum: 10c0/2d62b50ca761b2bd2d5759f48c03758f1af0665ac602c26ae1ae257ac512cf5c27fd82cde108ee0c6371ec39187adc6f45637f31ca79adf5bf96579f23e77143 + languageName: node + linkType: hard + +"@inquirer/rawlist@npm:^2.3.0": + version: 2.3.0 + resolution: "@inquirer/rawlist@npm:2.3.0" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/type": "npm:^1.5.3" + yoctocolors-cjs: "npm:^2.1.2" + checksum: 10c0/d49d5e12b7a54394c140b27c8d8748ba1ab855c67c01fa72b5a63810f12865df3bf4d5ae929f54fad77b5fc2f7431a332ae1e5fe4babb335380c28917002f364 + languageName: node + linkType: hard + +"@inquirer/search@npm:^1.1.0": + version: 1.1.0 + resolution: "@inquirer/search@npm:1.1.0" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/figures": "npm:^1.0.5" + "@inquirer/type": "npm:^1.5.3" + yoctocolors-cjs: "npm:^2.1.2" + checksum: 10c0/20d7e910266b9e3f0dc8eef8f3007f487e6149fa8421d293eaf7c11a1e35c3d82aa30af118b3a6e35eed1048a27d7d806f45722abb10005db5d099ea64b00b17 + languageName: node + linkType: hard + +"@inquirer/select@npm:^2.5.0": + version: 2.5.0 + resolution: "@inquirer/select@npm:2.5.0" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/figures": "npm:^1.0.5" + "@inquirer/type": "npm:^1.5.3" + ansi-escapes: "npm:^4.3.2" + yoctocolors-cjs: "npm:^2.1.2" + checksum: 10c0/280fa700187ff29da0ad4bf32aa11db776261584ddf5cc1ceac5caebb242a4ac0c5944af522a2579d78b6ec7d6e8b1b9f6564872101abd8dcc69929b4e33fc4c + languageName: node + linkType: hard + +"@inquirer/type@npm:^1.5.3": + version: 1.5.5 + resolution: "@inquirer/type@npm:1.5.5" + dependencies: + mute-stream: "npm:^1.0.0" + checksum: 10c0/4c41736c09ba9426b5a9e44993bdd54e8f532e791518802e33866f233a2a6126a25c1c82c19d1abbf1df627e57b1b957dd3f8318ea96073d8bfc32193943bcb3 + languageName: node + linkType: hard + +"@inquirer/type@npm:^2.0.0": + version: 2.0.0 + resolution: "@inquirer/type@npm:2.0.0" + dependencies: + mute-stream: "npm:^1.0.0" + checksum: 10c0/8c663d52beb2b89a896d3c3d5cc3d6d024fa149e565555bcb42fa640cbe23fba7ff2c51445342cef1fe6e46305e2d16c1590fa1d11ad0ddf93a67b655ef41f0a + languageName: node + linkType: hard + +"@isaacs/fs-minipass@npm:^4.0.0": + version: 4.0.1 + resolution: "@isaacs/fs-minipass@npm:4.0.1" + dependencies: + minipass: "npm:^7.0.4" + checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2 + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.5.5": + version: 1.5.5 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" + checksum: 10c0/f9e538f302b63c0ebc06eecb1dd9918dd4289ed36147a0ddce35d6ea4d7ebbda243cda7b2213b6a5e1d8087a298d5cf630fb2bd39329cdecb82017023f6081a0 + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 + languageName: node + linkType: hard + +"@oxlint/darwin-arm64@npm:0.16.12": + version: 0.16.12 + resolution: "@oxlint/darwin-arm64@npm:0.16.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@oxlint/darwin-x64@npm:0.16.12": + version: 0.16.12 + resolution: "@oxlint/darwin-x64@npm:0.16.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@oxlint/linux-arm64-gnu@npm:0.16.12": + version: 0.16.12 + resolution: "@oxlint/linux-arm64-gnu@npm:0.16.12" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@oxlint/linux-arm64-musl@npm:0.16.12": + version: 0.16.12 + resolution: "@oxlint/linux-arm64-musl@npm:0.16.12" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@oxlint/linux-x64-gnu@npm:0.16.12": + version: 0.16.12 + resolution: "@oxlint/linux-x64-gnu@npm:0.16.12" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@oxlint/linux-x64-musl@npm:0.16.12": + version: 0.16.12 + resolution: "@oxlint/linux-x64-musl@npm:0.16.12" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@oxlint/win32-arm64@npm:0.16.12": + version: 0.16.12 + resolution: "@oxlint/win32-arm64@npm:0.16.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@oxlint/win32-x64@npm:0.16.12": + version: 0.16.12 + resolution: "@oxlint/win32-x64@npm:0.16.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.60.4" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-android-arm64@npm:4.60.4" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-darwin-arm64@npm:4.60.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-darwin-x64@npm:4.60.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-arm64@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.60.4" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-x64@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-freebsd-x64@npm:4.60.4" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.60.4" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.60.4" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.60.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.60.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.60.4" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-musl@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.60.4" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-gnu@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.60.4" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-musl@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.60.4" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.60.4" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.60.4" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.60.4" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.60.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.60.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-openbsd-x64@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-openbsd-x64@npm:4.60.4" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.60.4" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.60.4" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.60.4" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.60.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.60.4": + version: 4.60.4 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.60.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@samverschueren/stream-to-observable@npm:^0.3.0": + version: 0.3.1 + resolution: "@samverschueren/stream-to-observable@npm:0.3.1" + dependencies: + any-observable: "npm:^0.3.0" + peerDependenciesMeta: + rxjs: + optional: true + zen-observable: + optional: true + checksum: 10c0/0d874453f6bc2460d71783292291f52feb36c2a75314b1072a6ffe6206562f33e9d664a554348d565a6b54da9041d75070371052545bc329caaa52f64216987f + languageName: node + linkType: hard + +"@sniptt/guards@npm:^0.2.0": + version: 0.2.0 + resolution: "@sniptt/guards@npm:0.2.0" + checksum: 10c0/749bb0f550d1ddd4abdb23dc1076cba26e977659922b73d000bceeb253c09270aaced0d18e92f09d4ce2fdaae33e55f60f2142f5359bc90ba505422af0873526 + languageName: node + linkType: hard + +"@types/chai@npm:^5.2.2": + version: 5.2.3 + resolution: "@types/chai@npm:5.2.3" + dependencies: + "@types/deep-eql": "npm:*" + assertion-error: "npm:^2.0.1" + checksum: 10c0/e0ef1de3b6f8045a5e473e867c8565788c444271409d155588504840ad1a53611011f85072188c2833941189400228c1745d78323dac13fcede9c2b28bacfb2f + languageName: node + linkType: hard + +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844 + languageName: node + linkType: hard + +"@types/estree@npm:1.0.8": + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 + languageName: node + linkType: hard + +"@types/estree@npm:^1.0.0": + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 + languageName: node + linkType: hard + +"@types/mute-stream@npm:^0.0.4": + version: 0.0.4 + resolution: "@types/mute-stream@npm:0.0.4" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/944730fd7b398c5078de3c3d4d0afeec8584283bc694da1803fdfca14149ea385e18b1b774326f1601baf53898ce6d121a952c51eb62d188ef6fcc41f725c0dc + languageName: node + linkType: hard + +"@types/node@npm:*": + version: 25.8.0 + resolution: "@types/node@npm:25.8.0" + dependencies: + undici-types: "npm:>=7.24.0 <7.24.7" + checksum: 10c0/ff53e5428309d2e6060190ec5e02afd0e4a7369456b16130a7f5898f12a6ad0efd62d752830f2f7355d714ae429bc0acbb2dc0cbf761cadb03e88c4996cdf1dc + languageName: node + linkType: hard + +"@types/node@npm:^22.5.5": + version: 22.19.19 + resolution: "@types/node@npm:22.19.19" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/402e0f088c94cabda3cd721546bd8e4e75e098e0b342f6e03b90ca1e19c28986f9650112c64fcfd09fc8cebc0f8b20291a513153e90489331cf666e1e5503e16 + languageName: node + linkType: hard + +"@types/node@npm:^24.7.2": + version: 24.12.4 + resolution: "@types/node@npm:24.12.4" + dependencies: + undici-types: "npm:~7.16.0" + checksum: 10c0/d2c36b78b6050d8677769fa05a32243061675e81ddc2bb43955d91a671af3465506ef2731a24c0c9ab42b6b679bd5c1513de45bbe9ea278c2c07ee63b564b61b + languageName: node + linkType: hard + +"@types/qs@npm:^6.9.0": + version: 6.15.1 + resolution: "@types/qs@npm:6.15.1" + checksum: 10c0/1dfdbcb4cf2a8f66d57f0b9a9fe6b1c7091cb816687b6698c1351eaf31f62e412cea9b7453a9637b570cd5fad8dced527e5a9e69b4fcc6e318daacd8b749f094 + languageName: node + linkType: hard + +"@types/react@npm:^19.0.0": + version: 19.2.14 + resolution: "@types/react@npm:19.2.14" + dependencies: + csstype: "npm:^3.2.2" + checksum: 10c0/7d25bf41b57719452d86d2ac0570b659210402707313a36ee612666bf11275a1c69824f8c3ee1fdca077ccfe15452f6da8f1224529b917050eb2d861e52b59b7 + languageName: node + linkType: hard + +"@types/wrap-ansi@npm:^3.0.0": + version: 3.0.0 + resolution: "@types/wrap-ansi@npm:3.0.0" + checksum: 10c0/8d8f53363f360f38135301a06b596c295433ad01debd082078c33c6ed98b05a5c8fe8853a88265432126096084f4a135ec1564e3daad631b83296905509f90b3 + languageName: node + linkType: hard + +"@types/ws@npm:^6.0.1": + version: 6.0.4 + resolution: "@types/ws@npm:6.0.4" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/fa958e64596ca9487c3ed6012834de70b47f25d971f1950cfb8e6a99cb77ff340ae82ac7627744e01b58010674ef8ede07d5a2ac29ca9ad0d67a430fcc69ae14 + languageName: node + linkType: hard + +"@vitest/expect@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/expect@npm:3.2.4" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:3.2.4" + "@vitest/utils": "npm:3.2.4" + chai: "npm:^5.2.0" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/7586104e3fd31dbe1e6ecaafb9a70131e4197dce2940f727b6a84131eee3decac7b10f9c7c72fa5edbdb68b6f854353bd4c0fa84779e274207fb7379563b10db + languageName: node + linkType: hard + +"@vitest/mocker@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/mocker@npm:3.2.4" + dependencies: + "@vitest/spy": "npm:3.2.4" + estree-walker: "npm:^3.0.3" + magic-string: "npm:^0.30.17" + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + checksum: 10c0/f7a4aea19bbbf8f15905847ee9143b6298b2c110f8b64789224cb0ffdc2e96f9802876aa2ca83f1ec1b6e1ff45e822abb34f0054c24d57b29ab18add06536ccd + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:3.2.4, @vitest/pretty-format@npm:^3.2.4": + version: 3.2.4 + resolution: "@vitest/pretty-format@npm:3.2.4" + dependencies: + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/5ad7d4278e067390d7d633e307fee8103958806a419ca380aec0e33fae71b44a64415f7a9b4bc11635d3c13d4a9186111c581d3cef9c65cc317e68f077456887 + languageName: node + linkType: hard + +"@vitest/runner@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/runner@npm:3.2.4" + dependencies: + "@vitest/utils": "npm:3.2.4" + pathe: "npm:^2.0.3" + strip-literal: "npm:^3.0.0" + checksum: 10c0/e8be51666c72b3668ae3ea348b0196656a4a5adb836cb5e270720885d9517421815b0d6c98bfdf1795ed02b994b7bfb2b21566ee356a40021f5bf4f6ed4e418a + languageName: node + linkType: hard + +"@vitest/snapshot@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/snapshot@npm:3.2.4" + dependencies: + "@vitest/pretty-format": "npm:3.2.4" + magic-string: "npm:^0.30.17" + pathe: "npm:^2.0.3" + checksum: 10c0/f8301a3d7d1559fd3d59ed51176dd52e1ed5c2d23aa6d8d6aa18787ef46e295056bc726a021698d8454c16ed825ecba163362f42fa90258bb4a98cfd2c9424fc + languageName: node + linkType: hard + +"@vitest/spy@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/spy@npm:3.2.4" + dependencies: + tinyspy: "npm:^4.0.3" + checksum: 10c0/6ebf0b4697dc238476d6b6a60c76ba9eb1dd8167a307e30f08f64149612fd50227682b876420e4c2e09a76334e73f72e3ebf0e350714dc22474258292e202024 + languageName: node + linkType: hard + +"@vitest/utils@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/utils@npm:3.2.4" + dependencies: + "@vitest/pretty-format": "npm:3.2.4" + loupe: "npm:^3.1.4" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/024a9b8c8bcc12cf40183c246c244b52ecff861c6deb3477cbf487ac8781ad44c68a9c5fd69f8c1361878e55b97c10d99d511f2597f1f7244b5e5101d028ba64 + languageName: node + linkType: hard + +"abbrev@npm:^4.0.0": + version: 4.0.0 + resolution: "abbrev@npm:4.0.0" + checksum: 10c0/b4cc16935235e80702fc90192e349e32f8ef0ed151ef506aa78c81a7c455ec18375c4125414b99f84b2e055199d66383e787675f0bcd87da7a4dbd59f9eac1d5 + languageName: node + linkType: hard + +"agent-base@npm:6": + version: 6.0.2 + resolution: "agent-base@npm:6.0.2" + dependencies: + debug: "npm:4" + checksum: 10c0/dc4f757e40b5f3e3d674bc9beb4f1048f4ee83af189bae39be99f57bf1f48dde166a8b0a5342a84b5944ee8e6ed1e5a9d801858f4ad44764e84957122fe46261 + languageName: node + linkType: hard + +"ansi-escapes@npm:^3.0.0": + version: 3.2.0 + resolution: "ansi-escapes@npm:3.2.0" + checksum: 10c0/084e1ce38139ad2406f18a8e7efe2b850ddd06ce3c00f633392d1ce67756dab44fe290e573d09ef3c9a0cb13c12881e0e35a8f77a017d39a0a4ab85ae2fae04f + languageName: node + linkType: hard + +"ansi-escapes@npm:^4.3.2": + version: 4.3.2 + resolution: "ansi-escapes@npm:4.3.2" + dependencies: + type-fest: "npm:^0.21.3" + checksum: 10c0/da917be01871525a3dfcf925ae2977bc59e8c513d4423368645634bf5d4ceba5401574eb705c1e92b79f7292af5a656f78c5725a4b0e1cec97c4b413705c1d50 + languageName: node + linkType: hard + +"ansi-escapes@npm:^7.3.0": + version: 7.3.0 + resolution: "ansi-escapes@npm:7.3.0" + dependencies: + environment: "npm:^1.0.0" + checksum: 10c0/068961d99f0ef28b661a4a9f84a5d645df93ccf3b9b93816cc7d46bbe1913321d4cdf156bb842a4e1e4583b7375c631fa963efb43001c4eb7ff9ab8f78fc0679 + languageName: node + linkType: hard + +"ansi-regex@npm:^2.0.0": + version: 2.1.1 + resolution: "ansi-regex@npm:2.1.1" + checksum: 10c0/78cebaf50bce2cb96341a7230adf28d804611da3ce6bf338efa7b72f06cc6ff648e29f80cd95e582617ba58d5fdbec38abfeed3500a98bce8381a9daec7c548b + languageName: node + linkType: hard + +"ansi-regex@npm:^3.0.0": + version: 3.0.1 + resolution: "ansi-regex@npm:3.0.1" + checksum: 10c0/d108a7498b8568caf4a46eea4f1784ab4e0dfb2e3f3938c697dee21443d622d765c958f2b7e2b9f6b9e55e2e2af0584eaa9915d51782b89a841c28e744e7a167 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 + languageName: node + linkType: hard + +"ansi-regex@npm:^6.2.2": + version: 6.2.2 + resolution: "ansi-regex@npm:6.2.2" + checksum: 10c0/05d4acb1d2f59ab2cf4b794339c7b168890d44dda4bf0ce01152a8da0213aca207802f930442ce8cd22d7a92f44907664aac6508904e75e038fa944d2601b30f + languageName: node + linkType: hard + +"ansi-styles@npm:^2.2.1": + version: 2.2.1 + resolution: "ansi-styles@npm:2.2.1" + checksum: 10c0/7c68aed4f1857389e7a12f85537ea5b40d832656babbf511cc7ecd9efc52889b9c3e5653a71a6aade783c3c5e0aa223ad4ff8e83c27ac8a666514e6c79068cab + languageName: node + linkType: hard + +"ansi-styles@npm:^3.2.1": + version: 3.2.1 + resolution: "ansi-styles@npm:3.2.1" + dependencies: + color-convert: "npm:^1.9.0" + checksum: 10c0/ece5a8ef069fcc5298f67e3f4771a663129abd174ea2dfa87923a2be2abf6cd367ef72ac87942da00ce85bd1d651d4cd8595aebdb1b385889b89b205860e977b + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 + languageName: node + linkType: hard + +"ansi-styles@npm:^6.2.1, ansi-styles@npm:^6.2.3": + version: 6.2.3 + resolution: "ansi-styles@npm:6.2.3" + checksum: 10c0/23b8a4ce14e18fb854693b95351e286b771d23d8844057ed2e7d083cd3e708376c3323707ec6a24365f7d7eda3ca00327fe04092e29e551499ec4c8b7bfac868 + languageName: node + linkType: hard + +"any-observable@npm:^0.3.0": + version: 0.3.0 + resolution: "any-observable@npm:0.3.0" + checksum: 10c0/104c2b79c2ac7e6c75b35f8fd62babf73015668f22bd25336c6f848350d91f9e7daf2fddbf1c1b76fe795e89fbc91b49f70a2aec5c69f1acf0562c344f36042b + languageName: node + linkType: hard + +"array-union@npm:^2.1.0": + version: 2.1.0 + resolution: "array-union@npm:2.1.0" + checksum: 10c0/429897e68110374f39b771ec47a7161fc6a8fc33e196857c0a396dc75df0b5f65e4d046674db764330b6bb66b39ef48dd7c53b6a2ee75cfb0681e0c1a7033962 + languageName: node + linkType: hard + +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: 10c0/bbbcb117ac6480138f8c93cf7f535614282dea9dc828f540cdece85e3c665e8f78958b96afac52f29ff883c72638e6a87d469ecc9fe5bc902df03ed24a55dba8 + languageName: node + linkType: hard + +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 10c0/669a32c2cb7e45091330c680e92eaeb791bc1d4132d827591e499cd1f776ff5a873e77e5f92d0ce795a8d60f10761dec9ddfe7225a5de680f5d357f67b1aac73 + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 10c0/2c50ef856c543ad500d8d8777d347e3c1ba623b93e99c9263ecc5f965c1b12d2a140e2ab6e43c3d0b85366110696f28114649411cbcd10b452a92a2318394186 + languageName: node + linkType: hard + +"async-limiter@npm:~1.0.0": + version: 1.0.1 + resolution: "async-limiter@npm:1.0.1" + checksum: 10c0/0693d378cfe86842a70d4c849595a0bb50dc44c11649640ca982fa90cbfc74e3cc4753b5a0847e51933f2e9c65ce8e05576e75e5e1fd963a086e673735b35969 + languageName: node + linkType: hard + +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d + languageName: node + linkType: hard + +"auto-bind@npm:^5.0.1": + version: 5.0.1 + resolution: "auto-bind@npm:5.0.1" + checksum: 10c0/a703375350ea7b6e92405d8e6bcc6dbfb84b0d7c7172b33e5788a7593929a18227999ff9aa9c32436741d06d021e6672457b1cec73287efe3fab95cff6627eaf + languageName: node + linkType: hard + +"axios@npm:^1.13.5": + version: 1.16.1 + resolution: "axios@npm:1.16.1" + dependencies: + follow-redirects: "npm:^1.16.0" + form-data: "npm:^4.0.5" + https-proxy-agent: "npm:^5.0.1" + proxy-from-env: "npm:^2.1.0" + checksum: 10c0/2f77e37e6552bbff8a772d058fb09500198e9188c6b20dc799d82dbe12a8cb506f6eed4e4e62a9ba612a35cbab496faa26d68f9bff14a53af6d15c3e136391a7 + languageName: node + linkType: hard + +"backo2@npm:^1.0.2": + version: 1.0.2 + resolution: "backo2@npm:1.0.2" + checksum: 10c0/a9e825a6a38a6d1c4a94476eabc13d6127dfaafb0967baf104affbb67806ae26abbb58dab8d572d2cd21ef06634ff57c3ad48dff14b904e18de1474cc2f22bf3 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.14 + resolution: "brace-expansion@npm:1.1.14" + dependencies: + balanced-match: "npm:^1.0.0" + concat-map: "npm:0.0.1" + checksum: 10c0/b6fdac832bc4e36a753658c9ed052c2e1a2be221763b002df25d1efbf7d21724334e726a6cd5eadc72a4b19ec3efb632d629cc003bc9c62f7af7a7915ffa4385 + languageName: node + linkType: hard + +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 + languageName: node + linkType: hard + +"cac@npm:^6.7.14": + version: 6.7.14 + resolution: "cac@npm:6.7.14" + checksum: 10c0/4ee06aaa7bab8981f0d54e5f5f9d4adcd64058e9697563ce336d8a3878ed018ee18ebe5359b2430eceae87e0758e62ea2019c3f52ae6e211b1bd2e133856cd10 + languageName: node + linkType: hard + +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10c0/47bd9901d57b857590431243fea704ff18078b16890a6b3e021e12d279bbf211d039155e27d7566b374d49ee1f8189344bac9833dec7a20cdec370506361c938 + languageName: node + linkType: hard + +"call-bound@npm:^1.0.2": + version: 1.0.4 + resolution: "call-bound@npm:1.0.4" + dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + get-intrinsic: "npm:^1.3.0" + checksum: 10c0/f4796a6a0941e71c766aea672f63b72bc61234c4f4964dc6d7606e3664c307e7d77845328a8f3359ce39ddb377fed67318f9ee203dea1d47e46165dcf2917644 + languageName: node + linkType: hard + +"camelcase@npm:^5.0.0": + version: 5.3.1 + resolution: "camelcase@npm:5.3.1" + checksum: 10c0/92ff9b443bfe8abb15f2b1513ca182d16126359ad4f955ebc83dc4ddcc4ef3fdd2c078bc223f2673dc223488e75c99b16cc4d056624374b799e6a1555cf61b23 + languageName: node + linkType: hard + +"chai@npm:^5.2.0": + version: 5.3.3 + resolution: "chai@npm:5.3.3" + dependencies: + assertion-error: "npm:^2.0.1" + check-error: "npm:^2.1.1" + deep-eql: "npm:^5.0.1" + loupe: "npm:^3.1.0" + pathval: "npm:^2.0.0" + checksum: 10c0/b360fd4d38861622e5010c2f709736988b05c7f31042305fa3f4e9911f6adb80ccfb4e302068bf8ed10e835c2e2520cba0f5edc13d878b886987e5aa62483f53 + languageName: node + linkType: hard + +"chalk@npm:^1.0.0, chalk@npm:^1.1.3": + version: 1.1.3 + resolution: "chalk@npm:1.1.3" + dependencies: + ansi-styles: "npm:^2.2.1" + escape-string-regexp: "npm:^1.0.2" + has-ansi: "npm:^2.0.0" + strip-ansi: "npm:^3.0.0" + supports-color: "npm:^2.0.0" + checksum: 10c0/28c3e399ec286bb3a7111fd4225ebedb0d7b813aef38a37bca7c498d032459c265ef43404201d5fbb8d888d29090899c95335b4c0cda13e8b126ff15c541cef8 + languageName: node + linkType: hard + +"chalk@npm:^2.4.1": + version: 2.4.2 + resolution: "chalk@npm:2.4.2" + dependencies: + ansi-styles: "npm:^3.2.1" + escape-string-regexp: "npm:^1.0.5" + supports-color: "npm:^5.3.0" + checksum: 10c0/e6543f02ec877732e3a2d1c3c3323ddb4d39fbab687c23f526e25bd4c6a9bf3b83a696e8c769d078e04e5754921648f7821b2a2acfd16c550435fd630026e073 + languageName: node + linkType: hard + +"chalk@npm:^5.3.0, chalk@npm:^5.6.0": + version: 5.6.2 + resolution: "chalk@npm:5.6.2" + checksum: 10c0/99a4b0f0e7991796b1e7e3f52dceb9137cae2a9dfc8fc0784a550dc4c558e15ab32ed70b14b21b52beb2679b4892b41a0aa44249bcb996f01e125d58477c6976 + languageName: node + linkType: hard + +"chardet@npm:^0.7.0": + version: 0.7.0 + resolution: "chardet@npm:0.7.0" + checksum: 10c0/96e4731b9ec8050cbb56ab684e8c48d6c33f7826b755802d14e3ebfdc51c57afeece3ea39bc6b09acc359e4363525388b915e16640c1378053820f5e70d0f27d + languageName: node + linkType: hard + +"check-error@npm:^2.1.1": + version: 2.1.3 + resolution: "check-error@npm:2.1.3" + checksum: 10c0/878e99038fb6476316b74668cd6a498c7e66df3efe48158fa40db80a06ba4258742ac3ee2229c4a2a98c5e73f5dff84eb3e50ceb6b65bbd8f831eafc8338607d + languageName: node + linkType: hard + +"chokidar@npm:^4.0.0": + version: 4.0.3 + resolution: "chokidar@npm:4.0.3" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/a58b9df05bb452f7d105d9e7229ac82fa873741c0c40ddcc7bb82f8a909fbe3f7814c9ebe9bc9a2bef9b737c0ec6e2d699d179048ef06ad3ec46315df0ebe6ad + languageName: node + linkType: hard + +"chownr@npm:^3.0.0": + version: 3.0.0 + resolution: "chownr@npm:3.0.0" + checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10 + languageName: node + linkType: hard + +"cli-boxes@npm:^3.0.0": + version: 3.0.0 + resolution: "cli-boxes@npm:3.0.0" + checksum: 10c0/4db3e8fbfaf1aac4fb3a6cbe5a2d3fa048bee741a45371b906439b9ffc821c6e626b0f108bdcd3ddf126a4a319409aedcf39a0730573ff050fdd7b6731e99fb9 + languageName: node + linkType: hard + +"cli-cursor@npm:^2.0.0, cli-cursor@npm:^2.1.0": + version: 2.1.0 + resolution: "cli-cursor@npm:2.1.0" + dependencies: + restore-cursor: "npm:^2.0.0" + checksum: 10c0/09ee6d8b5b818d840bf80ec9561eaf696672197d3a02a7daee2def96d5f52ce6e0bbe7afca754ccf14f04830b5a1b4556273e983507d5029f95bba3016618eda + languageName: node + linkType: hard + +"cli-cursor@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-cursor@npm:4.0.0" + dependencies: + restore-cursor: "npm:^4.0.0" + checksum: 10c0/e776e8c3c6727300d0539b0d25160b2bb56aed1a63942753ba1826b012f337a6f4b7ace3548402e4f2f13b5e16bfd751be672c44b203205e7eca8be94afec42c + languageName: node + linkType: hard + +"cli-truncate@npm:^0.2.1": + version: 0.2.1 + resolution: "cli-truncate@npm:0.2.1" + dependencies: + slice-ansi: "npm:0.0.4" + string-width: "npm:^1.0.1" + checksum: 10c0/c6caa5e2b70d841c42f4a2270d6fc7129df915f8911e4afa90c79231ccc857cd819a2c90e0707fde04e51ce56b4d71646b843f6cbaff4f7cdcb3b91ed51f6e89 + languageName: node + linkType: hard + +"cli-truncate@npm:^5.1.1": + version: 5.2.0 + resolution: "cli-truncate@npm:5.2.0" + dependencies: + slice-ansi: "npm:^8.0.0" + string-width: "npm:^8.2.0" + checksum: 10c0/0d4ec94702ca85b64522ac93633837fb5ea7db17b79b1322a60f6045e6ae2b8cd7bd4c1d19ac7d1f9e10e3bbda1112e172e439b68c02b785ee00da8d6a5c5471 + languageName: node + linkType: hard + +"cli-width@npm:^4.1.0": + version: 4.1.0 + resolution: "cli-width@npm:4.1.0" + checksum: 10c0/1fbd56413578f6117abcaf858903ba1f4ad78370a4032f916745fa2c7e390183a9d9029cf837df320b0fdce8137668e522f60a30a5f3d6529ff3872d265a955f + languageName: node + linkType: hard + +"cliui@npm:^6.0.0": + version: 6.0.0 + resolution: "cliui@npm:6.0.0" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.0" + wrap-ansi: "npm:^6.2.0" + checksum: 10c0/35229b1bb48647e882104cac374c9a18e34bbf0bace0e2cf03000326b6ca3050d6b59545d91e17bfe3705f4a0e2988787aa5cde6331bf5cbbf0164732cef6492 + languageName: node + linkType: hard + +"code-excerpt@npm:^4.0.0": + version: 4.0.0 + resolution: "code-excerpt@npm:4.0.0" + dependencies: + convert-to-spaces: "npm:^2.0.1" + checksum: 10c0/b6c5a06e039cecd2ab6a0e10ee0831de8362107d1f298ca3558b5f9004cb8e0260b02dd6c07f57b9a0e346c76864d2873311ee1989809fdeb05bd5fbbadde773 + languageName: node + linkType: hard + +"code-point-at@npm:^1.0.0": + version: 1.1.0 + resolution: "code-point-at@npm:1.1.0" + checksum: 10c0/33f6b234084e46e6e369b6f0b07949392651b4dde70fc6a592a8d3dafa08d5bb32e3981a02f31f6fc323a26bc03a4c063a9d56834848695bda7611c2417ea2e6 + languageName: node + linkType: hard + +"color-convert@npm:^1.9.0": + version: 1.9.3 + resolution: "color-convert@npm:1.9.3" + dependencies: + color-name: "npm:1.1.3" + checksum: 10c0/5ad3c534949a8c68fca8fbc6f09068f435f0ad290ab8b2f76841b9e6af7e0bb57b98cb05b0e19fe33f5d91e5a8611ad457e5f69e0a484caad1f7487fd0e8253c + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 + languageName: node + linkType: hard + +"color-name@npm:1.1.3": + version: 1.1.3 + resolution: "color-name@npm:1.1.3" + checksum: 10c0/566a3d42cca25b9b3cd5528cd7754b8e89c0eb646b7f214e8e2eaddb69994ac5f0557d9c175eb5d8f0ad73531140d9c47525085ee752a91a2ab15ab459caf6d6 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 + languageName: node + linkType: hard + +"combined-stream@npm:^1.0.8": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: "npm:~1.0.0" + checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5 + languageName: node + linkType: hard + +"commander@npm:^12.0.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f + languageName: node + linkType: hard + +"convert-to-spaces@npm:^2.0.1": + version: 2.0.1 + resolution: "convert-to-spaces@npm:2.0.1" + checksum: 10c0/d90aa0e3b6a27f9d5265a8d32def3c5c855b3e823a9db1f26d772f8146d6b91020a2fdfd905ce8048a73fad3aaf836fef8188c67602c374405e2ae8396c4ac46 + languageName: node + linkType: hard + +"csstype@npm:^3.2.2": + version: 3.2.3 + resolution: "csstype@npm:3.2.3" + checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce + languageName: node + linkType: hard + +"date-fns@npm:^1.27.2": + version: 1.30.1 + resolution: "date-fns@npm:1.30.1" + checksum: 10c0/bad6ad7c15180121e15d61ad62a4a214c108d66f35b35f5eeb6ade837a3c29aa4444b9528a93a5374b95ba11231c142276351bf52f4d168676f9a1e17ce3726a + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.4.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 + languageName: node + linkType: hard + +"decamelize@npm:^1.2.0": + version: 1.2.0 + resolution: "decamelize@npm:1.2.0" + checksum: 10c0/85c39fe8fbf0482d4a1e224ef0119db5c1897f8503bcef8b826adff7a1b11414972f6fef2d7dec2ee0b4be3863cf64ac1439137ae9e6af23a3d8dcbe26a5b4b2 + languageName: node + linkType: hard + +"deep-eql@npm:^5.0.1": + version: 5.0.2 + resolution: "deep-eql@npm:5.0.2" + checksum: 10c0/7102cf3b7bb719c6b9c0db2e19bf0aa9318d141581befe8c7ce8ccd39af9eaa4346e5e05adef7f9bd7015da0f13a3a25dcfe306ef79dc8668aedbecb658dd247 + languageName: node + linkType: hard + +"delayed-stream@npm:~1.0.0": + version: 1.0.0 + resolution: "delayed-stream@npm:1.0.0" + checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19 + languageName: node + linkType: hard + +"dir-glob@npm:^3.0.1": + version: 3.0.1 + resolution: "dir-glob@npm:3.0.1" + dependencies: + path-type: "npm:^4.0.0" + checksum: 10c0/dcac00920a4d503e38bb64001acb19df4efc14536ada475725e12f52c16777afdee4db827f55f13a908ee7efc0cb282e2e3dbaeeb98c0993dd93d1802d3bf00c + languageName: node + linkType: hard + +"dotenv@npm:^16.0.0, dotenv@npm:^16.4.0": + version: 16.6.1 + resolution: "dotenv@npm:16.6.1" + checksum: 10c0/15ce56608326ea0d1d9414a5c8ee6dcf0fffc79d2c16422b4ac2268e7e2d76ff5a572d37ffe747c377de12005f14b3cc22361e79fc7f1061cce81f77d2c973dc + languageName: node + linkType: hard + +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031 + languageName: node + linkType: hard + +"elegant-spinner@npm:^1.0.1": + version: 1.0.1 + resolution: "elegant-spinner@npm:1.0.1" + checksum: 10c0/df607c83c20fc3ce56c514175dd5d1ee7f667da00cee13d04d32c70d55e76555091fa236689e691cf7dedba17b0020fec635e499cdde84dbea2ef8639314e5f8 + languageName: node + linkType: hard + +"emoji-regex@npm:^10.3.0": + version: 10.6.0 + resolution: "emoji-regex@npm:10.6.0" + checksum: 10c0/1e4aa097bb007301c3b4b1913879ae27327fdc48e93eeefefe3b87e495eb33c5af155300be951b4349ff6ac084f4403dc9eff970acba7c1c572d89396a9a32d7 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 + languageName: node + linkType: hard + +"environment@npm:^1.0.0": + version: 1.1.0 + resolution: "environment@npm:1.1.0" + checksum: 10c0/fb26434b0b581ab397039e51ff3c92b34924a98b2039dcb47e41b7bca577b9dbf134a8eadb364415c74464b682e2d3afe1a4c0eb9873dc44ea814c5d3103331d + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10c0/3f54eb49c16c18707949ff25a1456728c883e81259f045003499efba399c08bad00deebf65cccde8c0e07908c1a225c9d472b7107e558f2a48e28d530e34527c + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + +"es-module-lexer@npm:^1.7.0": + version: 1.7.0 + resolution: "es-module-lexer@npm:1.7.0" + checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10c0/65364812ca4daf48eb76e2a3b7a89b3f6a2e62a1c420766ce9f692665a29d94fe41fe88b65f24106f449859549711e4b40d9fb8002d862dfd7eb1c512d10be0c + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10c0/ef2ca9ce49afe3931cb32e35da4dcb6d86ab02592cfc2ce3e49ced199d9d0bb5085fc7e73e06312213765f5efa47cc1df553a6a5154584b21448e9fb8355b1af + languageName: node + linkType: hard + +"es-toolkit@npm:^1.39.10": + version: 1.46.1 + resolution: "es-toolkit@npm:1.46.1" + dependenciesMeta: + "@trivago/prettier-plugin-sort-imports@4.3.0": + unplugged: true + prettier-plugin-sort-re-exports@0.0.1: + unplugged: true + checksum: 10c0/6a4c3dd0ddc2138fa13d983d93ebaf8061759c52c2cf7c3101315139fc1fca826d253daa67fe85ad880c03cc4c092bd53a83a7cbf58b4721c251479122ba5303 + languageName: node + linkType: hard + +"esbuild@npm:^0.25.0": + version: 0.25.12 + resolution: "esbuild@npm:0.25.12" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.12" + "@esbuild/android-arm": "npm:0.25.12" + "@esbuild/android-arm64": "npm:0.25.12" + "@esbuild/android-x64": "npm:0.25.12" + "@esbuild/darwin-arm64": "npm:0.25.12" + "@esbuild/darwin-x64": "npm:0.25.12" + "@esbuild/freebsd-arm64": "npm:0.25.12" + "@esbuild/freebsd-x64": "npm:0.25.12" + "@esbuild/linux-arm": "npm:0.25.12" + "@esbuild/linux-arm64": "npm:0.25.12" + "@esbuild/linux-ia32": "npm:0.25.12" + "@esbuild/linux-loong64": "npm:0.25.12" + "@esbuild/linux-mips64el": "npm:0.25.12" + "@esbuild/linux-ppc64": "npm:0.25.12" + "@esbuild/linux-riscv64": "npm:0.25.12" + "@esbuild/linux-s390x": "npm:0.25.12" + "@esbuild/linux-x64": "npm:0.25.12" + "@esbuild/netbsd-arm64": "npm:0.25.12" + "@esbuild/netbsd-x64": "npm:0.25.12" + "@esbuild/openbsd-arm64": "npm:0.25.12" + "@esbuild/openbsd-x64": "npm:0.25.12" + "@esbuild/openharmony-arm64": "npm:0.25.12" + "@esbuild/sunos-x64": "npm:0.25.12" + "@esbuild/win32-arm64": "npm:0.25.12" + "@esbuild/win32-ia32": "npm:0.25.12" + "@esbuild/win32-x64": "npm:0.25.12" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/c205357531423220a9de8e1e6c6514242bc9b1666e762cd67ccdf8fdfdc3f1d0bd76f8d9383958b97ad4c953efdb7b6e8c1f9ca5951cd2b7c5235e8755b34a6b + languageName: node + linkType: hard + +"esbuild@npm:^0.27.0": + version: 0.27.7 + resolution: "esbuild@npm:0.27.7" + dependencies: + "@esbuild/aix-ppc64": "npm:0.27.7" + "@esbuild/android-arm": "npm:0.27.7" + "@esbuild/android-arm64": "npm:0.27.7" + "@esbuild/android-x64": "npm:0.27.7" + "@esbuild/darwin-arm64": "npm:0.27.7" + "@esbuild/darwin-x64": "npm:0.27.7" + "@esbuild/freebsd-arm64": "npm:0.27.7" + "@esbuild/freebsd-x64": "npm:0.27.7" + "@esbuild/linux-arm": "npm:0.27.7" + "@esbuild/linux-arm64": "npm:0.27.7" + "@esbuild/linux-ia32": "npm:0.27.7" + "@esbuild/linux-loong64": "npm:0.27.7" + "@esbuild/linux-mips64el": "npm:0.27.7" + "@esbuild/linux-ppc64": "npm:0.27.7" + "@esbuild/linux-riscv64": "npm:0.27.7" + "@esbuild/linux-s390x": "npm:0.27.7" + "@esbuild/linux-x64": "npm:0.27.7" + "@esbuild/netbsd-arm64": "npm:0.27.7" + "@esbuild/netbsd-x64": "npm:0.27.7" + "@esbuild/openbsd-arm64": "npm:0.27.7" + "@esbuild/openbsd-x64": "npm:0.27.7" + "@esbuild/openharmony-arm64": "npm:0.27.7" + "@esbuild/sunos-x64": "npm:0.27.7" + "@esbuild/win32-arm64": "npm:0.27.7" + "@esbuild/win32-ia32": "npm:0.27.7" + "@esbuild/win32-x64": "npm:0.27.7" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/ccd51f0555708bc9ff4ec9dc3ac92d3daacd45ecaac949ca8645984c5c323bf8cefe98c2df307418685e0b4ce37f9a3bdbfe8e3651fe632a0059a436195a17d4 + languageName: node + linkType: hard + +"esbuild@npm:~0.28.0": + version: 0.28.0 + resolution: "esbuild@npm:0.28.0" + dependencies: + "@esbuild/aix-ppc64": "npm:0.28.0" + "@esbuild/android-arm": "npm:0.28.0" + "@esbuild/android-arm64": "npm:0.28.0" + "@esbuild/android-x64": "npm:0.28.0" + "@esbuild/darwin-arm64": "npm:0.28.0" + "@esbuild/darwin-x64": "npm:0.28.0" + "@esbuild/freebsd-arm64": "npm:0.28.0" + "@esbuild/freebsd-x64": "npm:0.28.0" + "@esbuild/linux-arm": "npm:0.28.0" + "@esbuild/linux-arm64": "npm:0.28.0" + "@esbuild/linux-ia32": "npm:0.28.0" + "@esbuild/linux-loong64": "npm:0.28.0" + "@esbuild/linux-mips64el": "npm:0.28.0" + "@esbuild/linux-ppc64": "npm:0.28.0" + "@esbuild/linux-riscv64": "npm:0.28.0" + "@esbuild/linux-s390x": "npm:0.28.0" + "@esbuild/linux-x64": "npm:0.28.0" + "@esbuild/netbsd-arm64": "npm:0.28.0" + "@esbuild/netbsd-x64": "npm:0.28.0" + "@esbuild/openbsd-arm64": "npm:0.28.0" + "@esbuild/openbsd-x64": "npm:0.28.0" + "@esbuild/openharmony-arm64": "npm:0.28.0" + "@esbuild/sunos-x64": "npm:0.28.0" + "@esbuild/win32-arm64": "npm:0.28.0" + "@esbuild/win32-ia32": "npm:0.28.0" + "@esbuild/win32-x64": "npm:0.28.0" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/8acd95c238ec6c4a9d16163277faf228a8994b642d187b3fe667ffbb469008e6748cde144fdc3c175bf8e78ee49e15a0ed9b9f183fdb5fcea1772f87fb1372a4 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^1.0.2, escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 10c0/a968ad453dd0c2724e14a4f20e177aaf32bb384ab41b674a8454afe9a41c5e6fe8903323e0a1052f56289d04bd600f81278edf140b0fcc02f5cac98d0f5b5371 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^2.0.0": + version: 2.0.0 + resolution: "escape-string-regexp@npm:2.0.0" + checksum: 10c0/2530479fe8db57eace5e8646c9c2a9c80fa279614986d16dcc6bcaceb63ae77f05a851ba6c43756d816c61d7f4534baf56e3c705e3e0d884818a46808811c507 + languageName: node + linkType: hard + +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + +"eventemitter3@npm:^3.1.0": + version: 3.1.2 + resolution: "eventemitter3@npm:3.1.2" + checksum: 10c0/c67262eccbf85848b7cc6d4abb6c6e34155e15686db2a01c57669fd0d44441a574a19d44d25948b442929e065774cbe5003d8e77eed47674fbf876ac77887793 + languageName: node + linkType: hard + +"expect-type@npm:^1.2.1": + version: 1.3.0 + resolution: "expect-type@npm:1.3.0" + checksum: 10c0/8412b3fe4f392c420ab41dae220b09700e4e47c639a29ba7ba2e83cc6cffd2b4926f7ac9e47d7e277e8f4f02acda76fd6931cb81fd2b382fa9477ef9ada953fd + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.3 + resolution: "exponential-backoff@npm:3.1.3" + checksum: 10c0/77e3ae682b7b1f4972f563c6dbcd2b0d54ac679e62d5d32f3e5085feba20483cf28bd505543f520e287a56d4d55a28d7874299941faf637e779a1aa5994d1267 + languageName: node + linkType: hard + +"external-editor@npm:^3.1.0": + version: 3.1.0 + resolution: "external-editor@npm:3.1.0" + dependencies: + chardet: "npm:^0.7.0" + iconv-lite: "npm:^0.4.24" + tmp: "npm:^0.0.33" + checksum: 10c0/c98f1ba3efdfa3c561db4447ff366a6adb5c1e2581462522c56a18bf90dfe4da382f9cd1feee3e330108c3595a854b218272539f311ba1b3298f841eb0fbf339 + languageName: node + linkType: hard + +"fast-glob@npm:^3.2.9": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.8" + checksum: 10c0/f6aaa141d0d3384cf73cbcdfc52f475ed293f6d5b65bfc5def368b09163a9f7e5ec2b3014d80f733c405f58e470ee0cc451c2937685045cddcdeaa24199c43fe + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.20.1 + resolution: "fastq@npm:1.20.1" + dependencies: + reusify: "npm:^1.0.4" + checksum: 10c0/e5dd725884decb1f11e5c822221d76136f239d0236f176fab80b7b8f9e7619ae57e6b4e5b73defc21e6b9ef99437ee7b545cff8e6c2c337819633712fa9d352e + languageName: node + linkType: hard + +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f + languageName: node + linkType: hard + +"figures@npm:^1.7.0": + version: 1.7.0 + resolution: "figures@npm:1.7.0" + dependencies: + escape-string-regexp: "npm:^1.0.5" + object-assign: "npm:^4.1.0" + checksum: 10c0/a10942b0eec3372bf61822ab130d2bbecdf527d551b0b013fbe7175b7a0238ead644ee8930a1a3cb872fb9ab2ec27df30e303765a3b70b97852e2e9ee43bdff3 + languageName: node + linkType: hard + +"figures@npm:^2.0.0": + version: 2.0.0 + resolution: "figures@npm:2.0.0" + dependencies: + escape-string-regexp: "npm:^1.0.5" + checksum: 10c0/5dc5a75fec3e7e04ae65d6ce51d28b3e70d4656c51b06996b6fdb2cb5b542df512e3b3c04482f5193a964edddafa5521479ff948fa84e12ff556e53e094ab4ce + languageName: node + linkType: hard + +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 + languageName: node + linkType: hard + +"find-up@npm:^4.1.0": + version: 4.1.0 + resolution: "find-up@npm:4.1.0" + dependencies: + locate-path: "npm:^5.0.0" + path-exists: "npm:^4.0.0" + checksum: 10c0/0406ee89ebeefa2d507feb07ec366bebd8a6167ae74aa4e34fb4c4abd06cf782a3ce26ae4194d70706f72182841733f00551c209fe575cb00bd92104056e78c1 + languageName: node + linkType: hard + +"follow-redirects@npm:^1.16.0": + version: 1.16.0 + resolution: "follow-redirects@npm:1.16.0" + peerDependenciesMeta: + debug: + optional: true + checksum: 10c0/a1e2900163e6f1b4d1ed5c221b607f41decbab65534c63fe7e287e40a5d552a6496e7d9d7d976fa4ba77b4c51c11e5e9f683f10b43011ea11e442ff128d0e181 + languageName: node + linkType: hard + +"form-data@npm:^4.0.5": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" + mime-types: "npm:^2.1.12" + checksum: 10c0/dd6b767ee0bbd6d84039db12a0fa5a2028160ffbfaba1800695713b46ae974a5f6e08b3356c3195137f8530dcd9dfcb5d5ae1eeff53d0db1e5aad863b619ce3b + languageName: node + linkType: hard + +"fs-extra@npm:^10.1.0": + version: 10.1.0 + resolution: "fs-extra@npm:10.1.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/5f579466e7109719d162a9249abbeffe7f426eb133ea486e020b89bc6d67a741134076bf439983f2eb79276ceaf6bd7b7c1e43c3fd67fe889863e69072fb0a5e + languageName: node + linkType: hard + +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 10c0/444cf1291d997165dfd4c0d58b69f0e4782bfd9149fd72faa4fe299e68e0e93d6db941660b37dd29153bf7186672ececa3b50b7e7249477b03fdf850f287c948 + languageName: node + linkType: hard + +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 + languageName: node + linkType: hard + +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 10c0/8a9f59df0f01cfefafdb3b451b80555e5cf6d76487095db91ac461a0e682e4ff7a9dbce15f4ecec191e53586d59eece01949e05a4b4492879600bbbe8e28d6b8 + languageName: node + linkType: hard + +"get-caller-file@npm:^2.0.1": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde + languageName: node + linkType: hard + +"get-east-asian-width@npm:^1.0.0, get-east-asian-width@npm:^1.3.1, get-east-asian-width@npm:^1.5.0": + version: 1.6.0 + resolution: "get-east-asian-width@npm:1.6.0" + checksum: 10c0/7e72e9550fd49ca5b246f9af6bb2afc129c96412845ff6556b3274fd44817a381702ca17028efe9866b261a3d44254cbf21e6c90cf05b4b61675630af776d431 + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" + dependencies: + async-function: "npm:^1.0.0" + async-generator-function: "npm:^1.0.0" + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + function-bind: "npm:^1.1.2" + generator-function: "npm:^2.0.0" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10c0/9f4ab0cf7efe0fd2c8185f52e6f637e708f3a112610c88869f8f041bb9ecc2ce44bf285dfdbdc6f4f7c277a5b88d8e94a432374d97cca22f3de7fc63795deb5d + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/9224acb44603c5526955e83510b9da41baf6ae73f7398875fba50edc5e944223a89c4a72b070fcd78beb5f7bdda58ecb6294adc28f7acfc0da05f76a2399643c + languageName: node + linkType: hard + +"glob-parent@npm:^5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: "npm:^4.0.1" + checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee + languageName: node + linkType: hard + +"glob@npm:^7.1.3": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: "npm:^1.0.0" + inflight: "npm:^1.0.4" + inherits: "npm:2" + minimatch: "npm:^3.1.1" + once: "npm:^1.3.0" + path-is-absolute: "npm:^1.0.0" + checksum: 10c0/65676153e2b0c9095100fe7f25a778bf45608eeb32c6048cf307f579649bcc30353277b3b898a3792602c65764e5baa4f643714dfbdfd64ea271d210c7a425fe + languageName: node + linkType: hard + +"globby@npm:^11.0.3": + version: 11.1.0 + resolution: "globby@npm:11.1.0" + dependencies: + array-union: "npm:^2.1.0" + dir-glob: "npm:^3.0.1" + fast-glob: "npm:^3.2.9" + ignore: "npm:^5.2.0" + merge2: "npm:^1.4.1" + slash: "npm:^3.0.0" + checksum: 10c0/b39511b4afe4bd8a7aead3a27c4ade2b9968649abab0a6c28b1a90141b96ca68ca5db1302f7c7bd29eab66bf51e13916b8e0a3d0ac08f75e1e84a39b35691189 + languageName: node + linkType: hard + +"globrex@npm:^0.1.2": + version: 0.1.2 + resolution: "globrex@npm:0.1.2" + checksum: 10c0/a54c029520cf58bda1d8884f72bd49b4cd74e977883268d931fd83bcbd1a9eb96d57c7dbd4ad80148fb9247467ebfb9b215630b2ed7563b2a8de02e1ff7f89d1 + languageName: node + linkType: hard + +"gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10c0/50fff1e04ba2b7737c097358534eacadad1e68d24cccee3272e04e007bed008e68d2614f3987788428fd192a5ae3889d08fb2331417e4fc4a9ab366b2043cead + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"graphql-query-batcher@npm:^1.0.1": + version: 1.0.1 + resolution: "graphql-query-batcher@npm:1.0.1" + checksum: 10c0/804d0f4064721a2116a16b9eac422e9233e85f4ab5b250cb8f83662725658ffde35779a8ae8211037f3dd2f9717de8cb63b394ad8057ce22171a83db2471196a + languageName: node + linkType: hard + +"graphql-sse@npm:^2.5.4": + version: 2.6.0 + resolution: "graphql-sse@npm:2.6.0" + peerDependencies: + graphql: ">=0.11 <=16" + checksum: 10c0/e05f0b5c8539d61e5ce34af8e0bb418c02bf922d6a7f9232a9abd53c77df5654684ca14f674ac771645c8fba9c37ce666c5d06f46eef54ea07e63653468065b0 + languageName: node + linkType: hard + +"graphql@npm:^16.6.0, graphql@npm:^16.8.1": + version: 16.14.0 + resolution: "graphql@npm:16.14.0" + checksum: 10c0/baaa368fcfeb7bf2cdf94b9f31bf916e97eaa9e7e82148a7046f31cbd49b760b38190f22393b024cbdd9ca0f4f46287515de0136b6a0cc164074b36ad7b50618 + languageName: node + linkType: hard + +"has-ansi@npm:^2.0.0": + version: 2.0.0 + resolution: "has-ansi@npm:2.0.0" + dependencies: + ansi-regex: "npm:^2.0.0" + checksum: 10c0/f54e4887b9f8f3c4bfefd649c48825b3c093987c92c27880ee9898539e6f01aed261e82e73153c3f920fde0db5bf6ebd58deb498ed1debabcb4bc40113ccdf05 + languageName: node + linkType: hard + +"has-flag@npm:^3.0.0": + version: 3.0.0 + resolution: "has-flag@npm:3.0.0" + checksum: 10c0/1c6c83b14b8b1b3c25b0727b8ba3e3b647f99e9e6e13eb7322107261de07a4c1be56fc0d45678fc376e09772a3a1642ccdaf8fc69bdf123b6c086598397ce473 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10c0/dde0a734b17ae51e84b10986e651c664379018d10b91b6b0e9b293eddb32f0f069688c841fb40f19e9611546130153e0a2a48fd7f512891fb000ddfa36f5a20e + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c + languageName: node + linkType: hard + +"hasown@npm:^2.0.2": + version: 2.0.3 + resolution: "hasown@npm:2.0.3" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/f5eb28c3fd0d3e4facd821c1eeee3836c37b70ab0b0fc532e8a39976e18fef43652415dadc52f8c7a5ff6d5ac93b7bef128789aa6f90f4e9b9a9083dce74ab38 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^5.0.1": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: "npm:6" + debug: "npm:4" + checksum: 10c0/6dd639f03434003577c62b27cafdb864784ef19b2de430d8ae2a1d45e31c4fd60719e5637b44db1a88a046934307da7089e03d6089ec3ddacc1189d8de8897d1 + languageName: node + linkType: hard + +"iconv-lite@npm:^0.4.24": + version: 0.4.24 + resolution: "iconv-lite@npm:0.4.24" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3" + checksum: 10c0/c6886a24cc00f2a059767440ec1bc00d334a89f250db8e0f7feb4961c8727118457e27c495ba94d082e51d3baca378726cd110aaf7ded8b9bbfd6a44760cf1d4 + languageName: node + linkType: hard + +"ignore@npm:^5.2.0": + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 + languageName: node + linkType: hard + +"indent-string@npm:^3.0.0": + version: 3.2.0 + resolution: "indent-string@npm:3.2.0" + checksum: 10c0/91b6d61621d24944c5c4d365d6f1ff4a490264ccaf1162a602faa0d323e69231db2180ad4ccc092c2f49cf8888cdb3da7b73e904cc0fdeec40d0bfb41ceb9478 + languageName: node + linkType: hard + +"indent-string@npm:^5.0.0": + version: 5.0.0 + resolution: "indent-string@npm:5.0.0" + checksum: 10c0/8ee77b57d92e71745e133f6f444d6fa3ed503ad0e1bcd7e80c8da08b42375c07117128d670589725ed07b1978065803fa86318c309ba45415b7fe13e7f170220 + languageName: node + linkType: hard + +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" + dependencies: + once: "npm:^1.3.0" + wrappy: "npm:1" + checksum: 10c0/7faca22584600a9dc5b9fca2cd5feb7135ac8c935449837b315676b4c90aa4f391ec4f42240178244b5a34e8bede1948627fda392ca3191522fc46b34e985ab2 + languageName: node + linkType: hard + +"inherits@npm:2": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 + languageName: node + linkType: hard + +"ink@npm:^6.8.0": + version: 6.8.0 + resolution: "ink@npm:6.8.0" + dependencies: + "@alcalzone/ansi-tokenize": "npm:^0.2.4" + ansi-escapes: "npm:^7.3.0" + ansi-styles: "npm:^6.2.1" + auto-bind: "npm:^5.0.1" + chalk: "npm:^5.6.0" + cli-boxes: "npm:^3.0.0" + cli-cursor: "npm:^4.0.0" + cli-truncate: "npm:^5.1.1" + code-excerpt: "npm:^4.0.0" + es-toolkit: "npm:^1.39.10" + indent-string: "npm:^5.0.0" + is-in-ci: "npm:^2.0.0" + patch-console: "npm:^2.0.0" + react-reconciler: "npm:^0.33.0" + scheduler: "npm:^0.27.0" + signal-exit: "npm:^3.0.7" + slice-ansi: "npm:^8.0.0" + stack-utils: "npm:^2.0.6" + string-width: "npm:^8.1.1" + terminal-size: "npm:^4.0.1" + type-fest: "npm:^5.4.1" + widest-line: "npm:^6.0.0" + wrap-ansi: "npm:^9.0.0" + ws: "npm:^8.18.0" + yoga-layout: "npm:~3.2.1" + peerDependencies: + "@types/react": ">=19.0.0" + react: ">=19.0.0" + react-devtools-core: ">=6.1.2" + peerDependenciesMeta: + "@types/react": + optional: true + react-devtools-core: + optional: true + checksum: 10c0/50500e547fdf6a1f1d836d6befbd4770e3ab649ef0be1884500a6da411fb68a90e22dd7dcc9c404911d30e9f87506b3b9d8e997c6c6ceac85ee054b4dadefaff + languageName: node + linkType: hard + +"inquirer@npm:^10.0.0": + version: 10.2.2 + resolution: "inquirer@npm:10.2.2" + dependencies: + "@inquirer/core": "npm:^9.1.0" + "@inquirer/prompts": "npm:^5.5.0" + "@inquirer/type": "npm:^1.5.3" + "@types/mute-stream": "npm:^0.0.4" + ansi-escapes: "npm:^4.3.2" + mute-stream: "npm:^1.0.0" + run-async: "npm:^3.0.0" + rxjs: "npm:^7.8.1" + checksum: 10c0/09bcff887a968ce29d3a8cba749ef35b6531b7fb7bf5b28aaf3e10aebae07af2494dd3ec67ae6f1dabe76da1064cfe4514098d4c7658fcaf3fd480b2975d7163 + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^1.0.0": + version: 1.0.0 + resolution: "is-fullwidth-code-point@npm:1.0.0" + dependencies: + number-is-nan: "npm:^1.0.0" + checksum: 10c0/12acfcf16142f2d431bf6af25d68569d3198e81b9799b4ae41058247aafcc666b0127d64384ea28e67a746372611fcbe9b802f69175287aba466da3eddd5ba0f + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^2.0.0": + version: 2.0.0 + resolution: "is-fullwidth-code-point@npm:2.0.0" + checksum: 10c0/e58f3e4a601fc0500d8b2677e26e9fe0cd450980e66adb29d85b6addf7969731e38f8e43ed2ec868a09c101a55ac3d8b78902209269f38c5286bc98f5bc1b4d9 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^5.0.0, is-fullwidth-code-point@npm:^5.1.0": + version: 5.1.0 + resolution: "is-fullwidth-code-point@npm:5.1.0" + dependencies: + get-east-asian-width: "npm:^1.3.1" + checksum: 10c0/c1172c2e417fb73470c56c431851681591f6a17233603a9e6f94b7ba870b2e8a5266506490573b607fb1081318589372034aa436aec07b465c2029c0bc7f07a4 + languageName: node + linkType: hard + +"is-glob@npm:^4.0.1": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: "npm:^2.1.1" + checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a + languageName: node + linkType: hard + +"is-in-ci@npm:^2.0.0": + version: 2.0.0 + resolution: "is-in-ci@npm:2.0.0" + bin: + is-in-ci: cli.js + checksum: 10c0/1e1d1056939a681e8206035de5ad84e0404556eaa7622bb55f0f1868b9788bff3df427bc0b1ed5a172623154a90fcb1e759a230817cd73d09435543ae3c71feb + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 + languageName: node + linkType: hard + +"is-observable@npm:^1.1.0": + version: 1.1.0 + resolution: "is-observable@npm:1.1.0" + dependencies: + symbol-observable: "npm:^1.1.0" + checksum: 10c0/cf3166b0822f70ad06e7851e09430166ce658349d54aaa64c93a03320420b9285735821b23164bdce741ff83a86730ac3e53035ce4e2511ed843dbff4105bfa2 + languageName: node + linkType: hard + +"is-promise@npm:^2.1.0": + version: 2.2.2 + resolution: "is-promise@npm:2.2.2" + checksum: 10c0/2dba959812380e45b3df0fb12e7cb4d4528c989c7abb03ececb1d1fd6ab1cbfee956ca9daa587b9db1d8ac3c1e5738cf217bdb3dfd99df8c691be4c00ae09069 + languageName: node + linkType: hard + +"is-stream@npm:^1.1.0": + version: 1.1.0 + resolution: "is-stream@npm:1.1.0" + checksum: 10c0/b8ae7971e78d2e8488d15f804229c6eed7ed36a28f8807a1815938771f4adff0e705218b7dab968270433f67103e4fef98062a0beea55d64835f705ee72c7002 + languageName: node + linkType: hard + +"isexe@npm:^4.0.0": + version: 4.0.0 + resolution: "isexe@npm:4.0.0" + checksum: 10c0/5884815115bceac452877659a9c7726382531592f43dc29e5d48b7c4100661aed54018cb90bd36cb2eaeba521092570769167acbb95c18d39afdccbcca06c5ce + languageName: node + linkType: hard + +"isomorphic-unfetch@npm:^3.0.0": + version: 3.1.0 + resolution: "isomorphic-unfetch@npm:3.1.0" + dependencies: + node-fetch: "npm:^2.6.1" + unfetch: "npm:^4.2.0" + checksum: 10c0/d3b61fca06304db692b7f76bdfd3a00f410e42cfa7403c3b250546bf71589d18cf2f355922f57198e4cc4a9872d3647b20397a5c3edf1a347c90d57c83cf2a89 + languageName: node + linkType: hard + +"iterall@npm:^1.2.1": + version: 1.3.0 + resolution: "iterall@npm:1.3.0" + checksum: 10c0/40de624e5fe937c4c0e511981b91caea9ff2142bfc0316cccc8506eaa03aa253820cc17c5bc5f0a98706c7268a373e5ebee9af9a0c8a359730cf7c05938b57b5 + languageName: node + linkType: hard + +"js-tokens@npm:^9.0.1": + version: 9.0.1 + resolution: "js-tokens@npm:9.0.1" + checksum: 10c0/68dcab8f233dde211a6b5fd98079783cbcd04b53617c1250e3553ee16ab3e6134f5e65478e41d82f6d351a052a63d71024553933808570f04dbf828d7921e80e + languageName: node + linkType: hard + +"jsonc-parser@npm:^3.2.0": + version: 3.3.1 + resolution: "jsonc-parser@npm:3.3.1" + checksum: 10c0/269c3ae0a0e4f907a914bf334306c384aabb9929bd8c99f909275ebd5c2d3bc70b9bcd119ad794f339dec9f24b6a4ee9cd5a8ab2e6435e730ad4075388fc2ab6 + languageName: node + linkType: hard + +"jsonfile@npm:^6.0.1": + version: 6.2.1 + resolution: "jsonfile@npm:6.2.1" + dependencies: + graceful-fs: "npm:^4.1.6" + universalify: "npm:^2.0.0" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10c0/e1abf000ecee9942d4d028a8e02dc752617face227d72afd1cfb2187e2433079e625bf82b807a313689db71b6472c6b2b389a2340d2798737b1199a39631c28a + languageName: node + linkType: hard + +"kleur@npm:^4.1.5": + version: 4.1.5 + resolution: "kleur@npm:4.1.5" + checksum: 10c0/e9de6cb49657b6fa70ba2d1448fd3d691a5c4370d8f7bbf1c2f64c24d461270f2117e1b0afe8cb3114f13bbd8e51de158c2a224953960331904e636a5e4c0f2a + languageName: node + linkType: hard + +"listr-silent-renderer@npm:^1.1.1": + version: 1.1.1 + resolution: "listr-silent-renderer@npm:1.1.1" + checksum: 10c0/a13e08ebf863516a757bce4887f05290070772113d89095e9f51a07cf0b11a43a7563a67ff3b287c752c08f6d781fdb2123b02957534e3e0675fb564f2a42e1b + languageName: node + linkType: hard + +"listr-update-renderer@npm:^0.5.0": + version: 0.5.0 + resolution: "listr-update-renderer@npm:0.5.0" + dependencies: + chalk: "npm:^1.1.3" + cli-truncate: "npm:^0.2.1" + elegant-spinner: "npm:^1.0.1" + figures: "npm:^1.7.0" + indent-string: "npm:^3.0.0" + log-symbols: "npm:^1.0.2" + log-update: "npm:^2.3.0" + strip-ansi: "npm:^3.0.1" + peerDependencies: + listr: ^0.14.2 + checksum: 10c0/8ade44bf3dc6146c8e0178000619439e8889792c4689b66be6ce82bd459f5fe462ecb34b05147fb206a8ad60e6d4e6f34c9f48038e18366f867fd972688b8edc + languageName: node + linkType: hard + +"listr-verbose-renderer@npm:^0.5.0": + version: 0.5.0 + resolution: "listr-verbose-renderer@npm:0.5.0" + dependencies: + chalk: "npm:^2.4.1" + cli-cursor: "npm:^2.1.0" + date-fns: "npm:^1.27.2" + figures: "npm:^2.0.0" + checksum: 10c0/041cd1e82da7054f27ae0a914e98b40d15faf9f950ef850578fc6241d3fff3c2d7158a4f6226006e566b4c47bf445be2d254dd1ce5c16569a3a5dcd575bec656 + languageName: node + linkType: hard + +"listr@npm:^0.14.3": + version: 0.14.3 + resolution: "listr@npm:0.14.3" + dependencies: + "@samverschueren/stream-to-observable": "npm:^0.3.0" + is-observable: "npm:^1.1.0" + is-promise: "npm:^2.1.0" + is-stream: "npm:^1.1.0" + listr-silent-renderer: "npm:^1.1.1" + listr-update-renderer: "npm:^0.5.0" + listr-verbose-renderer: "npm:^0.5.0" + p-map: "npm:^2.0.0" + rxjs: "npm:^6.3.3" + checksum: 10c0/753d518218c423f46bee8eeacccecadfd2e414ba9c0f602e7f85fe3f6fa18404dfab0812433aeda4683ee2548358488f597ac1a3d321196baec5d3149b200b10 + languageName: node + linkType: hard + +"locate-path@npm:^5.0.0": + version: 5.0.0 + resolution: "locate-path@npm:5.0.0" + dependencies: + p-locate: "npm:^4.1.0" + checksum: 10c0/33a1c5247e87e022f9713e6213a744557a3e9ec32c5d0b5efb10aa3a38177615bf90221a5592674857039c1a0fd2063b82f285702d37b792d973e9e72ace6c59 + languageName: node + linkType: hard + +"lodash@npm:^4.17.20, lodash@npm:^4.17.21": + version: 4.18.1 + resolution: "lodash@npm:4.18.1" + checksum: 10c0/757228fc68805c59789e82185135cf85f05d0b2d3d54631d680ca79ec21944ec8314d4533639a14b8bcfbd97a517e78960933041a5af17ecb693ec6eecb99a27 + languageName: node + linkType: hard + +"log-symbols@npm:^1.0.2": + version: 1.0.2 + resolution: "log-symbols@npm:1.0.2" + dependencies: + chalk: "npm:^1.0.0" + checksum: 10c0/c64e1fe41d0d043840f8b592d043b8607a836b846506f525a53d99d578561f02f97b2cba1d2b3c30bae5311d64b308d5a392a9930d252b906a9042fc2877da7a + languageName: node + linkType: hard + +"log-update@npm:^2.3.0": + version: 2.3.0 + resolution: "log-update@npm:2.3.0" + dependencies: + ansi-escapes: "npm:^3.0.0" + cli-cursor: "npm:^2.0.0" + wrap-ansi: "npm:^3.0.1" + checksum: 10c0/9bf21b138801ab4770a2bfa735161cf005b869360eaf5003a84ba64ddc5f5c3ce7217f4f1fa79d9c1f510d792213b2c9800327228e94df05859d19b716215d90 + languageName: node + linkType: hard + +"loupe@npm:^3.1.0, loupe@npm:^3.1.4": + version: 3.2.1 + resolution: "loupe@npm:3.2.1" + checksum: 10c0/910c872cba291309664c2d094368d31a68907b6f5913e989d301b5c25f30e97d76d77f23ab3bf3b46d0f601ff0b6af8810c10c31b91d2c6b2f132809ca2cc705 + languageName: node + linkType: hard + +"magic-string@npm:^0.30.17": + version: 0.30.21 + resolution: "magic-string@npm:0.30.21" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.5" + checksum: 10c0/299378e38f9a270069fc62358522ddfb44e94244baa0d6a8980ab2a9b2490a1d03b236b447eee309e17eb3bddfa482c61259d47960eb018a904f0ded52780c4a + languageName: node + linkType: hard + +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10c0/7579ff94e899e2f76ab64491d76cf606274c874d8f2af4a442c016bd85688927fcfca157ba6bf74b08e9439dc010b248ce05b96cc7c126a354c3bae7fcb48b7f + languageName: node + linkType: hard + +"merge2@npm:^1.3.0, merge2@npm:^1.4.1": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb + languageName: node + linkType: hard + +"micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 + languageName: node + linkType: hard + +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 10c0/0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa + languageName: node + linkType: hard + +"mime-types@npm:^2.1.12": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: "npm:1.52.0" + checksum: 10c0/82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2 + languageName: node + linkType: hard + +"mimic-fn@npm:^1.0.0": + version: 1.2.0 + resolution: "mimic-fn@npm:1.2.0" + checksum: 10c0/ad55214aec6094c0af4c0beec1a13787556f8116ed88807cf3f05828500f21f93a9482326bcd5a077ae91e3e8795b4e76b5b4c8bb12237ff0e4043a365516cba + languageName: node + linkType: hard + +"mimic-fn@npm:^2.1.0": + version: 2.1.0 + resolution: "mimic-fn@npm:2.1.0" + checksum: 10c0/b26f5479d7ec6cc2bce275a08f146cf78f5e7b661b18114e2506dd91ec7ec47e7a25bf4360e5438094db0560bcc868079fb3b1fb3892b833c1ecbf63f80c95a4 + languageName: node + linkType: hard + +"minimatch@npm:^3.1.1": + version: 3.1.5 + resolution: "minimatch@npm:3.1.5" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/2ecbdc0d33f07bddb0315a8b5afbcb761307a8778b48f0b312418ccbced99f104a2d17d8aca7573433c70e8ccd1c56823a441897a45e384ea76ef401a26ace70 + languageName: node + linkType: hard + +"minimist@npm:^1.2.6": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 + languageName: node + linkType: hard + +"minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.3 + resolution: "minipass@npm:7.1.3" + checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb + languageName: node + linkType: hard + +"minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10c0/5aad75ab0090b8266069c9aabe582c021ae53eb33c6c691054a13a45db3b4f91a7fb1bd79151e6b4e9e9a86727b522527c0a06ec7d45206b745d54cd3097bcec + languageName: node + linkType: hard + +"mkdirp@npm:^0.5.1": + version: 0.5.6 + resolution: "mkdirp@npm:0.5.6" + dependencies: + minimist: "npm:^1.2.6" + bin: + mkdirp: bin/cmd.js + checksum: 10c0/e2e2be789218807b58abced04e7b49851d9e46e88a2f9539242cc8a92c9b5c3a0b9bab360bd3014e02a140fc4fbc58e31176c408b493f8a2a6f4986bd7527b01 + languageName: node + linkType: hard + +"ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + +"mute-stream@npm:^1.0.0": + version: 1.0.0 + resolution: "mute-stream@npm:1.0.0" + checksum: 10c0/dce2a9ccda171ec979a3b4f869a102b1343dee35e920146776780de182f16eae459644d187e38d59a3d37adf85685e1c17c38cf7bfda7e39a9880f7a1d10a74c + languageName: node + linkType: hard + +"nanoid@npm:^3.3.11": + version: 3.3.12 + resolution: "nanoid@npm:3.3.12" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/ba142b7b39e11e80c16dd74b0365d407880c87c1cf7e1480956981ae940ee36060fa5b6f092cd1e315184dd19244c657bd017d03327bd3c62247d691c5e8edfb + languageName: node + linkType: hard + +"native-fetch@npm:^4.0.2": + version: 4.0.2 + resolution: "native-fetch@npm:4.0.2" + peerDependencies: + undici: "*" + checksum: 10c0/e3b824721daaa628086d9dcd02e8eb12f0a6c5e13a1d182682bae238d80c9bbf3dfd6314a94692ebe20316aa354476804b4df148201d066b46fc552a5794cfab + languageName: node + linkType: hard + +"node-fetch@npm:^2.6.1": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: "npm:^5.0.0" + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 12.3.0 + resolution: "node-gyp@npm:12.3.0" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + graceful-fs: "npm:^4.2.6" + nopt: "npm:^9.0.0" + proc-log: "npm:^6.0.0" + semver: "npm:^7.3.5" + tar: "npm:^7.5.4" + tinyglobby: "npm:^0.2.12" + undici: "npm:^6.25.0" + which: "npm:^6.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10c0/9d9032b405cbe42f72a105259d9eb679376470c102df4a2dbaa51e07d59bf741dcffb85897087ea9d8318b9cabb824a8978af51508ae142f0239ae1e6a3c2329 + languageName: node + linkType: hard + +"nopt@npm:^9.0.0": + version: 9.0.0 + resolution: "nopt@npm:9.0.0" + dependencies: + abbrev: "npm:^4.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/1822eb6f9b020ef6f7a7516d7b64a8036e09666ea55ac40416c36e4b2b343122c3cff0e2f085675f53de1d2db99a2a89a60ccea1d120bcd6a5347bf6ceb4a7fd + languageName: node + linkType: hard + +"normalize-path@npm:^2.1.1": + version: 2.1.1 + resolution: "normalize-path@npm:2.1.1" + dependencies: + remove-trailing-separator: "npm:^1.0.1" + checksum: 10c0/db814326ff88057437233361b4c7e9cac7b54815b051b57f2d341ce89b1d8ec8cbd43e7fa95d7652b3b69ea8fcc294b89b8530d556a84d1bdace94229e1e9a8b + languageName: node + linkType: hard + +"number-is-nan@npm:^1.0.0": + version: 1.0.1 + resolution: "number-is-nan@npm:1.0.1" + checksum: 10c0/cb97149006acc5cd512c13c1838223abdf202e76ddfa059c5e8e7507aff2c3a78cd19057516885a2f6f5b576543dc4f7b6f3c997cc7df53ae26c260855466df5 + languageName: node + linkType: hard + +"object-assign@npm:^4.1.0": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 + languageName: node + linkType: hard + +"object-inspect@npm:^1.13.3, object-inspect@npm:^1.13.4": + version: 1.13.4 + resolution: "object-inspect@npm:1.13.4" + checksum: 10c0/d7f8711e803b96ea3191c745d6f8056ce1f2496e530e6a19a0e92d89b0fa3c76d910c31f0aa270432db6bd3b2f85500a376a83aaba849a8d518c8845b3211692 + languageName: node + linkType: hard + +"once@npm:^1.3.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: "npm:1" + checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0 + languageName: node + linkType: hard + +"onetime@npm:^2.0.0": + version: 2.0.1 + resolution: "onetime@npm:2.0.1" + dependencies: + mimic-fn: "npm:^1.0.0" + checksum: 10c0/b4e44a8c34e70e02251bfb578a6e26d6de6eedbed106cd78211d2fd64d28b6281d54924696554e4e966559644243753ac5df73c87f283b0927533d3315696215 + languageName: node + linkType: hard + +"onetime@npm:^5.1.0": + version: 5.1.2 + resolution: "onetime@npm:5.1.2" + dependencies: + mimic-fn: "npm:^2.1.0" + checksum: 10c0/ffcef6fbb2692c3c40749f31ea2e22677a876daea92959b8a80b521d95cca7a668c884d8b2045d1d8ee7d56796aa405c405462af112a1477594cc63531baeb8f + languageName: node + linkType: hard + +"os-tmpdir@npm:~1.0.2": + version: 1.0.2 + resolution: "os-tmpdir@npm:1.0.2" + checksum: 10c0/f438450224f8e2687605a8dd318f0db694b6293c5d835ae509a69e97c8de38b6994645337e5577f5001115470414638978cc49da1cdcc25106dad8738dc69990 + languageName: node + linkType: hard + +"oxlint@npm:^0.16.0": + version: 0.16.12 + resolution: "oxlint@npm:0.16.12" + dependencies: + "@oxlint/darwin-arm64": "npm:0.16.12" + "@oxlint/darwin-x64": "npm:0.16.12" + "@oxlint/linux-arm64-gnu": "npm:0.16.12" + "@oxlint/linux-arm64-musl": "npm:0.16.12" + "@oxlint/linux-x64-gnu": "npm:0.16.12" + "@oxlint/linux-x64-musl": "npm:0.16.12" + "@oxlint/win32-arm64": "npm:0.16.12" + "@oxlint/win32-x64": "npm:0.16.12" + dependenciesMeta: + "@oxlint/darwin-arm64": + optional: true + "@oxlint/darwin-x64": + optional: true + "@oxlint/linux-arm64-gnu": + optional: true + "@oxlint/linux-arm64-musl": + optional: true + "@oxlint/linux-x64-gnu": + optional: true + "@oxlint/linux-x64-musl": + optional: true + "@oxlint/win32-arm64": + optional: true + "@oxlint/win32-x64": + optional: true + bin: + oxc_language_server: bin/oxc_language_server + oxlint: bin/oxlint + checksum: 10c0/209c3484039c4f1fdd340689e81be93a70fdf74e8c111731e70d81580d223b0029439107c95fa4118cec3c9b03ba6c56624d3dcb8cdc79af222da3561122b3e7 + languageName: node + linkType: hard + +"p-limit@npm:3.1.0": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: "npm:^0.1.0" + checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a + languageName: node + linkType: hard + +"p-limit@npm:^2.2.0": + version: 2.3.0 + resolution: "p-limit@npm:2.3.0" + dependencies: + p-try: "npm:^2.0.0" + checksum: 10c0/8da01ac53efe6a627080fafc127c873da40c18d87b3f5d5492d465bb85ec7207e153948df6b9cbaeb130be70152f874229b8242ee2be84c0794082510af97f12 + languageName: node + linkType: hard + +"p-locate@npm:^4.1.0": + version: 4.1.0 + resolution: "p-locate@npm:4.1.0" + dependencies: + p-limit: "npm:^2.2.0" + checksum: 10c0/1b476ad69ad7f6059744f343b26d51ce091508935c1dbb80c4e0a2f397ffce0ca3a1f9f5cd3c7ce19d7929a09719d5c65fe70d8ee289c3f267cd36f2881813e9 + languageName: node + linkType: hard + +"p-map@npm:^2.0.0": + version: 2.1.0 + resolution: "p-map@npm:2.1.0" + checksum: 10c0/735dae87badd4737a2dd582b6d8f93e49a1b79eabbc9815a4d63a528d5e3523e978e127a21d784cccb637010e32103a40d2aaa3ab23ae60250b1a820ca752043 + languageName: node + linkType: hard + +"p-try@npm:^2.0.0": + version: 2.2.0 + resolution: "p-try@npm:2.2.0" + checksum: 10c0/c36c19907734c904b16994e6535b02c36c2224d433e01a2f1ab777237f4d86e6289fd5fd464850491e940379d4606ed850c03e0f9ab600b0ebddb511312e177f + languageName: node + linkType: hard + +"patch-console@npm:^2.0.0": + version: 2.0.0 + resolution: "patch-console@npm:2.0.0" + checksum: 10c0/486602591a0af7af8d4c76d8eea42cad32b6de7200488819c6383c75e43733ca7bdc80e30f2e68ce05f06a1607cce1683a1706c6672ca27dada1921b366e8f1c + languageName: node + linkType: hard + +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 10c0/8c0bd3f5238188197dc78dced15207a4716c51cc4e3624c44fc97acf69558f5ebb9a2afff486fe1b4ee148e0c133e96c5e11a9aa5c48a3006e3467da070e5e1b + languageName: node + linkType: hard + +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 10c0/127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078 + languageName: node + linkType: hard + +"path-type@npm:^4.0.0": + version: 4.0.0 + resolution: "path-type@npm:4.0.0" + checksum: 10c0/666f6973f332f27581371efaf303fd6c272cc43c2057b37aa99e3643158c7e4b2626549555d88626e99ea9e046f82f32e41bbde5f1508547e9a11b149b52387c + languageName: node + linkType: hard + +"pathe@npm:^2.0.3": + version: 2.0.3 + resolution: "pathe@npm:2.0.3" + checksum: 10c0/c118dc5a8b5c4166011b2b70608762e260085180bb9e33e80a50dcdb1e78c010b1624f4280c492c92b05fc276715a4c357d1f9edc570f8f1b3d90b6839ebaca1 + languageName: node + linkType: hard + +"pathval@npm:^2.0.0": + version: 2.0.1 + resolution: "pathval@npm:2.0.1" + checksum: 10c0/460f4709479fbf2c45903a65655fc8f0a5f6d808f989173aeef5fdea4ff4f303dc13f7870303999add60ec49d4c14733895c0a869392e9866f1091fa64fd7581 + languageName: node + linkType: hard + +"picocolors@npm:^1.1.1": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + +"picomatch@npm:^2.3.1": + version: 2.3.2 + resolution: "picomatch@npm:2.3.2" + checksum: 10c0/a554d1709e59be97d1acb9eaedbbc700a5c03dbd4579807baed95100b00420bc729335440ef15004ae2378984e2487a7c1cebd743cfdb72b6fa9ab69223c0d61 + languageName: node + linkType: hard + +"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": + version: 4.0.4 + resolution: "picomatch@npm:4.0.4" + checksum: 10c0/e2c6023372cc7b5764719a5ffb9da0f8e781212fa7ca4bd0562db929df8e117460f00dff3cb7509dacfc06b86de924b247f504d0ce1806a37fac4633081466b0 + languageName: node + linkType: hard + +"postcss@npm:^8.5.6": + version: 8.5.14 + resolution: "postcss@npm:8.5.14" + dependencies: + nanoid: "npm:^3.3.11" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/48138207cf5ef5581be1bfe2cb65ccfe0ac75e43888ba045afc8ed6043d7b56aeb3b9a9fe5b353ff554be943cd0cc15d826ccb991525159175971e5ee8ab0237 + languageName: node + linkType: hard + +"preact@npm:^10.28.3": + version: 10.29.1 + resolution: "preact@npm:10.29.1" + checksum: 10c0/55da3994d0d9ebcc26476552c6571f8be1150cccdafe842aad40eff9abc84c867555f6d01db386fc63ac4cbe9f9da245002468bb24df0642365162d8f1b545e2 + languageName: node + linkType: hard + +"prettier@npm:^2.8.0": + version: 2.8.8 + resolution: "prettier@npm:2.8.8" + bin: + prettier: bin-prettier.js + checksum: 10c0/463ea8f9a0946cd5b828d8cf27bd8b567345cf02f56562d5ecde198b91f47a76b7ac9eae0facd247ace70e927143af6135e8cf411986b8cb8478784a4d6d724a + languageName: node + linkType: hard + +"proc-log@npm:^6.0.0": + version: 6.1.0 + resolution: "proc-log@npm:6.1.0" + checksum: 10c0/4f178d4062733ead9d71a9b1ab24ebcecdfe2250916a5b1555f04fe2eda972a0ec76fbaa8df1ad9c02707add6749219d118a4fc46dc56bdfe4dde4b47d80bb82 + languageName: node + linkType: hard + +"proxy-from-env@npm:^2.1.0": + version: 2.1.0 + resolution: "proxy-from-env@npm:2.1.0" + checksum: 10c0/ed01729fd4d094eab619cd7e17ce3698b3413b31eb102c4904f9875e677cd207392795d5b4adee9cec359dfd31c44d5ad7595a3a3ad51c40250e141512281c58 + languageName: node + linkType: hard + +"qs@npm:^6.11.0": + version: 6.15.1 + resolution: "qs@npm:6.15.1" + dependencies: + side-channel: "npm:^1.1.0" + checksum: 10c0/19ee504f0ebff72598503e38cd6d9bd7b52a8ab62ae18b1e6bee3d4db58469bd65871ef1893a881bafb0f80ef2f9ab586e1f255cf25cc8d816c0f5a704721d97 + languageName: node + linkType: hard + +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102 + languageName: node + linkType: hard + +"react-dom@npm:^19.0.0": + version: 19.2.6 + resolution: "react-dom@npm:19.2.6" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.6 + checksum: 10c0/dbf2aef67857c03a612bc9316a4562e5066f43fa04bf28f7f0734963fc1350da2c9f8eb973930655453281079f30cc8222bab383dbec4b5097084e2c081e3334 + languageName: node + linkType: hard + +"react-reconciler@npm:^0.33.0": + version: 0.33.0 + resolution: "react-reconciler@npm:0.33.0" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.0 + checksum: 10c0/3f7b27ea8d0ff4c8bf0e402a285e1af9b7d0e6f4c1a70a28f4384938bc1130bc82a90a31df0b79ef5e380e2e55e2598bd90b4dbf802b1203d735ba0355817d3a + languageName: node + linkType: hard + +"react@npm:^19.0.0": + version: 19.2.6 + resolution: "react@npm:19.2.6" + checksum: 10c0/66afde33b9a9ee87b1e1cae39d8e7e040d1262e719524fd70660c4d4ce79929c532ac19fc3df5a911edaf02768fdf2c49de4ede1ba99bc6aad72796e0e26e798 + languageName: node + linkType: hard + +"readdirp@npm:^4.0.1": + version: 4.1.2 + resolution: "readdirp@npm:4.1.2" + checksum: 10c0/60a14f7619dec48c9c850255cd523e2717001b0e179dc7037cfa0895da7b9e9ab07532d324bfb118d73a710887d1e35f79c495fa91582784493e085d18c72c62 + languageName: node + linkType: hard + +"remove-trailing-separator@npm:^1.0.1": + version: 1.1.0 + resolution: "remove-trailing-separator@npm:1.1.0" + checksum: 10c0/3568f9f8f5af3737b4aee9e6e1e8ec4be65a92da9cb27f989e0893714d50aa95ed2ff02d40d1fa35e1b1a234dc9c2437050ef356704a3999feaca6667d9e9bfc + languageName: node + linkType: hard + +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 + languageName: node + linkType: hard + +"require-main-filename@npm:^2.0.0": + version: 2.0.0 + resolution: "require-main-filename@npm:2.0.0" + checksum: 10c0/db91467d9ead311b4111cbd73a4e67fa7820daed2989a32f7023785a2659008c6d119752d9c4ac011ae07e537eb86523adff99804c5fdb39cd3a017f9b401bb6 + languageName: node + linkType: hard + +"resolve-from@npm:5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 + languageName: node + linkType: hard + +"restore-cursor@npm:^2.0.0": + version: 2.0.0 + resolution: "restore-cursor@npm:2.0.0" + dependencies: + onetime: "npm:^2.0.0" + signal-exit: "npm:^3.0.2" + checksum: 10c0/f5b335bee06f440445e976a7031a3ef53691f9b7c4a9d42a469a0edaf8a5508158a0d561ff2b26a1f4f38783bcca2c0e5c3a44f927326f6694d5b44d7a4993e6 + languageName: node + linkType: hard + +"restore-cursor@npm:^4.0.0": + version: 4.0.0 + resolution: "restore-cursor@npm:4.0.0" + dependencies: + onetime: "npm:^5.1.0" + signal-exit: "npm:^3.0.2" + checksum: 10c0/6f7da8c5e422ac26aa38354870b1afac09963572cf2879443540449068cb43476e9cbccf6f8de3e0171e0d6f7f533c2bc1a0a008003c9a525bbc098e89041318 + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.1.0 + resolution: "reusify@npm:1.1.0" + checksum: 10c0/4eff0d4a5f9383566c7d7ec437b671cc51b25963bd61bf127c3f3d3f68e44a026d99b8d2f1ad344afff8d278a8fe70a8ea092650a716d22287e8bef7126bb2fa + languageName: node + linkType: hard + +"rimraf@npm:^2.6.3": + version: 2.7.1 + resolution: "rimraf@npm:2.7.1" + dependencies: + glob: "npm:^7.1.3" + bin: + rimraf: ./bin.js + checksum: 10c0/4eef73d406c6940927479a3a9dee551e14a54faf54b31ef861250ac815172bade86cc6f7d64a4dc5e98b65e4b18a2e1c9ff3b68d296be0c748413f092bb0dd40 + languageName: node + linkType: hard + +"rollup@npm:^4.43.0": + version: 4.60.4 + resolution: "rollup@npm:4.60.4" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.60.4" + "@rollup/rollup-android-arm64": "npm:4.60.4" + "@rollup/rollup-darwin-arm64": "npm:4.60.4" + "@rollup/rollup-darwin-x64": "npm:4.60.4" + "@rollup/rollup-freebsd-arm64": "npm:4.60.4" + "@rollup/rollup-freebsd-x64": "npm:4.60.4" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.60.4" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.60.4" + "@rollup/rollup-linux-arm64-gnu": "npm:4.60.4" + "@rollup/rollup-linux-arm64-musl": "npm:4.60.4" + "@rollup/rollup-linux-loong64-gnu": "npm:4.60.4" + "@rollup/rollup-linux-loong64-musl": "npm:4.60.4" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.60.4" + "@rollup/rollup-linux-ppc64-musl": "npm:4.60.4" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.60.4" + "@rollup/rollup-linux-riscv64-musl": "npm:4.60.4" + "@rollup/rollup-linux-s390x-gnu": "npm:4.60.4" + "@rollup/rollup-linux-x64-gnu": "npm:4.60.4" + "@rollup/rollup-linux-x64-musl": "npm:4.60.4" + "@rollup/rollup-openbsd-x64": "npm:4.60.4" + "@rollup/rollup-openharmony-arm64": "npm:4.60.4" + "@rollup/rollup-win32-arm64-msvc": "npm:4.60.4" + "@rollup/rollup-win32-ia32-msvc": "npm:4.60.4" + "@rollup/rollup-win32-x64-gnu": "npm:4.60.4" + "@rollup/rollup-win32-x64-msvc": "npm:4.60.4" + "@types/estree": "npm:1.0.8" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loong64-gnu": + optional: true + "@rollup/rollup-linux-loong64-musl": + optional: true + "@rollup/rollup-linux-ppc64-gnu": + optional: true + "@rollup/rollup-linux-ppc64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-openbsd-x64": + optional: true + "@rollup/rollup-openharmony-arm64": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-gnu": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/2734511579da220408eefb877b51281767d790652cee25da8fcd4936c947e3db14882b5edb1d0d5d5bf60f2a71a58ae7d5f7f46c11e3fdf33182538953886243 + languageName: node + linkType: hard + +"run-async@npm:^3.0.0": + version: 3.0.0 + resolution: "run-async@npm:3.0.0" + checksum: 10c0/b18b562ae37c3020083dcaae29642e4cc360c824fbfb6b7d50d809a9d5227bb986152d09310255842c8dce40526e82ca768f02f00806c91ba92a8dfa6159cb85 + languageName: node + linkType: hard + +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: "npm:^1.2.2" + checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39 + languageName: node + linkType: hard + +"rxjs@npm:^6.3.3": + version: 6.6.7 + resolution: "rxjs@npm:6.6.7" + dependencies: + tslib: "npm:^1.9.0" + checksum: 10c0/e556a13a9aa89395e5c9d825eabcfa325568d9c9990af720f3f29f04a888a3b854f25845c2b55875d875381abcae2d8100af9cacdc57576e7ed6be030a01d2fe + languageName: node + linkType: hard + +"rxjs@npm:^7.8.1": + version: 7.8.2 + resolution: "rxjs@npm:7.8.2" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10c0/1fcd33d2066ada98ba8f21fcbbcaee9f0b271de1d38dc7f4e256bfbc6ffcdde68c8bfb69093de7eeb46f24b1fb820620bf0223706cff26b4ab99a7ff7b2e2c45 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 + languageName: node + linkType: hard + +"scheduler@npm:^0.27.0": + version: 0.27.0 + resolution: "scheduler@npm:0.27.0" + checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452 + languageName: node + linkType: hard + +"semver@npm:^7.3.5": + version: 7.8.0 + resolution: "semver@npm:7.8.0" + bin: + semver: bin/semver.js + checksum: 10c0/8f096ca9b80ffd47b308d03f9ce8c873e27e2983f36023c559cdc92c51e8433fc23ebbfe57ec9623fc155636a6961ee989501099841ae4bb1babc8d2b3f048cd + languageName: node + linkType: hard + +"set-blocking@npm:^2.0.0": + version: 2.0.0 + resolution: "set-blocking@npm:2.0.0" + checksum: 10c0/9f8c1b2d800800d0b589de1477c753492de5c1548d4ade52f57f1d1f5e04af5481554d75ce5e5c43d4004b80a3eb714398d6907027dc0534177b7539119f4454 + languageName: node + linkType: hard + +"side-channel-list@npm:^1.0.0": + version: 1.0.1 + resolution: "side-channel-list@npm:1.0.1" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.4" + checksum: 10c0/d346c787fd2f9f1c2fdea14f00e8250118db0e7596d85a6cb9faa75f105d31a73a8f7a341c93d7df2a2429098c3d37a77bd3be9e88c37094b8c01807bc77c7a2 + languageName: node + linkType: hard + +"side-channel-map@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-map@npm:1.0.1" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.5" + object-inspect: "npm:^1.13.3" + checksum: 10c0/010584e6444dd8a20b85bc926d934424bd809e1a3af941cace229f7fdcb751aada0fb7164f60c2e22292b7fa3c0ff0bce237081fd4cdbc80de1dc68e95430672 + languageName: node + linkType: hard + +"side-channel-weakmap@npm:^1.0.2": + version: 1.0.2 + resolution: "side-channel-weakmap@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.5" + object-inspect: "npm:^1.13.3" + side-channel-map: "npm:^1.0.1" + checksum: 10c0/71362709ac233e08807ccd980101c3e2d7efe849edc51455030327b059f6c4d292c237f94dc0685031dd11c07dd17a68afde235d6cf2102d949567f98ab58185 + languageName: node + linkType: hard + +"side-channel@npm:^1.1.0": + version: 1.1.0 + resolution: "side-channel@npm:1.1.0" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.3" + side-channel-list: "npm:^1.0.0" + side-channel-map: "npm:^1.0.1" + side-channel-weakmap: "npm:^1.0.2" + checksum: 10c0/cb20dad41eb032e6c24c0982e1e5a24963a28aa6122b4f05b3f3d6bf8ae7fd5474ef382c8f54a6a3ab86e0cac4d41a23bd64ede3970e5bfb50326ba02a7996e6 + languageName: node + linkType: hard + +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: 10c0/3def8f8e516fbb34cb6ae415b07ccc5d9c018d85b4b8611e3dc6f8be6d1899f693a4382913c9ed51a06babb5201639d76453ab297d1c54a456544acf5c892e34 + languageName: node + linkType: hard + +"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.7": + version: 3.0.7 + resolution: "signal-exit@npm:3.0.7" + checksum: 10c0/25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912 + languageName: node + linkType: hard + +"signal-exit@npm:^4.1.0": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 + languageName: node + linkType: hard + +"slash@npm:^3.0.0": + version: 3.0.0 + resolution: "slash@npm:3.0.0" + checksum: 10c0/e18488c6a42bdfd4ac5be85b2ced3ccd0224773baae6ad42cfbb9ec74fc07f9fa8396bd35ee638084ead7a2a0818eb5e7151111544d4731ce843019dab4be47b + languageName: node + linkType: hard + +"slice-ansi@npm:0.0.4": + version: 0.0.4 + resolution: "slice-ansi@npm:0.0.4" + checksum: 10c0/997d4cc73e34aa8c0f60bdb71701b16c062cc4acd7a95e3b10e8c05d790eb5e735d9b470270dc6f443b1ba21492db7ceb849d5c93011d1256061bf7ed7216c7a + languageName: node + linkType: hard + +"slice-ansi@npm:^8.0.0": + version: 8.0.0 + resolution: "slice-ansi@npm:8.0.0" + dependencies: + ansi-styles: "npm:^6.2.3" + is-fullwidth-code-point: "npm:^5.1.0" + checksum: 10c0/0ce4aa91febb7cea4a00c2c27bb820fa53b6d2862ce0f80f7120134719f7914fc416b0ed966cf35250a3169e152916392f35917a2d7cad0fcc5d8b841010fa9a + languageName: node + linkType: hard + +"source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf + languageName: node + linkType: hard + +"stack-utils@npm:^2.0.6": + version: 2.0.6 + resolution: "stack-utils@npm:2.0.6" + dependencies: + escape-string-regexp: "npm:^2.0.0" + checksum: 10c0/651c9f87667e077584bbe848acaecc6049bc71979f1e9a46c7b920cad4431c388df0f51b8ad7cfd6eed3db97a2878d0fc8b3122979439ea8bac29c61c95eec8a + languageName: node + linkType: hard + +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 10c0/89a1416668f950236dd5ac9f9a6b2588e1b9b62b1b6ad8dff1bfc5d1a15dbf0aafc9b52d2226d00c28dffff212da464eaeebfc6b7578b9d180cef3e3782c5983 + languageName: node + linkType: hard + +"std-env@npm:^3.9.0": + version: 3.10.0 + resolution: "std-env@npm:3.10.0" + checksum: 10c0/1814927a45004d36dde6707eaf17552a546769bc79a6421be2c16ce77d238158dfe5de30910b78ec30d95135cc1c59ea73ee22d2ca170f8b9753f84da34c427f + languageName: node + linkType: hard + +"string-width@npm:^1.0.1": + version: 1.0.2 + resolution: "string-width@npm:1.0.2" + dependencies: + code-point-at: "npm:^1.0.0" + is-fullwidth-code-point: "npm:^1.0.0" + strip-ansi: "npm:^3.0.0" + checksum: 10c0/c558438baed23a9ab9370bb6a939acbdb2b2ffc517838d651aad0f5b2b674fb85d460d9b1d0b6a4c210dffd09e3235222d89a5bd4c0c1587f78b2bb7bc00c65e + languageName: node + linkType: hard + +"string-width@npm:^2.1.1": + version: 2.1.1 + resolution: "string-width@npm:2.1.1" + dependencies: + is-fullwidth-code-point: "npm:^2.0.0" + strip-ansi: "npm:^4.0.0" + checksum: 10c0/e5f2b169fcf8a4257a399f95d069522f056e92ec97dbdcb9b0cdf14d688b7ca0b1b1439a1c7b9773cd79446cbafd582727279d6bfdd9f8edd306ea5e90e5b610 + languageName: node + linkType: hard + +"string-width@npm:^4.1.0, string-width@npm:^4.2.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b + languageName: node + linkType: hard + +"string-width@npm:^7.0.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + +"string-width@npm:^8.1.0, string-width@npm:^8.1.1, string-width@npm:^8.2.0": + version: 8.2.1 + resolution: "string-width@npm:8.2.1" + dependencies: + get-east-asian-width: "npm:^1.5.0" + strip-ansi: "npm:^7.1.2" + checksum: 10c0/d467b4eaf4c40a01bb438a2620e77badd2456ffd5131c9973abe4f3acf7c802d5b21f3b6a00a5e33a7fc28ca8f9c103226e01bac61e9f259659c6f46d78e353a + languageName: node + linkType: hard + +"strip-ansi@npm:^3.0.0, strip-ansi@npm:^3.0.1": + version: 3.0.1 + resolution: "strip-ansi@npm:3.0.1" + dependencies: + ansi-regex: "npm:^2.0.0" + checksum: 10c0/f6e7fbe8e700105dccf7102eae20e4f03477537c74b286fd22cfc970f139002ed6f0d9c10d0e21aa9ed9245e0fa3c9275930e8795c5b947da136e4ecb644a70f + languageName: node + linkType: hard + +"strip-ansi@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-ansi@npm:4.0.0" + dependencies: + ansi-regex: "npm:^3.0.0" + checksum: 10c0/d75d9681e0637ea316ddbd7d4d3be010b1895a17e885155e0ed6a39755ae0fd7ef46e14b22162e66a62db122d3a98ab7917794e255532ab461bb0a04feb03e7d + languageName: node + linkType: hard + +"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 + languageName: node + linkType: hard + +"strip-ansi@npm:^7.1.0, strip-ansi@npm:^7.1.2": + version: 7.2.0 + resolution: "strip-ansi@npm:7.2.0" + dependencies: + ansi-regex: "npm:^6.2.2" + checksum: 10c0/544d13b7582f8254811ea97db202f519e189e59d35740c46095897e254e4f1aa9fe1524a83ad6bc5ad67d4dd6c0281d2e0219ed62b880a6238a16a17d375f221 + languageName: node + linkType: hard + +"strip-literal@npm:^3.0.0": + version: 3.1.0 + resolution: "strip-literal@npm:3.1.0" + dependencies: + js-tokens: "npm:^9.0.1" + checksum: 10c0/50918f669915d9ad0fe4b7599902b735f853f2201c97791ead00104a654259c0c61bc2bc8fa3db05109339b61f4cf09e47b94ecc874ffbd0e013965223893af8 + languageName: node + linkType: hard + +"subscriptions-transport-ws@npm:^0.9.16": + version: 0.9.19 + resolution: "subscriptions-transport-ws@npm:0.9.19" + dependencies: + backo2: "npm:^1.0.2" + eventemitter3: "npm:^3.1.0" + iterall: "npm:^1.2.1" + symbol-observable: "npm:^1.0.4" + ws: "npm:^5.2.0 || ^6.0.0 || ^7.0.0" + peerDependencies: + graphql: ">=0.10.0" + checksum: 10c0/6f2ade56865f0ba291d3ff82c79781b051c2374873bac853286fedfdbc05001b8c4018ab7cba44af667ead7f573e48d18892d58a8f9ca8d90dfb4bff5c125045 + languageName: node + linkType: hard + +"supports-color@npm:^2.0.0": + version: 2.0.0 + resolution: "supports-color@npm:2.0.0" + checksum: 10c0/570e0b63be36cccdd25186350a6cb2eaad332a95ff162fa06d9499982315f2fe4217e69dd98e862fbcd9c81eaff300a825a1fe7bf5cc752e5b84dfed042b0dda + languageName: node + linkType: hard + +"supports-color@npm:^5.3.0": + version: 5.5.0 + resolution: "supports-color@npm:5.5.0" + dependencies: + has-flag: "npm:^3.0.0" + checksum: 10c0/6ae5ff319bfbb021f8a86da8ea1f8db52fac8bd4d499492e30ec17095b58af11f0c55f8577390a749b1c4dde691b6a0315dab78f5f54c9b3d83f8fb5905c1c05 + languageName: node + linkType: hard + +"symbol-observable@npm:^1.0.4, symbol-observable@npm:^1.1.0": + version: 1.2.0 + resolution: "symbol-observable@npm:1.2.0" + checksum: 10c0/009fee50798ef80ed4b8195048288f108b03de162db07493f2e1fd993b33fafa72d659e832b584da5a2427daa78e5a738fb2a9ab027ee9454252e0bedbcd1fdc + languageName: node + linkType: hard + +"tagged-tag@npm:^1.0.0": + version: 1.0.0 + resolution: "tagged-tag@npm:1.0.0" + checksum: 10c0/91d25c9ffb86a91f20522cefb2cbec9b64caa1febe27ad0df52f08993ff60888022d771e868e6416cf2e72dab68449d2139e8709ba009b74c6c7ecd4000048d1 + languageName: node + linkType: hard + +"tar@npm:^7.5.4": + version: 7.5.15 + resolution: "tar@npm:7.5.15" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.1.0" + yallist: "npm:^5.0.0" + checksum: 10c0/8f039edb1d12fdd7df6c6f9877d125afe9f3da3f5f9317df326fdd090d48793d6998cede1506a1471f3e3a250db270a89dace28005eb5e99c5a9132d704ac956 + languageName: node + linkType: hard + +"terminal-size@npm:^4.0.1": + version: 4.0.1 + resolution: "terminal-size@npm:4.0.1" + checksum: 10c0/89afd9d816dd9dbfe4499da9aeea70491bbde4ff4592226a9c8ac71074a7580afead6a78e95ecc35f6d42e09087b55ffcb1019302cd55e0cc957b6ce5c4847e8 + languageName: node + linkType: hard + +"tinybench@npm:^2.9.0": + version: 2.9.0 + resolution: "tinybench@npm:2.9.0" + checksum: 10c0/c3500b0f60d2eb8db65250afe750b66d51623057ee88720b7f064894a6cb7eb93360ca824a60a31ab16dab30c7b1f06efe0795b352e37914a9d4bad86386a20c + languageName: node + linkType: hard + +"tinyexec@npm:^0.3.2": + version: 0.3.2 + resolution: "tinyexec@npm:0.3.2" + checksum: 10c0/3efbf791a911be0bf0821eab37a3445c2ba07acc1522b1fa84ae1e55f10425076f1290f680286345ed919549ad67527d07281f1c19d584df3b74326909eb1f90 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.14, tinyglobby@npm:^0.2.15": + version: 0.2.16 + resolution: "tinyglobby@npm:0.2.16" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/f2e09fd93dd95c41e522113b686ff6f7c13020962f8698a864a257f3d7737599afc47722b7ab726e12f8a813f779906187911ff8ee6701ede65072671a7e934b + languageName: node + linkType: hard + +"tinypool@npm:^1.1.1": + version: 1.1.1 + resolution: "tinypool@npm:1.1.1" + checksum: 10c0/bf26727d01443061b04fa863f571016950888ea994ba0cd8cba3a1c51e2458d84574341ab8dbc3664f1c3ab20885c8cf9ff1cc4b18201f04c2cde7d317fff69b + languageName: node + linkType: hard + +"tinyrainbow@npm:^2.0.0": + version: 2.0.0 + resolution: "tinyrainbow@npm:2.0.0" + checksum: 10c0/c83c52bef4e0ae7fb8ec6a722f70b5b6fa8d8be1c85792e829f56c0e1be94ab70b293c032dc5048d4d37cfe678f1f5babb04bdc65fd123098800148ca989184f + languageName: node + linkType: hard + +"tinyspy@npm:^4.0.3": + version: 4.0.4 + resolution: "tinyspy@npm:4.0.4" + checksum: 10c0/a8020fc17799251e06a8398dcc352601d2770aa91c556b9531ecd7a12581161fd1c14e81cbdaff0c1306c93bfdde8ff6d1c1a3f9bbe6d91604f0fd4e01e2f1eb + languageName: node + linkType: hard + +"tmp@npm:^0.0.33": + version: 0.0.33 + resolution: "tmp@npm:0.0.33" + dependencies: + os-tmpdir: "npm:~1.0.2" + checksum: 10c0/69863947b8c29cabad43fe0ce65cec5bb4b481d15d4b4b21e036b060b3edbf3bc7a5541de1bacb437bb3f7c4538f669752627fdf9b4aaf034cebd172ba373408 + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: "npm:^7.0.0" + checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 + languageName: node + linkType: hard + +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11 + languageName: node + linkType: hard + +"tsconfck@npm:^3.0.3": + version: 3.1.6 + resolution: "tsconfck@npm:3.1.6" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + bin: + tsconfck: bin/tsconfck.js + checksum: 10c0/269c3c513540be44844117bb9b9258fe6f8aeab026d32aeebf458d5299125f330711429dbb556dbf125a0bc25f4a81e6c24ac96de2740badd295c3fb400f66c4 + languageName: node + linkType: hard + +"tslib@npm:^1.9.0, tslib@npm:^1.9.3": + version: 1.14.1 + resolution: "tslib@npm:1.14.1" + checksum: 10c0/69ae09c49eea644bc5ebe1bca4fa4cc2c82b7b3e02f43b84bd891504edf66dbc6b2ec0eef31a957042de2269139e4acff911e6d186a258fb14069cd7f6febce2 + languageName: node + linkType: hard + +"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.4.0": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + +"tsx@npm:^4.0.0": + version: 4.22.2 + resolution: "tsx@npm:4.22.2" + dependencies: + esbuild: "npm:~0.28.0" + fsevents: "npm:~2.3.3" + dependenciesMeta: + fsevents: + optional: true + bin: + tsx: dist/cli.mjs + checksum: 10c0/5e9a9b5577642b6084bac14450379bdb72567a9bf47b3508470f3ccd530693358ab0c5eb86c57c249260f2ffa0549e2e4cedc93cbe526bbae5ef4d215e1d57a3 + languageName: node + linkType: hard + +"twenty-client-sdk@npm:2.4.0": + version: 2.4.0 + resolution: "twenty-client-sdk@npm:2.4.0" + dependencies: + "@genql/cli": "npm:^3.0.3" + "@genql/runtime": "npm:^2.10.0" + esbuild: "npm:^0.25.0" + graphql: "npm:^16.8.1" + checksum: 10c0/cdd9e70a59b8ce16f40e84b79c16e89b984dfcbb04e4646341fea40911acee8eab5aa61872421f25212949e677a75ed51af99e61829f2902af0f0a10e6e5614d + languageName: node + linkType: hard + +"twenty-partners@workspace:.": + version: 0.0.0-use.local + resolution: "twenty-partners@workspace:." + dependencies: + "@types/node": "npm:^24.7.2" + "@types/react": "npm:^19.0.0" + dotenv: "npm:^16.0.0" + oxlint: "npm:^0.16.0" + react: "npm:^19.0.0" + react-dom: "npm:^19.0.0" + tsx: "npm:^4.0.0" + twenty-client-sdk: "npm:2.4.0" + twenty-sdk: "npm:2.4.0" + typescript: "npm:^5.9.3" + vite-tsconfig-paths: "npm:^4.2.1" + vitest: "npm:^3.1.1" + languageName: unknown + linkType: soft + +"twenty-sdk@npm:2.4.0": + version: 2.4.0 + resolution: "twenty-sdk@npm:2.4.0" + dependencies: + "@genql/cli": "npm:^3.0.3" + "@genql/runtime": "npm:^2.10.0" + "@sniptt/guards": "npm:^0.2.0" + axios: "npm:^1.13.5" + chalk: "npm:^5.3.0" + chokidar: "npm:^4.0.0" + commander: "npm:^12.0.0" + dotenv: "npm:^16.4.0" + esbuild: "npm:^0.25.0" + graphql: "npm:^16.8.1" + graphql-sse: "npm:^2.5.4" + ink: "npm:^6.8.0" + inquirer: "npm:^10.0.0" + jsonc-parser: "npm:^3.2.0" + preact: "npm:^10.28.3" + react: "npm:^19.0.0" + react-dom: "npm:^19.0.0" + tinyglobby: "npm:^0.2.15" + twenty-client-sdk: "npm:2.4.0" + typescript: "npm:^5.9.2" + uuid: "npm:^13.0.0" + vite: "npm:^7.0.0" + vite-tsconfig-paths: "npm:^4.2.1" + zod: "npm:^4.1.11" + bin: + twenty: dist/cli.cjs + checksum: 10c0/7266755dd0f192f0107b25b72db7340a8476f4b97f3e8d2fa539010f6702f9104463da8ddca9318b2e7df2d6be43362be5eddb95af2067d8863578907b713617 + languageName: node + linkType: hard + +"type-fest@npm:^0.21.3": + version: 0.21.3 + resolution: "type-fest@npm:0.21.3" + checksum: 10c0/902bd57bfa30d51d4779b641c2bc403cdf1371fb9c91d3c058b0133694fcfdb817aef07a47f40faf79039eecbaa39ee9d3c532deff244f3a19ce68cea71a61e8 + languageName: node + linkType: hard + +"type-fest@npm:^5.4.1": + version: 5.6.0 + resolution: "type-fest@npm:5.6.0" + dependencies: + tagged-tag: "npm:^1.0.0" + checksum: 10c0/5468a8ffda7f3904e6f7bbd8069eb8b6dd4bd9156e206df7a01d09a73e28cd1afedf74ead9d0fc12841c8c90074194859feca240511c50800962fde1bd9ddcbc + languageName: node + linkType: hard + +"typescript@npm:^5.9.2, typescript@npm:^5.9.3": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A^5.9.2#optional!builtin, typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 + languageName: node + linkType: hard + +"undici-types@npm:>=7.24.0 <7.24.7": + version: 7.24.6 + resolution: "undici-types@npm:7.24.6" + checksum: 10c0/d9cd8befb643ac904615c280a095ba4240531f6bb4a5e75a22a7483630ca8d3f1016d2ab6ace6ceda1f63b3a2db2fe037fafe121d6917a0187573aa548ff78ca + languageName: node + linkType: hard + +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard + +"undici-types@npm:~7.16.0": + version: 7.16.0 + resolution: "undici-types@npm:7.16.0" + checksum: 10c0/3033e2f2b5c9f1504bdc5934646cb54e37ecaca0f9249c983f7b1fc2e87c6d18399ebb05dc7fd5419e02b2e915f734d872a65da2e3eeed1813951c427d33cc9a + languageName: node + linkType: hard + +"undici@npm:^5.18.0": + version: 5.29.0 + resolution: "undici@npm:5.29.0" + dependencies: + "@fastify/busboy": "npm:^2.0.0" + checksum: 10c0/e4e4d631ca54ee0ad82d2e90e7798fa00a106e27e6c880687e445cc2f13b4bc87c5eba2a88c266c3eecffb18f26e227b778412da74a23acc374fca7caccec49b + languageName: node + linkType: hard + +"undici@npm:^6.25.0": + version: 6.25.0 + resolution: "undici@npm:6.25.0" + checksum: 10c0/2597cc6689bdb02c210c557b1f85febbfda65becae6e6fc1061508e2f33734d25207f81cd8af56ada9956329eb3a7bd7431e87dcfeceba20ee87059b57dcf985 + languageName: node + linkType: hard + +"unfetch@npm:^4.2.0": + version: 4.2.0 + resolution: "unfetch@npm:4.2.0" + checksum: 10c0/a5c0a896a6f09f278b868075aea65652ad185db30e827cb7df45826fe5ab850124bf9c44c4dafca4bf0c55a0844b17031e8243467fcc38dd7a7d435007151f1b + languageName: node + linkType: hard + +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: 10c0/73e8ee3809041ca8b818efb141801a1004e3fc0002727f1531f4de613ea281b494a40909596dae4a042a4fb6cd385af5d4db2e137b1362e0e91384b828effd3a + languageName: node + linkType: hard + +"unixify@npm:^1.0.0": + version: 1.0.0 + resolution: "unixify@npm:1.0.0" + dependencies: + normalize-path: "npm:^2.1.1" + checksum: 10c0/8b89100619ebde9f0ab4024a4d402316fb7b1d4853723410fc828944e8d3d01480f210cddf94d9a1699559f8180d861eb6323da8011b7bcc1bbaf6a11a5b1f1e + languageName: node + linkType: hard + +"utility-types@npm:^3.10.0": + version: 3.11.0 + resolution: "utility-types@npm:3.11.0" + checksum: 10c0/2f1580137b0c3e6cf5405f37aaa8f5249961a76d26f1ca8efc0ff49a2fc0e0b2db56de8e521a174d075758e0c7eb3e590edec0832eb44478b958f09914920f19 + languageName: node + linkType: hard + +"uuid@npm:^13.0.0": + version: 13.0.2 + resolution: "uuid@npm:13.0.2" + bin: + uuid: dist-node/bin/uuid + checksum: 10c0/32c7ee84fa7c7966cc09b3a1514a752a8e2f609f15c00033fbaedc0255d577d1877b839f11ee537f37e587bbc620bbb98c3b3a1482931b8ed47d79497cd7a7cd + languageName: node + linkType: hard + +"value-or-promise@npm:^1.0.12": + version: 1.0.12 + resolution: "value-or-promise@npm:1.0.12" + checksum: 10c0/b75657b74e4d17552bd88e0c2857020fbab34a4d091dc058db18c470e7da0336067e72c130b3358e3321ac0a6ff11c0b92b67a382318a3705ad5d57de7ff3262 + languageName: node + linkType: hard + +"vite-node@npm:3.2.4": + version: 3.2.4 + resolution: "vite-node@npm:3.2.4" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.4.1" + es-module-lexer: "npm:^1.7.0" + pathe: "npm:^2.0.3" + vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" + bin: + vite-node: vite-node.mjs + checksum: 10c0/6ceca67c002f8ef6397d58b9539f80f2b5d79e103a18367288b3f00a8ab55affa3d711d86d9112fce5a7fa658a212a087a005a045eb8f4758947dd99af2a6c6b + languageName: node + linkType: hard + +"vite-tsconfig-paths@npm:^4.2.1": + version: 4.3.2 + resolution: "vite-tsconfig-paths@npm:4.3.2" + dependencies: + debug: "npm:^4.1.1" + globrex: "npm:^0.1.2" + tsconfck: "npm:^3.0.3" + peerDependencies: + vite: "*" + peerDependenciesMeta: + vite: + optional: true + checksum: 10c0/f390ac1d1c3992fc5ac50f9274c1090f8b55ab34a89ea88893db9a6924a3b26c9f64bc1163615150ad100749db73b6b2cf1d57f6cd60df6e762ceb5b8ad30024 + languageName: node + linkType: hard + +"vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0, vite@npm:^7.0.0": + version: 7.3.3 + resolution: "vite@npm:7.3.3" + dependencies: + esbuild: "npm:^0.27.0" + fdir: "npm:^6.5.0" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.3" + postcss: "npm:^8.5.6" + rollup: "npm:^4.43.0" + tinyglobby: "npm:^0.2.15" + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/44fed2591d5d0a9d1f6313e0a4330659b7f1eec57e542558f12a924c53b450a84b9fad6d57ac28ec739eca1cf5ff0f62e41b965e3806c47eefdbbe13b74ec9ae + languageName: node + linkType: hard + +"vitest@npm:^3.1.1": + version: 3.2.4 + resolution: "vitest@npm:3.2.4" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/expect": "npm:3.2.4" + "@vitest/mocker": "npm:3.2.4" + "@vitest/pretty-format": "npm:^3.2.4" + "@vitest/runner": "npm:3.2.4" + "@vitest/snapshot": "npm:3.2.4" + "@vitest/spy": "npm:3.2.4" + "@vitest/utils": "npm:3.2.4" + chai: "npm:^5.2.0" + debug: "npm:^4.4.1" + expect-type: "npm:^1.2.1" + magic-string: "npm:^0.30.17" + pathe: "npm:^2.0.3" + picomatch: "npm:^4.0.2" + std-env: "npm:^3.9.0" + tinybench: "npm:^2.9.0" + tinyexec: "npm:^0.3.2" + tinyglobby: "npm:^0.2.14" + tinypool: "npm:^1.1.1" + tinyrainbow: "npm:^2.0.0" + vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" + vite-node: "npm:3.2.4" + why-is-node-running: "npm:^2.3.0" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/debug": ^4.1.12 + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + "@vitest/browser": 3.2.4 + "@vitest/ui": 3.2.4 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/debug": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 10c0/5bf53ede3ae6a0e08956d72dab279ae90503f6b5a05298a6a5e6ef47d2fd1ab386aaf48fafa61ed07a0ebfe9e371772f1ccbe5c258dd765206a8218bf2eb79eb + languageName: node + linkType: hard + +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db + languageName: node + linkType: hard + +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: "npm:~0.0.3" + webidl-conversions: "npm:^3.0.0" + checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5 + languageName: node + linkType: hard + +"which-module@npm:^2.0.0": + version: 2.0.1 + resolution: "which-module@npm:2.0.1" + checksum: 10c0/087038e7992649eaffa6c7a4f3158d5b53b14cf5b6c1f0e043dccfacb1ba179d12f17545d5b85ebd94a42ce280a6fe65d0cbcab70f4fc6daad1dfae85e0e6a3e + languageName: node + linkType: hard + +"which@npm:^6.0.0": + version: 6.0.1 + resolution: "which@npm:6.0.1" + dependencies: + isexe: "npm:^4.0.0" + bin: + node-which: bin/which.js + checksum: 10c0/7e710e54ea36d2d6183bee2f9caa27a3b47b9baf8dee55a199b736fcf85eab3b9df7556fca3d02b50af7f3dfba5ea3a45644189836df06267df457e354da66d5 + languageName: node + linkType: hard + +"why-is-node-running@npm:^2.3.0": + version: 2.3.0 + resolution: "why-is-node-running@npm:2.3.0" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 10c0/1cde0b01b827d2cf4cb11db962f3958b9175d5d9e7ac7361d1a7b0e2dc6069a263e69118bd974c4f6d0a890ef4eedfe34cf3d5167ec14203dbc9a18620537054 + languageName: node + linkType: hard + +"widest-line@npm:^6.0.0": + version: 6.0.0 + resolution: "widest-line@npm:6.0.0" + dependencies: + string-width: "npm:^8.1.0" + checksum: 10c0/735f1fdcd97fe765a07bb8b5e73c020bed8e53ab34e83ce0ef01693ba3c914d9e7977fe5f5facf0d0b670297a82dd5e376d3efa0896860dfcdaf7cd6924c0fb7 + languageName: node + linkType: hard + +"wrap-ansi@npm:^3.0.1": + version: 3.0.1 + resolution: "wrap-ansi@npm:3.0.1" + dependencies: + string-width: "npm:^2.1.1" + strip-ansi: "npm:^4.0.0" + checksum: 10c0/ad6fed8f242c26755badaf452da154122d0d862f8b7aab56e758466857f230efafdc5fbffca026650b947ac3fc0eb563df5c05b9e2190a52a4a68f4eef3d4555 + languageName: node + linkType: hard + +"wrap-ansi@npm:^6.2.0": + version: 6.2.0 + resolution: "wrap-ansi@npm:6.2.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/baad244e6e33335ea24e86e51868fe6823626e3a3c88d9a6674642afff1d34d9a154c917e74af8d845fd25d170c4ea9cf69a47133c3f3656e1252b3d462d9f6c + languageName: node + linkType: hard + +"wrap-ansi@npm:^9.0.0": + version: 9.0.2 + resolution: "wrap-ansi@npm:9.0.2" + dependencies: + ansi-styles: "npm:^6.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/3305839b9a0d6fb930cb63a52f34d3936013d8b0682ff3ec133c9826512620f213800ffa19ea22904876d5b7e9a3c1f40682f03597d986a4ca881fa7b033688c + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0 + languageName: node + linkType: hard + +"ws@npm:^5.2.0 || ^6.0.0 || ^7.0.0": + version: 7.5.10 + resolution: "ws@npm:7.5.10" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/bd7d5f4aaf04fae7960c23dcb6c6375d525e00f795dd20b9385902bd008c40a94d3db3ce97d878acc7573df852056ca546328b27b39f47609f80fb22a0a9b61d + languageName: node + linkType: hard + +"ws@npm:^6.1.4": + version: 6.2.3 + resolution: "ws@npm:6.2.3" + dependencies: + async-limiter: "npm:~1.0.0" + checksum: 10c0/56a35b9799993cea7ce2260197e7879f21bbbb194a967f31acbbda6f7f46ecda4365951966fb062044c95197e19fb2f053be6f65c172435455186835f494de41 + languageName: node + linkType: hard + +"ws@npm:^8.18.0": + version: 8.20.1 + resolution: "ws@npm:8.20.1" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/ce162433218399cdedeb76fd33363d4d86a7d910058d4e3c679dce08cea65d6da6b39f11baa4d7808d024cf46ed88f6a05c17611621aaad8fc5e62edacc30c5d + languageName: node + linkType: hard + +"y18n@npm:^4.0.0": + version: 4.0.3 + resolution: "y18n@npm:4.0.3" + checksum: 10c0/308a2efd7cc296ab2c0f3b9284fd4827be01cfeb647b3ba18230e3a416eb1bc887ac050de9f8c4fd9e7856b2e8246e05d190b53c96c5ad8d8cb56dffb6f81024 + languageName: node + linkType: hard + +"yallist@npm:^5.0.0": + version: 5.0.0 + resolution: "yallist@npm:5.0.0" + checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416 + languageName: node + linkType: hard + +"yargs-parser@npm:^18.1.2": + version: 18.1.3 + resolution: "yargs-parser@npm:18.1.3" + dependencies: + camelcase: "npm:^5.0.0" + decamelize: "npm:^1.2.0" + checksum: 10c0/25df918833592a83f52e7e4f91ba7d7bfaa2b891ebf7fe901923c2ee797534f23a176913ff6ff7ebbc1cc1725a044cc6a6539fed8bfd4e13b5b16376875f9499 + languageName: node + linkType: hard + +"yargs@npm:^15.3.1": + version: 15.4.1 + resolution: "yargs@npm:15.4.1" + dependencies: + cliui: "npm:^6.0.0" + decamelize: "npm:^1.2.0" + find-up: "npm:^4.1.0" + get-caller-file: "npm:^2.0.1" + require-directory: "npm:^2.1.1" + require-main-filename: "npm:^2.0.0" + set-blocking: "npm:^2.0.0" + string-width: "npm:^4.2.0" + which-module: "npm:^2.0.0" + y18n: "npm:^4.0.0" + yargs-parser: "npm:^18.1.2" + checksum: 10c0/f1ca680c974333a5822732825cca7e95306c5a1e7750eb7b973ce6dc4f97a6b0a8837203c8b194f461969bfe1fb1176d1d423036635285f6010b392fa498ab2d + languageName: node + linkType: hard + +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f + languageName: node + linkType: hard + +"yoctocolors-cjs@npm:^2.1.2": + version: 2.1.3 + resolution: "yoctocolors-cjs@npm:2.1.3" + checksum: 10c0/584168ef98eb5d913473a4858dce128803c4a6cd87c0f09e954fa01126a59a33ab9e513b633ad9ab953786ed16efdd8c8700097a51635aafaeed3fef7712fa79 + languageName: node + linkType: hard + +"yoga-layout@npm:~3.2.1": + version: 3.2.1 + resolution: "yoga-layout@npm:3.2.1" + checksum: 10c0/9001e51be993c85e03757e5a04a2b61b8b30c9e5a7865d0156ca87a6431a3b717d51eb4990bfe588189fcfeac688dd9c3de707bbd50d1c344a84e63974cc54a8 + languageName: node + linkType: hard + +"zen-observable-ts@npm:^0.8.21": + version: 0.8.21 + resolution: "zen-observable-ts@npm:0.8.21" + dependencies: + tslib: "npm:^1.9.3" + zen-observable: "npm:^0.8.0" + checksum: 10c0/fe4a02f862b5f7e8ae0f86230c37b84c7d5611f5c206981afb4043e732d04cf7067a6cbe1ba82d20f18b735a3387937195a12542158a631d308ae3959a1d93c4 + languageName: node + linkType: hard + +"zen-observable@npm:^0.8.0": + version: 0.8.15 + resolution: "zen-observable@npm:0.8.15" + checksum: 10c0/71cc2f2bbb537300c3f569e25693d37b3bc91f225cefce251a71c30bc6bb3e7f8e9420ca0eb57f2ac9e492b085b8dfa075fd1e8195c40b83c951dd59c6e4fbf8 + languageName: node + linkType: hard + +"zod@npm:^4.1.11": + version: 4.4.3 + resolution: "zod@npm:4.4.3" + checksum: 10c0/7ea31b558e88f9faf44f31dd185e2e1cbf51fed3081787fb96cc2534749b50c0acfc6da7f0922a7353ed092dd358c7d50c28ea96c94d04af64191bd33152eca3 + languageName: node + linkType: hard diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index d0a8d321952..9dfae1e6412 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -1764,6 +1764,18 @@ type WorkspaceUrls { subdomainUrl: String! } +type ApplicationRegistrationVariableDTO { + id: UUID! + key: String! + value: String + description: String! + isSecret: Boolean! + isRequired: Boolean! + isFilled: Boolean! + createdAt: DateTime! + updatedAt: DateTime! +} + type BillingTrialPeriod { duration: Float! isCreditCardRequired: Boolean! @@ -1968,18 +1980,6 @@ type RotateClientSecret { clientSecret: String! } -type ApplicationRegistrationVariableDTO { - id: UUID! - key: String! - value: String - description: String! - isSecret: Boolean! - isRequired: Boolean! - isFilled: Boolean! - createdAt: DateTime! - updatedAt: DateTime! -} - type Relation { type: RelationType! sourceObjectMetadata: Object! @@ -2476,6 +2476,18 @@ type ImapSmtpCaldavConnectionSuccess { connectedAccountId: String! } +type Webhook { + id: UUID! + targetUrl: String! + operations: [String!]! + description: String + secret: String! + applicationId: UUID! + createdAt: DateTime! + updatedAt: DateTime! + deletedAt: DateTime +} + type ToolIndexEntry { name: String! description: String! @@ -2902,18 +2914,6 @@ type MinimalMetadata { collectionHashes: [CollectionHash!]! } -type Webhook { - id: UUID! - targetUrl: String! - operations: [String!]! - description: String - secret: String! - applicationId: UUID! - createdAt: DateTime! - updatedAt: DateTime! - deletedAt: DateTime -} - type Query { navigationMenuItems: [NavigationMenuItem!]! navigationMenuItem(id: UUID!): NavigationMenuItem @@ -2982,6 +2982,8 @@ type Query { getRoles: [Role!]! getToolIndex: [ToolIndexEntry!]! getToolInputSchema(toolName: String!): JSON + webhooks: [Webhook!]! + webhook(id: UUID!): Webhook field( """The id of the record to find.""" id: UUID! @@ -2999,8 +3001,6 @@ type Query { myMessageChannels(connectedAccountId: UUID): [MessageChannel!]! myConnectedAccounts: [ConnectedAccountPublicDTO!]! myCalendarChannels(connectedAccountId: UUID): [CalendarChannel!]! - webhooks: [Webhook!]! - webhook(id: UUID!): Webhook minimalMetadata: MinimalMetadata! chatThreads: [AgentChatThread!]! chatThread(id: UUID!): AgentChatThread! @@ -3222,6 +3222,9 @@ type Mutation { upsertRowLevelPermissionPredicates(input: UpsertRowLevelPermissionPredicatesInput!): UpsertRowLevelPermissionPredicatesResult! assignRoleToAgent(agentId: UUID!, roleId: UUID!): Boolean! removeRoleFromAgent(agentId: UUID!): Boolean! + createWebhook(input: CreateWebhookInput!): Webhook! + updateWebhook(input: UpdateWebhookInput!): Webhook! + deleteWebhook(id: UUID!): Webhook! createOneField(input: CreateOneFieldMetadataInput!): Field! updateOneField(input: UpdateOneFieldMetadataInput!): Field! deleteOneField(input: DeleteOneFieldInput!): Field! @@ -3238,9 +3241,6 @@ type Mutation { deleteEmailGroupChannel(id: UUID!): MessageChannel! deleteConnectedAccount(id: UUID!): ConnectedAccountPublicDTO! updateCalendarChannel(input: UpdateCalendarChannelInput!): CalendarChannel! - createWebhook(input: CreateWebhookInput!): Webhook! - updateWebhook(input: UpdateWebhookInput!): Webhook! - deleteWebhook(id: UUID!): Webhook! createChatThread: AgentChatThread! sendChatMessage(threadId: UUID!, text: String!, messageId: UUID!, browsingContext: JSON, modelId: String, fileAttachments: [FileAttachmentInput!]): SendChatMessageResult! stopAgentChatStream(threadId: UUID!): Boolean! @@ -3294,7 +3294,6 @@ type Mutation { updateWorkspace(data: UpdateWorkspaceInput!): Workspace! deleteCurrentWorkspace: Workspace! checkCustomDomainValidRecords: DomainValidRecords - installApplication(appRegistrationId: String!, version: String): Boolean! runWorkspaceMigration(workspaceMigration: WorkspaceMigrationInput!): Boolean! uninstallApplication(universalIdentifier: String!): Boolean! createOIDCIdentityProvider(input: SetupOIDCSsoInput!): SetupSso! @@ -3315,7 +3314,8 @@ type Mutation { deleteEmailingDomain(id: String!): Boolean! verifyEmailingDomain(id: String!): EmailingDomain! createOneAppToken(input: CreateOneAppTokenInput!): AppToken! - installMarketplaceApp(universalIdentifier: String!, version: String): Boolean! + installMarketplaceApp(universalIdentifier: String!, version: String): Boolean! @deprecated(reason: "Use installApplication instead") + installApplication(universalIdentifier: String!, version: String): Application! syncMarketplaceCatalog: Boolean! createDevelopmentApplication(universalIdentifier: String!, name: String!): DevelopmentApplication! generateApplicationToken(applicationId: UUID!): ApplicationTokenPair! @@ -3392,7 +3392,7 @@ input UpdateViewFilterGroupInput { input CreateViewFilterInput { id: UUID fieldMetadataId: UUID! - operand: ViewFilterOperand = CONTAINS + operand: ViewFilterOperand value: JSON! viewFilterGroupId: UUID positionInViewFilterGroup: Float @@ -3500,7 +3500,7 @@ input UpsertViewWidgetViewFieldInput { input UpsertViewWidgetViewFilterInput { id: UUID fieldMetadataId: UUID! - operand: ViewFilterOperand = CONTAINS + operand: ViewFilterOperand value: JSON! viewFilterGroupId: UUID positionInViewFilterGroup: Float @@ -4047,6 +4047,29 @@ input RowLevelPermissionPredicateGroupInput { positionInRowLevelPermissionPredicateGroup: Float } +input CreateWebhookInput { + id: UUID + targetUrl: String! + operations: [String!]! + description: String + secret: String +} + +input UpdateWebhookInput { + """The id of the webhook to update""" + id: UUID! + + """The webhook fields to update""" + update: UpdateWebhookInputUpdates! +} + +input UpdateWebhookInputUpdates { + targetUrl: String + operations: [String!] + description: String + secret: String +} + input CreateOneFieldMetadataInput { """The record to create""" field: CreateFieldInput! @@ -4184,29 +4207,6 @@ input UpdateCalendarChannelInputUpdates { isSyncEnabled: Boolean } -input CreateWebhookInput { - id: UUID - targetUrl: String! - operations: [String!]! - description: String - secret: String -} - -input UpdateWebhookInput { - """The id of the webhook to update""" - id: UUID! - - """The webhook fields to update""" - update: UpdateWebhookInputUpdates! -} - -input UpdateWebhookInputUpdates { - targetUrl: String - operations: [String!] - description: String - secret: String -} - input FileAttachmentInput { id: UUID! filename: String! diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index 7e0025ea0e5..70080a01e7f 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -1401,6 +1401,19 @@ export interface WorkspaceUrls { __typename: 'WorkspaceUrls' } +export interface ApplicationRegistrationVariableDTO { + id: Scalars['UUID'] + key: Scalars['String'] + value?: Scalars['String'] + description: Scalars['String'] + isSecret: Scalars['Boolean'] + isRequired: Scalars['Boolean'] + isFilled: Scalars['Boolean'] + createdAt: Scalars['DateTime'] + updatedAt: Scalars['DateTime'] + __typename: 'ApplicationRegistrationVariableDTO' +} + export interface BillingTrialPeriod { duration: Scalars['Float'] isCreditCardRequired: Scalars['Boolean'] @@ -1609,19 +1622,6 @@ export interface RotateClientSecret { __typename: 'RotateClientSecret' } -export interface ApplicationRegistrationVariableDTO { - id: Scalars['UUID'] - key: Scalars['String'] - value?: Scalars['String'] - description: Scalars['String'] - isSecret: Scalars['Boolean'] - isRequired: Scalars['Boolean'] - isFilled: Scalars['Boolean'] - createdAt: Scalars['DateTime'] - updatedAt: Scalars['DateTime'] - __typename: 'ApplicationRegistrationVariableDTO' -} - export interface Relation { type: RelationType sourceObjectMetadata: Object @@ -2160,6 +2160,19 @@ export interface ImapSmtpCaldavConnectionSuccess { __typename: 'ImapSmtpCaldavConnectionSuccess' } +export interface Webhook { + id: Scalars['UUID'] + targetUrl: Scalars['String'] + operations: Scalars['String'][] + description?: Scalars['String'] + secret: Scalars['String'] + applicationId: Scalars['UUID'] + createdAt: Scalars['DateTime'] + updatedAt: Scalars['DateTime'] + deletedAt?: Scalars['DateTime'] + __typename: 'Webhook' +} + export interface ToolIndexEntry { name: Scalars['String'] description: Scalars['String'] @@ -2528,19 +2541,6 @@ export interface MinimalMetadata { __typename: 'MinimalMetadata' } -export interface Webhook { - id: Scalars['UUID'] - targetUrl: Scalars['String'] - operations: Scalars['String'][] - description?: Scalars['String'] - secret: Scalars['String'] - applicationId: Scalars['UUID'] - createdAt: Scalars['DateTime'] - updatedAt: Scalars['DateTime'] - deletedAt?: Scalars['DateTime'] - __typename: 'Webhook' -} - export interface Query { navigationMenuItems: NavigationMenuItem[] navigationMenuItem?: NavigationMenuItem @@ -2591,6 +2591,8 @@ export interface Query { getRoles: Role[] getToolIndex: ToolIndexEntry[] getToolInputSchema?: Scalars['JSON'] + webhooks: Webhook[] + webhook?: Webhook field: Field fields: FieldConnection getViewGroups: ViewGroup[] @@ -2599,8 +2601,6 @@ export interface Query { myMessageChannels: MessageChannel[] myConnectedAccounts: ConnectedAccountPublicDTO[] myCalendarChannels: CalendarChannel[] - webhooks: Webhook[] - webhook?: Webhook minimalMetadata: MinimalMetadata chatThreads: AgentChatThread[] chatThread: AgentChatThread @@ -2755,6 +2755,9 @@ export interface Mutation { upsertRowLevelPermissionPredicates: UpsertRowLevelPermissionPredicatesResult assignRoleToAgent: Scalars['Boolean'] removeRoleFromAgent: Scalars['Boolean'] + createWebhook: Webhook + updateWebhook: Webhook + deleteWebhook: Webhook createOneField: Field updateOneField: Field deleteOneField: Field @@ -2771,9 +2774,6 @@ export interface Mutation { deleteEmailGroupChannel: MessageChannel deleteConnectedAccount: ConnectedAccountPublicDTO updateCalendarChannel: CalendarChannel - createWebhook: Webhook - updateWebhook: Webhook - deleteWebhook: Webhook createChatThread: AgentChatThread sendChatMessage: SendChatMessageResult stopAgentChatStream: Scalars['Boolean'] @@ -2827,7 +2827,6 @@ export interface Mutation { updateWorkspace: Workspace deleteCurrentWorkspace: Workspace checkCustomDomainValidRecords?: DomainValidRecords - installApplication: Scalars['Boolean'] runWorkspaceMigration: Scalars['Boolean'] uninstallApplication: Scalars['Boolean'] createOIDCIdentityProvider: SetupSso @@ -2848,7 +2847,9 @@ export interface Mutation { deleteEmailingDomain: Scalars['Boolean'] verifyEmailingDomain: EmailingDomain createOneAppToken: AppToken + /** @deprecated Use installApplication instead */ installMarketplaceApp: Scalars['Boolean'] + installApplication: Application syncMarketplaceCatalog: Scalars['Boolean'] createDevelopmentApplication: DevelopmentApplication generateApplicationToken: ApplicationTokenPair @@ -4332,6 +4333,20 @@ export interface WorkspaceUrlsGenqlSelection{ __scalar?: boolean | number } +export interface ApplicationRegistrationVariableDTOGenqlSelection{ + id?: boolean | number + key?: boolean | number + value?: boolean | number + description?: boolean | number + isSecret?: boolean | number + isRequired?: boolean | number + isFilled?: boolean | number + createdAt?: boolean | number + updatedAt?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + export interface BillingTrialPeriodGenqlSelection{ duration?: boolean | number isCreditCardRequired?: boolean | number @@ -4553,20 +4568,6 @@ export interface RotateClientSecretGenqlSelection{ __scalar?: boolean | number } -export interface ApplicationRegistrationVariableDTOGenqlSelection{ - id?: boolean | number - key?: boolean | number - value?: boolean | number - description?: boolean | number - isSecret?: boolean | number - isRequired?: boolean | number - isFilled?: boolean | number - createdAt?: boolean | number - updatedAt?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - export interface RelationGenqlSelection{ type?: boolean | number sourceObjectMetadata?: ObjectGenqlSelection @@ -5165,6 +5166,20 @@ export interface ImapSmtpCaldavConnectionSuccessGenqlSelection{ __scalar?: boolean | number } +export interface WebhookGenqlSelection{ + id?: boolean | number + targetUrl?: boolean | number + operations?: boolean | number + description?: boolean | number + secret?: boolean | number + applicationId?: boolean | number + createdAt?: boolean | number + updatedAt?: boolean | number + deletedAt?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + export interface ToolIndexEntryGenqlSelection{ name?: boolean | number description?: boolean | number @@ -5540,20 +5555,6 @@ export interface MinimalMetadataGenqlSelection{ __scalar?: boolean | number } -export interface WebhookGenqlSelection{ - id?: boolean | number - targetUrl?: boolean | number - operations?: boolean | number - description?: boolean | number - secret?: boolean | number - applicationId?: boolean | number - createdAt?: boolean | number - updatedAt?: boolean | number - deletedAt?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - export interface QueryGenqlSelection{ navigationMenuItems?: NavigationMenuItemGenqlSelection navigationMenuItem?: (NavigationMenuItemGenqlSelection & { __args: {id: Scalars['UUID']} }) @@ -5616,6 +5617,8 @@ export interface QueryGenqlSelection{ getRoles?: RoleGenqlSelection getToolIndex?: ToolIndexEntryGenqlSelection getToolInputSchema?: { __args: {toolName: Scalars['String']} } + webhooks?: WebhookGenqlSelection + webhook?: (WebhookGenqlSelection & { __args: {id: Scalars['UUID']} }) field?: (FieldGenqlSelection & { __args: { /** The id of the record to find. */ id: Scalars['UUID']} }) @@ -5630,8 +5633,6 @@ export interface QueryGenqlSelection{ myMessageChannels?: (MessageChannelGenqlSelection & { __args?: {connectedAccountId?: (Scalars['UUID'] | null)} }) myConnectedAccounts?: ConnectedAccountPublicDTOGenqlSelection myCalendarChannels?: (CalendarChannelGenqlSelection & { __args?: {connectedAccountId?: (Scalars['UUID'] | null)} }) - webhooks?: WebhookGenqlSelection - webhook?: (WebhookGenqlSelection & { __args: {id: Scalars['UUID']} }) minimalMetadata?: MinimalMetadataGenqlSelection chatThreads?: AgentChatThreadGenqlSelection chatThread?: (AgentChatThreadGenqlSelection & { __args: {id: Scalars['UUID']} }) @@ -5807,6 +5808,9 @@ export interface MutationGenqlSelection{ upsertRowLevelPermissionPredicates?: (UpsertRowLevelPermissionPredicatesResultGenqlSelection & { __args: {input: UpsertRowLevelPermissionPredicatesInput} }) assignRoleToAgent?: { __args: {agentId: Scalars['UUID'], roleId: Scalars['UUID']} } removeRoleFromAgent?: { __args: {agentId: Scalars['UUID']} } + createWebhook?: (WebhookGenqlSelection & { __args: {input: CreateWebhookInput} }) + updateWebhook?: (WebhookGenqlSelection & { __args: {input: UpdateWebhookInput} }) + deleteWebhook?: (WebhookGenqlSelection & { __args: {id: Scalars['UUID']} }) createOneField?: (FieldGenqlSelection & { __args: {input: CreateOneFieldMetadataInput} }) updateOneField?: (FieldGenqlSelection & { __args: {input: UpdateOneFieldMetadataInput} }) deleteOneField?: (FieldGenqlSelection & { __args: {input: DeleteOneFieldInput} }) @@ -5823,9 +5827,6 @@ export interface MutationGenqlSelection{ deleteEmailGroupChannel?: (MessageChannelGenqlSelection & { __args: {id: Scalars['UUID']} }) deleteConnectedAccount?: (ConnectedAccountPublicDTOGenqlSelection & { __args: {id: Scalars['UUID']} }) updateCalendarChannel?: (CalendarChannelGenqlSelection & { __args: {input: UpdateCalendarChannelInput} }) - createWebhook?: (WebhookGenqlSelection & { __args: {input: CreateWebhookInput} }) - updateWebhook?: (WebhookGenqlSelection & { __args: {input: UpdateWebhookInput} }) - deleteWebhook?: (WebhookGenqlSelection & { __args: {id: Scalars['UUID']} }) createChatThread?: AgentChatThreadGenqlSelection sendChatMessage?: (SendChatMessageResultGenqlSelection & { __args: {threadId: Scalars['UUID'], text: Scalars['String'], messageId: Scalars['UUID'], browsingContext?: (Scalars['JSON'] | null), modelId?: (Scalars['String'] | null), fileAttachments?: (FileAttachmentInput[] | null)} }) stopAgentChatStream?: { __args: {threadId: Scalars['UUID']} } @@ -5879,7 +5880,6 @@ export interface MutationGenqlSelection{ updateWorkspace?: (WorkspaceGenqlSelection & { __args: {data: UpdateWorkspaceInput} }) deleteCurrentWorkspace?: WorkspaceGenqlSelection checkCustomDomainValidRecords?: DomainValidRecordsGenqlSelection - installApplication?: { __args: {appRegistrationId: Scalars['String'], version?: (Scalars['String'] | null)} } runWorkspaceMigration?: { __args: {workspaceMigration: WorkspaceMigrationInput} } uninstallApplication?: { __args: {universalIdentifier: Scalars['String']} } createOIDCIdentityProvider?: (SetupSsoGenqlSelection & { __args: {input: SetupOIDCSsoInput} }) @@ -5900,7 +5900,9 @@ export interface MutationGenqlSelection{ deleteEmailingDomain?: { __args: {id: Scalars['String']} } verifyEmailingDomain?: (EmailingDomainGenqlSelection & { __args: {id: Scalars['String']} }) createOneAppToken?: (AppTokenGenqlSelection & { __args: {input: CreateOneAppTokenInput} }) + /** @deprecated Use installApplication instead */ installMarketplaceApp?: { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} } + installApplication?: (ApplicationGenqlSelection & { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} }) syncMarketplaceCatalog?: boolean | number createDevelopmentApplication?: (DevelopmentApplicationGenqlSelection & { __args: {universalIdentifier: Scalars['String'], name: Scalars['String']} }) generateApplicationToken?: (ApplicationTokenPairGenqlSelection & { __args: {applicationId: Scalars['UUID']} }) @@ -6152,6 +6154,16 @@ export interface RowLevelPermissionPredicateInput {id?: (Scalars['UUID'] | null) export interface RowLevelPermissionPredicateGroupInput {id?: (Scalars['UUID'] | null),objectMetadataId: Scalars['UUID'],parentRowLevelPermissionPredicateGroupId?: (Scalars['UUID'] | null),logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator,positionInRowLevelPermissionPredicateGroup?: (Scalars['Float'] | null)} +export interface CreateWebhookInput {id?: (Scalars['UUID'] | null),targetUrl: Scalars['String'],operations: Scalars['String'][],description?: (Scalars['String'] | null),secret?: (Scalars['String'] | null)} + +export interface UpdateWebhookInput { +/** The id of the webhook to update */ +id: Scalars['UUID'], +/** The webhook fields to update */ +update: UpdateWebhookInputUpdates} + +export interface UpdateWebhookInputUpdates {targetUrl?: (Scalars['String'] | null),operations?: (Scalars['String'][] | null),description?: (Scalars['String'] | null),secret?: (Scalars['String'] | null)} + export interface CreateOneFieldMetadataInput { /** The record to create */ field: CreateFieldInput} @@ -6204,16 +6216,6 @@ export interface UpdateCalendarChannelInput {id: Scalars['UUID'],update: UpdateC export interface UpdateCalendarChannelInputUpdates {visibility?: (CalendarChannelVisibility | null),isContactAutoCreationEnabled?: (Scalars['Boolean'] | null),contactAutoCreationPolicy?: (CalendarChannelContactAutoCreationPolicy | null),isSyncEnabled?: (Scalars['Boolean'] | null)} -export interface CreateWebhookInput {id?: (Scalars['UUID'] | null),targetUrl: Scalars['String'],operations: Scalars['String'][],description?: (Scalars['String'] | null),secret?: (Scalars['String'] | null)} - -export interface UpdateWebhookInput { -/** The id of the webhook to update */ -id: Scalars['UUID'], -/** The webhook fields to update */ -update: UpdateWebhookInputUpdates} - -export interface UpdateWebhookInputUpdates {targetUrl?: (Scalars['String'] | null),operations?: (Scalars['String'][] | null),description?: (Scalars['String'] | null),secret?: (Scalars['String'] | null)} - export interface FileAttachmentInput {id: Scalars['UUID'],filename: Scalars['String']} export interface CreateSkillInput {id?: (Scalars['UUID'] | null),name: Scalars['String'],label: Scalars['String'],icon?: (Scalars['String'] | null),description?: (Scalars['String'] | null),content: Scalars['String']} @@ -7183,6 +7185,14 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null + const ApplicationRegistrationVariableDTO_possibleTypes: string[] = ['ApplicationRegistrationVariableDTO'] + export const isApplicationRegistrationVariableDTO = (obj?: { __typename?: any } | null): obj is ApplicationRegistrationVariableDTO => { + if (!obj?.__typename) throw new Error('__typename is missing in "isApplicationRegistrationVariableDTO"') + return ApplicationRegistrationVariableDTO_possibleTypes.includes(obj.__typename) + } + + + const BillingTrialPeriod_possibleTypes: string[] = ['BillingTrialPeriod'] export const isBillingTrialPeriod = (obj?: { __typename?: any } | null): obj is BillingTrialPeriod => { if (!obj?.__typename) throw new Error('__typename is missing in "isBillingTrialPeriod"') @@ -7367,14 +7377,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null - const ApplicationRegistrationVariableDTO_possibleTypes: string[] = ['ApplicationRegistrationVariableDTO'] - export const isApplicationRegistrationVariableDTO = (obj?: { __typename?: any } | null): obj is ApplicationRegistrationVariableDTO => { - if (!obj?.__typename) throw new Error('__typename is missing in "isApplicationRegistrationVariableDTO"') - return ApplicationRegistrationVariableDTO_possibleTypes.includes(obj.__typename) - } - - - const Relation_possibleTypes: string[] = ['Relation'] export const isRelation = (obj?: { __typename?: any } | null): obj is Relation => { if (!obj?.__typename) throw new Error('__typename is missing in "isRelation"') @@ -7935,6 +7937,14 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null + const Webhook_possibleTypes: string[] = ['Webhook'] + export const isWebhook = (obj?: { __typename?: any } | null): obj is Webhook => { + if (!obj?.__typename) throw new Error('__typename is missing in "isWebhook"') + return Webhook_possibleTypes.includes(obj.__typename) + } + + + const ToolIndexEntry_possibleTypes: string[] = ['ToolIndexEntry'] export const isToolIndexEntry = (obj?: { __typename?: any } | null): obj is ToolIndexEntry => { if (!obj?.__typename) throw new Error('__typename is missing in "isToolIndexEntry"') @@ -8199,14 +8209,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null - const Webhook_possibleTypes: string[] = ['Webhook'] - export const isWebhook = (obj?: { __typename?: any } | null): obj is Webhook => { - if (!obj?.__typename) throw new Error('__typename is missing in "isWebhook"') - return Webhook_possibleTypes.includes(obj.__typename) - } - - - const Query_possibleTypes: string[] = ['Query'] export const isQuery = (obj?: { __typename?: any } | null): obj is Query => { if (!obj?.__typename) throw new Error('__typename is missing in "isQuery"') diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index b426d01484f..bfff3b4d4ff 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -51,29 +51,29 @@ export default { 163, 167, 169, - 173, 174, - 181, - 184, - 187, + 175, + 182, + 185, + 188, 201, 226, 262, 263, - 292, - 301, + 293, 302, 303, 304, - 306, + 305, 307, 308, 309, 310, 311, 312, - 315, - 317, + 313, + 316, + 318, 327, 334, 341, @@ -3434,6 +3434,38 @@ export default { 1 ] }, + "ApplicationRegistrationVariableDTO": { + "id": [ + 3 + ], + "key": [ + 1 + ], + "value": [ + 1 + ], + "description": [ + 1 + ], + "isSecret": [ + 6 + ], + "isRequired": [ + 6 + ], + "isFilled": [ + 6 + ], + "createdAt": [ + 4 + ], + "updatedAt": [ + 4 + ], + "__typename": [ + 1 + ] + }, "BillingTrialPeriod": { "duration": [ 11 @@ -3453,10 +3485,10 @@ export default { 1 ], "type": [ - 173 + 174 ], "status": [ - 174 + 175 ], "issuer": [ 1 @@ -3469,7 +3501,7 @@ export default { "SSOIdentityProviderStatus": {}, "AuthProviders": { "sso": [ - 172 + 173 ], "google": [ 6 @@ -3506,10 +3538,10 @@ export default { 3 ], "authProviders": [ - 175 + 176 ], "authBypassProviders": [ - 176 + 177 ], "logo": [ 1 @@ -3557,7 +3589,7 @@ export default { 1 ], "modelFamily": [ - 181 + 182 ], "modelFamilyLabel": [ 1 @@ -3572,7 +3604,7 @@ export default { 11 ], "nativeCapabilities": [ - 179 + 180 ], "isDeprecated": [ 6 @@ -3608,7 +3640,7 @@ export default { 1 ], "trialPeriods": [ - 171 + 172 ], "__typename": [ 1 @@ -3616,7 +3648,7 @@ export default { }, "Support": { "supportDriver": [ - 184 + 185 ], "supportFrontChatId": [ 1 @@ -3642,7 +3674,7 @@ export default { }, "Captcha": { "provider": [ - 187 + 188 ], "siteKey": [ 1 @@ -3679,7 +3711,7 @@ export default { 169 ], "metadata": [ - 189 + 190 ], "__typename": [ 1 @@ -3704,13 +3736,13 @@ export default { 1 ], "authProviders": [ - 175 + 176 ], "billing": [ - 182 + 183 ], "aiModels": [ - 180 + 181 ], "signInPrefilled": [ 6 @@ -3731,25 +3763,25 @@ export default { 6 ], "support": [ - 183 + 184 ], "isAttachmentPreviewEnabled": [ 6 ], "sentry": [ - 185 - ], - "captcha": [ 186 ], + "captcha": [ + 187 + ], "api": [ - 188 + 189 ], "canManageFeatureFlags": [ 6 ], "publicFeatureFlags": [ - 190 + 191 ], "isMicrosoftMessagingEnabled": [ 6 @@ -3788,7 +3820,7 @@ export default { 6 ], "maintenance": [ - 191 + 192 ], "__typename": [ 1 @@ -3827,7 +3859,7 @@ export default { 1 ], "versionDistribution": [ - 194 + 195 ], "__typename": [ 1 @@ -3872,38 +3904,6 @@ export default { 1 ] }, - "ApplicationRegistrationVariableDTO": { - "id": [ - 3 - ], - "key": [ - 1 - ], - "value": [ - 1 - ], - "description": [ - 1 - ], - "isSecret": [ - 6 - ], - "isRequired": [ - 6 - ], - "isFilled": [ - 6 - ], - "createdAt": [ - 4 - ], - "updatedAt": [ - 4 - ], - "__typename": [ - 1 - ] - }, "Relation": { "type": [ 201 @@ -4095,7 +4095,7 @@ export default { 3 ], "type": [ - 173 + 174 ], "issuer": [ 1 @@ -4104,7 +4104,7 @@ export default { 1 ], "status": [ - 174 + 175 ], "__typename": [ 1 @@ -4123,7 +4123,7 @@ export default { }, "FindAvailableSSOIDP": { "type": [ - 173 + 174 ], "id": [ 3 @@ -4135,7 +4135,7 @@ export default { 1 ], "status": [ - 174 + 175 ], "workspace": [ 218 @@ -4149,7 +4149,7 @@ export default { 3 ], "type": [ - 173 + 174 ], "issuer": [ 1 @@ -4158,7 +4158,7 @@ export default { 1 ], "status": [ - 174 + 175 ], "__typename": [ 1 @@ -4166,7 +4166,7 @@ export default { }, "SSOConnection": { "type": [ - 173 + 174 ], "id": [ 3 @@ -4178,7 +4178,7 @@ export default { 1 ], "status": [ - 174 + 175 ], "__typename": [ 1 @@ -4529,13 +4529,13 @@ export default { }, "UsageAnalytics": { "usageByUser": [ - 193 + 194 ], "usageByOperationType": [ - 193 + 194 ], "usageByModel": [ - 193 + 194 ], "timeSeries": [ 251 @@ -4912,6 +4912,38 @@ export default { 1 ] }, + "Webhook": { + "id": [ + 3 + ], + "targetUrl": [ + 1 + ], + "operations": [ + 1 + ], + "description": [ + 1 + ], + "secret": [ + 1 + ], + "applicationId": [ + 3 + ], + "createdAt": [ + 4 + ], + "updatedAt": [ + 4 + ], + "deletedAt": [ + 4 + ], + "__typename": [ + 1 + ] + }, "ToolIndexEntry": { "name": [ 1 @@ -5051,7 +5083,7 @@ export default { 1 ], "series": [ - 277 + 278 ], "xAxisLabel": [ 1 @@ -5100,7 +5132,7 @@ export default { 1 ], "data": [ - 279 + 280 ], "__typename": [ 1 @@ -5108,7 +5140,7 @@ export default { }, "LineChartData": { "series": [ - 280 + 281 ], "xAxisLabel": [ 1 @@ -5145,7 +5177,7 @@ export default { }, "PieChartData": { "data": [ - 282 + 283 ], "showLegend": [ 6 @@ -5239,13 +5271,13 @@ export default { }, "EventLogQueryResult": { "records": [ - 286 + 287 ], "totalCount": [ 21 ], "pageInfo": [ - 287 + 288 ], "__typename": [ 1 @@ -5309,7 +5341,7 @@ export default { 1 ], "parts": [ - 275 + 276 ], "processedAt": [ 4 @@ -5323,7 +5355,7 @@ export default { }, "AgentChatThread": { "id": [ - 292 + 293 ], "title": [ 1 @@ -5379,7 +5411,7 @@ export default { }, "AiSystemPromptPreview": { "sections": [ - 293 + 294 ], "estimatedTokenCount": [ 21 @@ -5455,10 +5487,10 @@ export default { 3 ], "evaluations": [ - 298 + 299 ], "messages": [ - 290 + 291 ], "createdAt": [ 4 @@ -5475,19 +5507,19 @@ export default { 1 ], "syncStatus": [ - 301 - ], - "syncStage": [ 302 ], - "visibility": [ + "syncStage": [ 303 ], + "visibility": [ + 304 + ], "isContactAutoCreationEnabled": [ 6 ], "contactAutoCreationPolicy": [ - 304 + 305 ], "isSyncEnabled": [ 6 @@ -5523,22 +5555,22 @@ export default { 3 ], "visibility": [ - 306 + 307 ], "handle": [ 1 ], "type": [ - 307 + 308 ], "isContactAutoCreationEnabled": [ 6 ], "contactAutoCreationPolicy": [ - 308 + 309 ], "messageFolderImportPolicy": [ - 309 + 310 ], "excludeNonProfessionalEmails": [ 6 @@ -5547,7 +5579,7 @@ export default { 6 ], "pendingGroupEmailsAction": [ - 310 + 311 ], "isSyncEnabled": [ 6 @@ -5556,10 +5588,10 @@ export default { 4 ], "syncStatus": [ - 311 + 312 ], "syncStage": [ - 312 + 313 ], "syncStageStartedAt": [ 4 @@ -5595,7 +5627,7 @@ export default { "MessageChannelSyncStage": {}, "CreateEmailGroupChannelOutput": { "messageChannel": [ - 305 + 306 ], "forwardingAddress": [ 1 @@ -5624,7 +5656,7 @@ export default { 1 ], "pendingSyncAction": [ - 315 + 316 ], "messageChannelId": [ 3 @@ -5642,7 +5674,7 @@ export default { "MessageFolderPendingSyncAction": {}, "CollectionHash": { "collectionName": [ - 317 + 318 ], "hash": [ 1 @@ -5709,45 +5741,13 @@ export default { }, "MinimalMetadata": { "objectMetadataItems": [ - 318 - ], - "views": [ 319 ], + "views": [ + 320 + ], "collectionHashes": [ - 316 - ], - "__typename": [ - 1 - ] - }, - "Webhook": { - "id": [ - 3 - ], - "targetUrl": [ - 1 - ], - "operations": [ - 1 - ], - "description": [ - 1 - ], - "secret": [ - 1 - ], - "applicationId": [ - 3 - ], - "createdAt": [ - 4 - ], - "updatedAt": [ - 4 - ], - "deletedAt": [ - 4 + 317 ], "__typename": [ 1 @@ -6107,7 +6107,7 @@ export default { 29 ], "getToolIndex": [ - 274 + 275 ], "getToolInputSchema": [ 15, @@ -6118,6 +6118,18 @@ export default { ] } ], + "webhooks": [ + 274 + ], + "webhook": [ + 274, + { + "id": [ + 3, + "UUID!" + ] + } + ], "field": [ 43, { @@ -6158,7 +6170,7 @@ export default { } ], "myMessageFolders": [ - 314, + 315, { "messageChannelId": [ 3 @@ -6166,7 +6178,7 @@ export default { } ], "myMessageChannels": [ - 305, + 306, { "connectedAccountId": [ 3 @@ -6177,33 +6189,21 @@ export default { 269 ], "myCalendarChannels": [ - 300, + 301, { "connectedAccountId": [ 3 ] } ], - "webhooks": [ + "minimalMetadata": [ 321 ], - "webhook": [ - 321, - { - "id": [ - 3, - "UUID!" - ] - } - ], - "minimalMetadata": [ - 320 - ], "chatThreads": [ - 291 + 292 ], "chatThread": [ - 291, + 292, { "id": [ 3, @@ -6212,7 +6212,7 @@ export default { } ], "chatMessages": [ - 290, + 291, { "threadId": [ 3, @@ -6221,7 +6221,7 @@ export default { } ], "chatStreamCatchupChunks": [ - 295, + 296, { "threadId": [ 3, @@ -6230,13 +6230,13 @@ export default { } ], "getAiSystemPromptPreview": [ - 294 + 295 ], "skills": [ - 289 + 290 ], "skill": [ - 289, + 290, { "id": [ 3, @@ -6245,7 +6245,7 @@ export default { } ], "agentTurns": [ - 299, + 300, { "agentId": [ 3, @@ -6293,7 +6293,7 @@ export default { } ], "findApplicationRegistrationByClientId": [ - 197, + 198, { "clientId": [ 1, @@ -6323,7 +6323,7 @@ export default { } ], "findApplicationRegistrationStats": [ - 195, + 196, { "id": [ 1, @@ -6332,7 +6332,7 @@ export default { } ], "findApplicationRegistrationVariables": [ - 199, + 171, { "applicationRegistrationId": [ 1, @@ -6356,7 +6356,7 @@ export default { 75 ], "getPublicWorkspaceDataByDomain": [ - 177, + 178, { "origin": [ 1 @@ -6364,7 +6364,7 @@ export default { } ], "getPublicWorkspaceDataById": [ - 178, + 179, { "id": [ 3, @@ -6390,7 +6390,7 @@ export default { 219 ], "eventLogs": [ - 288, + 289, { "input": [ 326, @@ -6399,7 +6399,7 @@ export default { } ], "pieChartData": [ - 283, + 284, { "input": [ 330, @@ -6408,7 +6408,7 @@ export default { } ], "lineChartData": [ - 281, + 282, { "input": [ 331, @@ -6417,7 +6417,7 @@ export default { } ], "barChartData": [ - 278, + 279, { "input": [ 332, @@ -6506,7 +6506,7 @@ export default { }, "LogicFunctionIdInput": { "id": [ - 292 + 293 ], "__typename": [ 1 @@ -7616,11 +7616,38 @@ export default { ] } ], + "createWebhook": [ + 274, + { + "input": [ + 418, + "CreateWebhookInput!" + ] + } + ], + "updateWebhook": [ + 274, + { + "input": [ + 419, + "UpdateWebhookInput!" + ] + } + ], + "deleteWebhook": [ + 274, + { + "id": [ + 3, + "UUID!" + ] + } + ], "createOneField": [ 43, { "input": [ - 418, + 421, "CreateOneFieldMetadataInput!" ] } @@ -7629,7 +7656,7 @@ export default { 43, { "input": [ - 420, + 423, "UpdateOneFieldMetadataInput!" ] } @@ -7638,7 +7665,7 @@ export default { 43, { "input": [ - 422, + 425, "DeleteOneFieldInput!" ] } @@ -7647,7 +7674,7 @@ export default { 65, { "input": [ - 423, + 426, "CreateViewGroupInput!" ] } @@ -7656,7 +7683,7 @@ export default { 65, { "inputs": [ - 423, + 426, "[CreateViewGroupInput!]!" ] } @@ -7665,7 +7692,7 @@ export default { 65, { "input": [ - 424, + 427, "UpdateViewGroupInput!" ] } @@ -7674,7 +7701,7 @@ export default { 65, { "inputs": [ - 424, + 427, "[UpdateViewGroupInput!]!" ] } @@ -7683,7 +7710,7 @@ export default { 65, { "input": [ - 426, + 429, "DeleteViewGroupInput!" ] } @@ -7692,49 +7719,49 @@ export default { 65, { "input": [ - 427, + 430, "DestroyViewGroupInput!" ] } ], "updateMessageFolder": [ - 314, + 315, { "input": [ - 428, + 431, "UpdateMessageFolderInput!" ] } ], "updateMessageFolders": [ - 314, + 315, { "input": [ - 430, + 433, "UpdateMessageFoldersInput!" ] } ], "updateMessageChannel": [ - 305, + 306, { "input": [ - 431, + 434, "UpdateMessageChannelInput!" ] } ], "createEmailGroupChannel": [ - 313, + 314, { "input": [ - 433, + 436, "CreateEmailGroupChannelInput!" ] } ], "deleteEmailGroupChannel": [ - 305, + 306, { "id": [ 3, @@ -7752,46 +7779,19 @@ export default { } ], "updateCalendarChannel": [ - 300, + 301, { "input": [ - 434, + 437, "UpdateCalendarChannelInput!" ] } ], - "createWebhook": [ - 321, - { - "input": [ - 436, - "CreateWebhookInput!" - ] - } - ], - "updateWebhook": [ - 321, - { - "input": [ - 437, - "UpdateWebhookInput!" - ] - } - ], - "deleteWebhook": [ - 321, - { - "id": [ - 3, - "UUID!" - ] - } - ], "createChatThread": [ - 291 + 292 ], "sendChatMessage": [ - 296, + 297, { "threadId": [ 3, @@ -7827,7 +7827,7 @@ export default { } ], "renameChatThread": [ - 291, + 292, { "id": [ 3, @@ -7840,7 +7840,7 @@ export default { } ], "archiveChatThread": [ - 291, + 292, { "id": [ 3, @@ -7849,7 +7849,7 @@ export default { } ], "unarchiveChatThread": [ - 291, + 292, { "id": [ 3, @@ -7876,7 +7876,7 @@ export default { } ], "createSkill": [ - 289, + 290, { "input": [ 440, @@ -7885,7 +7885,7 @@ export default { } ], "updateSkill": [ - 289, + 290, { "input": [ 441, @@ -7894,7 +7894,7 @@ export default { } ], "deleteSkill": [ - 289, + 290, { "id": [ 3, @@ -7903,7 +7903,7 @@ export default { } ], "activateSkill": [ - 289, + 290, { "id": [ 3, @@ -7912,7 +7912,7 @@ export default { } ], "deactivateSkill": [ - 289, + 290, { "id": [ 3, @@ -7921,7 +7921,7 @@ export default { } ], "evaluateAgentTurn": [ - 298, + 299, { "turnId": [ 3, @@ -7930,7 +7930,7 @@ export default { } ], "runEvaluationInput": [ - 299, + 300, { "agentId": [ 3, @@ -8197,7 +8197,7 @@ export default { } ], "createApplicationRegistration": [ - 196, + 197, { "input": [ 443, @@ -8224,7 +8224,7 @@ export default { } ], "rotateApplicationRegistrationClientSecret": [ - 198, + 199, { "id": [ 1, @@ -8388,18 +8388,6 @@ export default { "checkCustomDomainValidRecords": [ 228 ], - "installApplication": [ - 6, - { - "appRegistrationId": [ - 1, - "String!" - ], - "version": [ - 1 - ] - } - ], "runWorkspaceMigration": [ 6, { @@ -8455,7 +8443,7 @@ export default { } ], "duplicateDashboard": [ - 284, + 285, { "id": [ 3, @@ -8477,7 +8465,7 @@ export default { } ], "sendEmail": [ - 285, + 286, { "input": [ 459, @@ -8486,7 +8474,7 @@ export default { } ], "startChannelSync": [ - 276, + 277, { "connectedAccountId": [ 3, @@ -8613,6 +8601,18 @@ export default { ] } ], + "installApplication": [ + 58, + { + "universalIdentifier": [ + 1, + "String!" + ], + "version": [ + 1 + ] + } + ], "syncMarketplaceCatalog": [ 6 ], @@ -10320,9 +10320,57 @@ export default { 1 ] }, + "CreateWebhookInput": { + "id": [ + 3 + ], + "targetUrl": [ + 1 + ], + "operations": [ + 1 + ], + "description": [ + 1 + ], + "secret": [ + 1 + ], + "__typename": [ + 1 + ] + }, + "UpdateWebhookInput": { + "id": [ + 3 + ], + "update": [ + 420 + ], + "__typename": [ + 1 + ] + }, + "UpdateWebhookInputUpdates": { + "targetUrl": [ + 1 + ], + "operations": [ + 1 + ], + "description": [ + 1 + ], + "secret": [ + 1 + ], + "__typename": [ + 1 + ] + }, "CreateOneFieldMetadataInput": { "field": [ - 419 + 422 ], "__typename": [ 1 @@ -10395,7 +10443,7 @@ export default { 3 ], "update": [ - 421 + 424 ], "__typename": [ 1 @@ -10487,7 +10535,7 @@ export default { 3 ], "update": [ - 425 + 428 ], "__typename": [ 1 @@ -10531,7 +10579,7 @@ export default { 3 ], "update": [ - 429 + 432 ], "__typename": [ 1 @@ -10550,7 +10598,7 @@ export default { 3 ], "update": [ - 429 + 432 ], "__typename": [ 1 @@ -10561,7 +10609,7 @@ export default { 3 ], "update": [ - 432 + 435 ], "__typename": [ 1 @@ -10569,16 +10617,16 @@ export default { }, "UpdateMessageChannelInputUpdates": { "visibility": [ - 306 + 307 ], "isContactAutoCreationEnabled": [ 6 ], "contactAutoCreationPolicy": [ - 308 + 309 ], "messageFolderImportPolicy": [ - 309 + 310 ], "isSyncEnabled": [ 6 @@ -10606,7 +10654,7 @@ export default { 3 ], "update": [ - 435 + 438 ], "__typename": [ 1 @@ -10614,13 +10662,13 @@ export default { }, "UpdateCalendarChannelInputUpdates": { "visibility": [ - 303 + 304 ], "isContactAutoCreationEnabled": [ 6 ], "contactAutoCreationPolicy": [ - 304 + 305 ], "isSyncEnabled": [ 6 @@ -10629,54 +10677,6 @@ export default { 1 ] }, - "CreateWebhookInput": { - "id": [ - 3 - ], - "targetUrl": [ - 1 - ], - "operations": [ - 1 - ], - "description": [ - 1 - ], - "secret": [ - 1 - ], - "__typename": [ - 1 - ] - }, - "UpdateWebhookInput": { - "id": [ - 3 - ], - "update": [ - 438 - ], - "__typename": [ - 1 - ] - }, - "UpdateWebhookInputUpdates": { - "targetUrl": [ - 1 - ], - "operations": [ - 1 - ], - "description": [ - 1 - ], - "secret": [ - 1 - ], - "__typename": [ - 1 - ] - }, "FileAttachmentInput": { "id": [ 3 @@ -10947,7 +10947,7 @@ export default { 454 ], "metadataName": [ - 317 + 318 ], "universalIdentifier": [ 1 @@ -11010,7 +11010,7 @@ export default { 3 ], "status": [ - 174 + 175 ], "__typename": [ 1 @@ -11138,7 +11138,7 @@ export default { } ], "onAgentChatEvent": [ - 297, + 298, { "threadId": [ 3, diff --git a/packages/twenty-docker/twenty/Dockerfile b/packages/twenty-docker/twenty/Dockerfile index e4fdf3260cc..bbf79056b69 100644 --- a/packages/twenty-docker/twenty/Dockerfile +++ b/packages/twenty-docker/twenty/Dockerfile @@ -86,7 +86,11 @@ RUN if [ -d /app/packages/twenty-front/build ]; then \ FROM node:24.15.0-alpine3.23@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f AS twenty-server -RUN apk add --no-cache curl jq postgresql-client +RUN apk add --no-cache \ + 'curl>=8.19.0-r0' \ + 'nghttp2-libs>=1.69.0-r0' \ + 'postgresql18-client>=18.4-r0' \ + jq COPY ./packages/twenty-docker/twenty/entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh diff --git a/packages/twenty-docs/.oxlintrc.json b/packages/twenty-docs/.oxlintrc.json index b127d6100a4..3f43733f58d 100644 --- a/packages/twenty-docs/.oxlintrc.json +++ b/packages/twenty-docs/.oxlintrc.json @@ -4,7 +4,10 @@ "ignorePatterns": ["node_modules", ".next", "storybook-static"], "rules": { "func-style": ["error", "declaration", { "allowArrowFunctions": true }], - "no-console": ["warn", { "allow": ["group", "groupCollapsed", "groupEnd"] }], + "no-console": [ + "warn", + { "allow": ["group", "groupCollapsed", "groupEnd"] } + ], "no-control-regex": "off", "no-debugger": "error", "no-duplicate-imports": "error", @@ -13,22 +16,31 @@ "no-redeclare": "off", "import/no-duplicates": "error", "typescript/no-redeclare": "error", - "typescript/consistent-type-imports": ["error", { - "prefer": "type-imports", - "fixStyle": "inline-type-imports" - }], + "typescript/consistent-type-imports": [ + "error", + { + "prefer": "type-imports", + "fixStyle": "inline-type-imports" + } + ], "typescript/explicit-function-return-type": "off", "typescript/explicit-module-boundary-types": "off", - "typescript/no-empty-object-type": ["error", { - "allowInterfaces": "with-single-extends" - }], + "typescript/no-empty-object-type": [ + "error", + { + "allowInterfaces": "with-single-extends" + } + ], "typescript/no-empty-function": "off", "typescript/no-explicit-any": "off", - "typescript/no-unused-vars": ["warn", { - "vars": "all", - "varsIgnorePattern": "^_", - "args": "after-used", - "argsIgnorePattern": "^_" - }] + "typescript/no-unused-vars": [ + "warn", + { + "vars": "all", + "varsIgnorePattern": "^_", + "args": "after-used", + "argsIgnorePattern": "^_" + } + ] } } diff --git a/packages/twenty-docs/custom.css b/packages/twenty-docs/custom.css index fc66530852f..5ea1d241735 100644 --- a/packages/twenty-docs/custom.css +++ b/packages/twenty-docs/custom.css @@ -12,8 +12,8 @@ opacity: 0.85 !important; } -:is(.dark, [data-theme="dark"]) #topbar-cta-button a, -:is(.dark, [data-theme="dark"]) #topbar-cta-button a span { +:is(.dark, [data-theme='dark']) #topbar-cta-button a, +:is(.dark, [data-theme='dark']) #topbar-cta-button a span { background-color: #ffffff !important; color: #141414 !important; } @@ -35,14 +35,14 @@ * We use :has() to scope the rule to only the sidebar group that contains * the developers/introduction link, so other tabs are unaffected. */ -div:has(> .sidebar-group a[href="/developers/introduction"]), -div:has(> .sidebar-group a[href="/user-guide/introduction"]) { +div:has(> .sidebar-group a[href='/developers/introduction']), +div:has(> .sidebar-group a[href='/user-guide/introduction']) { display: none; } /* Remove the top margin on the group that follows the hidden overview group, so it sits flush at the top of the sidebar without a gap. */ -div:has(> .sidebar-group a[href="/developers/introduction"]) + div, -div:has(> .sidebar-group a[href="/user-guide/introduction"]) + div { +div:has(> .sidebar-group a[href='/developers/introduction']) + div, +div:has(> .sidebar-group a[href='/user-guide/introduction']) + div { margin-top: 0 !important; } diff --git a/packages/twenty-docs/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/developers/extend/apps/layout/views.mdx index 12144fa371e..ea950f30432 100644 --- a/packages/twenty-docs/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/developers/extend/apps/layout/views.mdx @@ -1,7 +1,7 @@ --- title: Views description: Ship pre-configured saved views — column order, filters, groups — for objects in your app. -icon: "list" +icon: 'list' --- A **view** is a saved configuration for how records of an object are displayed: which fields appear, their order, whether they're visible, and any filters or groups applied. Use `defineView()` to ship pre-configured views with your app — typically a default index view for each custom object you create. @@ -38,6 +38,60 @@ export default defineView({ - You can also declare `filters`, `filterGroups`, `groups`, and `fieldGroups` for advanced configurations. - `position` controls ordering when multiple views exist for the same object. +## Filters + +A view can ship with pre-applied filters. Each filter has three coordinates: the **field** being filtered, the **operand** (how to compare), and the **value** (what to compare against). All three must line up — using an operand that doesn't apply to a field type will be rejected at sync time. + +```ts +import { ViewFilterOperand } from 'twenty-shared/types'; + +filters: [ + { + universalIdentifier: '...', + fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['ACTIVE'], + }, +], +``` + +### Supported operands per field type + +| Field type | Supported operands | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `TEXT`, `EMAILS`, `FULL_NAME`, `ADDRESS`, `LINKS`, `PHONES`, `RAW_JSON`, `FILES`, `ACTOR`, `ARRAY` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `ACTOR.source`, `ACTOR.workspaceMemberId` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `SELECT` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `MULTI_SELECT` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RELATION` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `NUMBER` | `IS`, `IS_NOT`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RATING` | `IS`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY`, `CURRENCY.amountMicros` | `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY.currencyCode` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `DATE`, `DATE_TIME` | `IS`, `IS_RELATIVE`, `IS_IN_PAST`, `IS_IN_FUTURE`, `IS_TODAY`, `IS_BEFORE`, `IS_AFTER`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `BOOLEAN` | `IS` | +| `UUID` | `IS` | +| `TS_VECTOR` | `VECTOR_SEARCH` | + +> Field types with similar names can use entirely different operands — `SELECT` and `MULTI_SELECT` being a common case. + +### Value shape per operand + +The `value` field is always a JSON-serializable value, but its expected shape depends on the operand: + +| Operand family | Value shape | Example | +| ----------------------------------------------------- | ------------------------------ | ------------------------ | +| `IS`, `IS_NOT` on `SELECT` | array of option keys (strings) | `['ACTIVE', 'PENDING']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` on `MULTI_SELECT` | array of option keys (strings) | `['TAG_A']` | +| `IS`, `IS_NOT` on `RELATION` | array of record IDs (uuids) | `['c5a1...']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` on text-like fields | string | `'acme'` | +| `IS`, `IS_NOT` on `NUMBER` | string (the value) | `'5'` | +| `IS` on `RATING` / `UUID` | string (the value) | `'5'` | +| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | string (the bound) | `'10'` | +| `IS`, `IS_BEFORE`, `IS_AFTER` on `DATE` / `DATE_TIME` | ISO 8601 string | `'2025-01-01T00:00:00Z'` | +| `IS_EMPTY`, `IS_NOT_EMPTY` | empty string | `''` | +| `IS` on `BOOLEAN` | `'true'` or `'false'` | `'true'` | + ## How views show up in the UI A view by itself isn't reachable from the sidebar. To make it appear there, pair it with a [navigation menu item](/developers/extend/apps/layout/navigation-menu-items) of type `VIEW` that points at the view's `universalIdentifier`. That's the canonical pattern: every custom object typically ships a default view + a sidebar entry that opens it. diff --git a/packages/twenty-docs/l/de/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/de/developers/extend/apps/layout/views.mdx index 5f9fcd23dc5..4660b538aa3 100644 --- a/packages/twenty-docs/l/de/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/de/developers/extend/apps/layout/views.mdx @@ -38,6 +38,60 @@ export default defineView({ * Für erweiterte Konfigurationen können Sie außerdem `filters`, `filterGroups`, `groups` und `fieldGroups` deklarieren. * `position` steuert die Reihenfolge, wenn mehrere Ansichten für dasselbe Objekt existieren. +## Filter + +Eine Ansicht kann mit vorab angewendeten Filtern ausgeliefert werden. Jeder Filter hat drei Koordinaten: das **Feld**, das gefiltert wird, der **Operand** (wie verglichen wird) und der **Wert** (womit verglichen wird). Alle drei müssen übereinstimmen — die Verwendung eines Operanden, der nicht auf einen Feldtyp anwendbar ist, wird bei der Synchronisierung zurückgewiesen. + +```ts +import { ViewFilterOperand } from 'twenty-shared/types'; + +filters: [ + { + universalIdentifier: '...', + fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['ACTIVE'], + }, +], +``` + +### Unterstützte Operanden pro Feldtyp + +| Feldtyp | Unterstützte Operanden | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `TEXT`, `EMAILS`, `FULL_NAME`, `ADDRESS`, `LINKS`, `PHONES`, `RAW_JSON`, `FILES`, `ACTOR`, `ARRAY` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `ACTOR.source`, `ACTOR.workspaceMemberId` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `SELECT` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `MULTI_SELECT` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RELATION` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `NUMBER` | `IS`, `IS_NOT`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RATING` | `IS`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY`, `CURRENCY.amountMicros` | `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY.currencyCode` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `DATE`, `DATE_TIME` | `IS`, `IS_RELATIVE`, `IS_IN_PAST`, `IS_IN_FUTURE`, `IS_TODAY`, `IS_BEFORE`, `IS_AFTER`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `BOOLEAN` | `IS` | +| `UUID` | `IS` | +| `TS_VECTOR` | `VECTOR_SEARCH` | + +> Feldtypen mit ähnlichen Namen können völlig unterschiedliche Operanden verwenden – `SELECT` und `MULTI_SELECT` sind ein häufiges Beispiel. + +### Wertstruktur je Operand + +Das Feld `value` ist immer ein JSON-serialisierbarer Wert, aber die erwartete Struktur hängt vom Operanden ab: + +| Operandenfamilie | Wertstruktur | Beispiel | +| -------------------------------------------------------- | ------------------------------------- | ------------------------ | +| `IS`, `IS_NOT` bei `SELECT` | Array von Optionsschlüsseln (Strings) | `['ACTIVE', 'PENDING']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` bei `MULTI_SELECT` | Array von Optionsschlüsseln (Strings) | `['TAG_A']` | +| `IS`, `IS_NOT` bei `RELATION` | Array von Datensatz-IDs (UUIDs) | `['c5a1...']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` bei textähnlichen Feldern | Zeichenkette | `'acme'` | +| `IS`, `IS_NOT` bei `NUMBER` | String (der Wert) | `'5'` | +| `IS` bei `RATING` / `UUID` | String (der Wert) | `'5'` | +| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | String (der Grenzwert) | `'10'` | +| `IS`, `IS_BEFORE`, `IS_AFTER` bei `DATE` / `DATE_TIME` | ISO-8601-String | `'2025-01-01T00:00:00Z'` | +| `IS_EMPTY`, `IS_NOT_EMPTY` | leerer String | `''` | +| `IS` bei `BOOLEAN` | `'true'` oder `'false'` | `'true'` | + ## Wie Ansichten in der UI angezeigt werden Eine Ansicht für sich ist aus der Seitenleiste nicht erreichbar. Damit sie dort erscheint, verknüpfen Sie sie mit einem [Navigationsmenüeintrag](/l/de/developers/extend/apps/layout/navigation-menu-items) des Typs `VIEW`, der auf den `universalIdentifier` der Ansicht zeigt. Das ist das kanonische Muster: Jedes benutzerdefinierte Objekt liefert typischerweise eine Standardansicht plus einen Eintrag in der Seitenleiste, der sie öffnet. diff --git a/packages/twenty-docs/l/de/user-guide/permissions-access/capabilities/permissions.mdx b/packages/twenty-docs/l/de/user-guide/permissions-access/capabilities/permissions.mdx index 07fc575610f..e0abcfe2284 100644 --- a/packages/twenty-docs/l/de/user-guide/permissions-access/capabilities/permissions.mdx +++ b/packages/twenty-docs/l/de/user-guide/permissions-access/capabilities/permissions.mdx @@ -98,6 +98,22 @@ Klicken Sie auf **+ Regel hinzufügen** und wählen Sie ein Objekt aus, um eine | Opportunities → „Datensätze ansehen“ deaktivieren | Praktikant kann das Opportunities-Objekt überhaupt nicht sehen | | People → „Datensätze bearbeiten“ aktivieren | Praktikant kann People-Datensätze bearbeiten (aber keine anderen Objekte) | +### Berechtigungen auf Zeilenebene + + +Berechtigungen auf Zeilenebene sind ein **Premium-Feature**, das im Tarif **Organization** (Cloud und Self-Hosted) verfügbar ist. + + +Berechtigungen auf Zeilenebene ermöglichen es dir, anhand dynamischer Kriterien einzuschränken, welche einzelnen Datensätze eine Rolle sehen oder bearbeiten kann. Im Gegensatz zu Objektberechtigungen (die für einen gesamten Objekttyp gelten) werden Berechtigungen auf Zeilenebene für jeden Datensatz einzeln ausgewertet. + +**Beispielanwendungsfälle:** + +* Vertriebsmitarbeitende können nur ihre eigenen Opportunities sehen +* Manager:innen können alle Datensätze in ihrer Region sehen +* Support-Mitarbeitende können nur Tickets sehen, die ihnen zugewiesen sind + +Um Berechtigungen auf Zeilenebene zu konfigurieren, öffne eine Rolle, navigiere zum Tab **Objects** und verwende den Abschnitt **Row-Level**, um Filterbedingungen für ein bestimmtes Objekt zu definieren. + ### Feldberechtigungen Innerhalb jeder Regel auf Objektebene können Sie weitergehen und **Berechtigungen auf Feldebene** konfigurieren, um den Zugriff auf bestimmte Felder zu steuern. diff --git a/packages/twenty-docs/l/de/user-guide/permissions-access/how-tos/permissions-faq.mdx b/packages/twenty-docs/l/de/user-guide/permissions-access/how-tos/permissions-faq.mdx index cd104d3c244..53358d1d437 100644 --- a/packages/twenty-docs/l/de/user-guide/permissions-access/how-tos/permissions-faq.mdx +++ b/packages/twenty-docs/l/de/user-guide/permissions-access/how-tos/permissions-faq.mdx @@ -60,7 +60,7 @@ Für Felder: -Berechtigungen auf Zeilenebene werden bis Q1 2026 im Tarif **Organization** verfügbar sein. Damit können Sie den Zugriff auf bestimmte Datensätze anhand von Kriterien einschränken (z. B. nur die eigenen Verkaufschancen sehen). +Berechtigungen auf Zeilenebene sind im Tarif **Organization** verfügbar. Damit können Sie den Zugriff auf bestimmte Datensätze anhand von Kriterien einschränken (z. B. nur die eigenen Verkaufschancen sehen). diff --git a/packages/twenty-docs/l/pt/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/pt/developers/extend/apps/layout/views.mdx index 8cd22e09931..dcbfbea72ba 100644 --- a/packages/twenty-docs/l/pt/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/pt/developers/extend/apps/layout/views.mdx @@ -38,6 +38,60 @@ export default defineView({ * Você também pode declarar `filters`, `filterGroups`, `groups` e `fieldGroups` para configurações avançadas. * `position` controla a ordenação quando existem várias visualizações para o mesmo objeto. +## Filtros + +Uma visualização pode vir com filtros pré-aplicados. Cada filtro tem três coordenadas: o **campo** a ser filtrado, o **operador** (como comparar) e o **valor** (com o que comparar). As três precisam estar alinhadas — usar um operador que não se aplica a um tipo de campo será rejeitado no momento da sincronização. + +```ts +import { ViewFilterOperand } from 'twenty-shared/types'; + +filters: [ + { + universalIdentifier: '...', + fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['ACTIVE'], + }, +], +``` + +### Operadores compatíveis por tipo de campo + +| Tipo de campo | Operandos suportados | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `TEXT`, `EMAILS`, `FULL_NAME`, `ADDRESS`, `LINKS`, `PHONES`, `RAW_JSON`, `FILES`, `ACTOR`, `ARRAY` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `ACTOR.source`, `ACTOR.workspaceMemberId` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `SELECT` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `MULTI_SELECT` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RELATION` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `NUMBER` | `IS`, `IS_NOT`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RATING` | `IS`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY`, `CURRENCY.amountMicros` | `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY.currencyCode` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `DATE`, `DATE_TIME` | `IS`, `IS_RELATIVE`, `IS_IN_PAST`, `IS_IN_FUTURE`, `IS_TODAY`, `IS_BEFORE`, `IS_AFTER`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `BOOLEAN` | `IS` | +| `UUID` | `IS` | +| `TS_VECTOR` | `VECTOR_SEARCH` | + +> Tipos de campos com nomes semelhantes podem usar operandos completamente diferentes — `SELECT` e `MULTI_SELECT` sendo um caso comum. + +### Formato do valor por operando + +O campo `value` é sempre um valor serializável em JSON, mas o seu formato esperado depende do operando: + +| Família do operando | Formato do valor | Exemplo | +| ----------------------------------------------------- | ----------------------------------- | ------------------------ | +| `IS`, `IS_NOT` em `SELECT` | array de chaves de opções (strings) | `['ACTIVE', 'PENDING']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` em `MULTI_SELECT` | array de chaves de opções (strings) | `['TAG_A']` | +| `IS`, `IS_NOT` em `RELATION` | array de IDs de registros (uuids) | `['c5a1...']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` em campos de texto | string | `'acme'` | +| `IS`, `IS_NOT` em `NUMBER` | string (o valor) | `'5'` | +| `IS` em `RATING` / `UUID` | string (o valor) | `'5'` | +| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | string (o limite) | `'10'` | +| `IS`, `IS_BEFORE`, `IS_AFTER` em `DATE` / `DATE_TIME` | string ISO 8601 | `'2025-01-01T00:00:00Z'` | +| `IS_EMPTY`, `IS_NOT_EMPTY` | string vazia | `''` | +| `IS` em `BOOLEAN` | `'true'` ou `'false'` | `'true'` | + ## Como as visualizações aparecem na UI Uma visualização por si só não é acessível a partir da barra lateral. Para fazê-la aparecer lá, associe-a a um [item de menu de navegação](/l/pt/developers/extend/apps/layout/navigation-menu-items) do tipo `VIEW` que aponte para o `universalIdentifier` da visualização. Esse é o padrão canônico: cada objeto personalizado normalmente inclui uma visualização padrão + uma entrada na barra lateral que a abre. diff --git a/packages/twenty-docs/l/pt/user-guide/permissions-access/capabilities/permissions.mdx b/packages/twenty-docs/l/pt/user-guide/permissions-access/capabilities/permissions.mdx index 2dd25b5e7a4..3a764ab9f86 100644 --- a/packages/twenty-docs/l/pt/user-guide/permissions-access/capabilities/permissions.mdx +++ b/packages/twenty-docs/l/pt/user-guide/permissions-access/capabilities/permissions.mdx @@ -98,6 +98,22 @@ Clique em **+ Adicionar regra** e selecione um objeto para criar uma exceção. | Oportunidades → desativar "Ver registros" | O estagiário não consegue ver o objeto Oportunidades. | | Pessoas → ativar "Editar registros" | O estagiário pode editar registros de Pessoas (mas não de outros objetos) | +### Permissões em Nível de Linha + + +As permissões em nível de linha são um **recurso Premium** disponível no plano **Organization** (Cloud e Self-Hosted). + + +As permissões em nível de linha permitem restringir quais registros individuais uma função pode ver ou editar, com base em critérios dinâmicos. Diferente das permissões de objeto (que se aplicam a todo um tipo de objeto), as permissões em nível de linha avaliam cada registro de forma independente. + +**Casos de uso de exemplo:** + +* Representantes de vendas só podem ver suas próprias oportunidades +* Gerentes podem ver todos os registros em sua região +* Agentes de suporte só podem visualizar tickets atribuídos a eles + +Para configurar permissões em nível de linha, abra uma função, navegue até a guia **Objects** e use a seção **Row-Level** para definir condições de filtro para um objeto específico. + ### Permissões de Campos Em cada regra em nível de objeto, você pode ir além e configurar **permissões em nível de campo** para controlar o acesso a campos específicos. diff --git a/packages/twenty-docs/l/pt/user-guide/permissions-access/how-tos/permissions-faq.mdx b/packages/twenty-docs/l/pt/user-guide/permissions-access/how-tos/permissions-faq.mdx index c9c5d666c23..de422884c6b 100644 --- a/packages/twenty-docs/l/pt/user-guide/permissions-access/how-tos/permissions-faq.mdx +++ b/packages/twenty-docs/l/pt/user-guide/permissions-access/how-tos/permissions-faq.mdx @@ -60,7 +60,7 @@ Para campos: -As permissões em nível de linha estarão disponíveis no plano **Organization** até o 1º trimestre de 2026. Isso permite restringir o acesso a registros específicos com base em critérios (por exemplo, ver apenas suas próprias oportunidades). +As permissões em nível de linha estão disponíveis no plano **Organization**. Isso permite restringir o acesso a registros específicos com base em critérios (por exemplo, ver apenas suas próprias oportunidades). diff --git a/packages/twenty-docs/l/ro/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/ro/developers/extend/apps/layout/views.mdx index 1dec9293a37..838fa07a518 100644 --- a/packages/twenty-docs/l/ro/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/ro/developers/extend/apps/layout/views.mdx @@ -38,6 +38,60 @@ export default defineView({ * Puteți declara, de asemenea, `filters`, `filterGroups`, `groups` și `fieldGroups` pentru configurații avansate. * `position` controlează ordonarea atunci când există mai multe vizualizări pentru același obiect. +## Filtre + +O vizualizare poate include filtre aplicate în prealabil. Fiecare filtru are trei coordonate: **câmpul** care este filtrat, **operandul** (cum se compară) și **valoarea** (față de ce se compară). Toate cele trei trebuie să se potrivească — folosirea unui operand care nu se aplică unui tip de câmp va fi respinsă în timpul sincronizării. + +```ts +import { ViewFilterOperand } from 'twenty-shared/types'; + +filters: [ + { + universalIdentifier: '...', + fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['ACTIVE'], + }, +], +``` + +### Operanzi acceptați pentru fiecare tip de câmp + +| Tipul câmpului | Operanzi acceptați | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `TEXT`, `EMAILS`, `FULL_NAME`, `ADDRESS`, `LINKS`, `PHONES`, `RAW_JSON`, `FILES`, `ACTOR`, `ARRAY` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `ACTOR.source`, `ACTOR.workspaceMemberId` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `SELECT` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `MULTI_SELECT` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RELATION` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `NUMBER` | `IS`, `IS_NOT`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RATING` | `IS`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY`, `CURRENCY.amountMicros` | `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY.currencyCode` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `DATE`, `DATE_TIME` | `IS`, `IS_RELATIVE`, `IS_IN_PAST`, `IS_IN_FUTURE`, `IS_TODAY`, `IS_BEFORE`, `IS_AFTER`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `BOOLEAN` | `IS` | +| `UUID` | `IS` | +| `TS_VECTOR` | `VECTOR_SEARCH` | + +> Tipurile de câmp cu nume similare pot folosi operanzi complet diferiți — `SELECT` și `MULTI_SELECT` fiind un caz comun. + +### Formă de valoare per operand + +Câmpul `value` este întotdeauna o valoare serializabilă JSON, dar forma sa așteptată depinde de operand: + +| Familie de operanzi | Formă de valoare | Exemplu | +| ----------------------------------------------------- | ---------------------------------------------- | ------------------------ | +| `IS`, `IS_NOT` pe `SELECT` | array de chei de opțiune (șiruri de caractere) | `['ACTIVE', 'PENDING']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` pe `MULTI_SELECT` | array de chei de opțiune (șiruri de caractere) | `['TAG_A']` | +| `IS`, `IS_NOT` pe `RELATION` | array de ID-uri de înregistrare (uuid-uri) | `['c5a1...']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` pe câmpuri de tip text | șir | `'acme'` | +| `IS`, `IS_NOT` pe `NUMBER` | șir de caractere (valoarea) | `'5'` | +| `IS` pe `RATING` / `UUID` | șir de caractere (valoarea) | `'5'` | +| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | șir de caractere (limita) | `'10'` | +| `IS`, `IS_BEFORE`, `IS_AFTER` pe `DATE` / `DATE_TIME` | șir în format ISO 8601 | `'2025-01-01T00:00:00Z'` | +| `IS_EMPTY`, `IS_NOT_EMPTY` | șir gol | `''` | +| `IS` pe `BOOLEAN` | `'true'` sau `'false'` | `'true'` | + ## Cum apar vizualizările în interfața utilizatorului O vizualizare, de una singură, nu este accesibilă din bara laterală. Pentru a o face să apară acolo, asociați-o cu un [element de meniu de navigare](/l/ro/developers/extend/apps/layout/navigation-menu-items) de tip `VIEW` care indică către `universalIdentifier` al vizualizării. Acesta este modelul canonic: fiecare obiect personalizat livrează, de obicei, o vizualizare implicită + o intrare în bara laterală care o deschide. diff --git a/packages/twenty-docs/l/ro/user-guide/permissions-access/capabilities/permissions.mdx b/packages/twenty-docs/l/ro/user-guide/permissions-access/capabilities/permissions.mdx index 8fb574f92be..43294b46491 100644 --- a/packages/twenty-docs/l/ro/user-guide/permissions-access/capabilities/permissions.mdx +++ b/packages/twenty-docs/l/ro/user-guide/permissions-access/capabilities/permissions.mdx @@ -98,6 +98,22 @@ Faceți clic pe **+ Add rule** și selectați un obiect pentru a crea o excepți | Opportunities → dezactivați "See Records" | Internul nu poate vedea deloc obiectul Opportunities | | People → activați "Edit Records" | Internul poate edita înregistrările People (dar nu și alte obiecte) | +### Permisiuni la Nivel de Rând + + +Permisiunile la nivel de rând sunt o **funcționalitate Premium** disponibilă în planul **Organization** (Cloud și Self-Hosted). + + +Permisiunile la nivel de rând îți permit să restricționezi ce înregistrări individuale poate vedea sau edita un rol, pe baza unor criterii dinamice. Spre deosebire de permisiunile la nivel de obiect (care se aplică unui întreg tip de obiect), permisiunile la nivel de rând evaluează independent fiecare înregistrare. + +**Exemple de cazuri de utilizare:** + +* Reprezentanții de vânzări pot vedea doar oportunitățile proprii +* Managerii pot vedea toate înregistrările din regiunea lor +* Agenții de suport pot vizualiza doar tichetele alocate lor + +Pentru a configura permisiunile la nivel de rând, deschide un rol, navighează la fila **Objects** și folosește secțiunea **Row-Level** pentru a defini condițiile de filtrare pentru un anumit obiect. + ### Permisiuni pentru câmpuri În cadrul fiecărei reguli la nivel de obiect, puteți merge mai departe și configura **permisiuni la nivel de câmp** pentru a controla accesul la câmpuri specifice. diff --git a/packages/twenty-docs/l/ro/user-guide/permissions-access/how-tos/permissions-faq.mdx b/packages/twenty-docs/l/ro/user-guide/permissions-access/how-tos/permissions-faq.mdx index a8fd841d9c2..411bd8e0cfd 100644 --- a/packages/twenty-docs/l/ro/user-guide/permissions-access/how-tos/permissions-faq.mdx +++ b/packages/twenty-docs/l/ro/user-guide/permissions-access/how-tos/permissions-faq.mdx @@ -60,7 +60,7 @@ Pentru câmpuri: -Permisiunile la nivel de rând vor fi disponibile în planul **Organization** până în trimestrul I 2026. Acest lucru vă permite să restricționați accesul la anumite înregistrări pe baza unor criterii (de exemplu, să vedeți doar propriile oportunități). +Permisiunile la nivel de rând sunt disponibile în planul **Organization**. Acest lucru vă permite să restricționați accesul la anumite înregistrări pe baza unor criterii (de exemplu, să vedeți doar propriile oportunități). diff --git a/packages/twenty-docs/l/ru/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/ru/developers/extend/apps/layout/views.mdx index 940419069d2..91ebbf2e311 100644 --- a/packages/twenty-docs/l/ru/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/ru/developers/extend/apps/layout/views.mdx @@ -38,6 +38,60 @@ export default defineView({ * Также вы можете определить `filters`, `filterGroups`, `groups` и `fieldGroups` для более продвинутых конфигураций. * `position` управляет порядком, когда для одного и того же объекта существует несколько представлений. +## Фильтры + +Представление может поставляться с заранее примененными фильтрами. У каждого фильтра есть три координаты: **поле**, по которому выполняется фильтрация, **операнд** (как сравнивать) и **значение** (с чем сравнивать). Все три должны совпадать — использование операнда, который не подходит для типа поля, будет отклонено во время синхронизации. + +```ts +import { ViewFilterOperand } from 'twenty-shared/types'; + +filters: [ + { + universalIdentifier: '...', + fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['ACTIVE'], + }, +], +``` + +### Поддерживаемые операнды по типу поля + +| Тип поля | Поддерживаемые операнды | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `TEXT`, `EMAILS`, `FULL_NAME`, `ADDRESS`, `LINKS`, `PHONES`, `RAW_JSON`, `FILES`, `ACTOR`, `ARRAY` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `ACTOR.source`, `ACTOR.workspaceMemberId` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `SELECT` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `MULTI_SELECT` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RELATION` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `NUMBER` | `IS`, `IS_NOT`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RATING` | `IS`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY`, `CURRENCY.amountMicros` | `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY.currencyCode` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `DATE`, `DATE_TIME` | `IS`, `IS_RELATIVE`, `IS_IN_PAST`, `IS_IN_FUTURE`, `IS_TODAY`, `IS_BEFORE`, `IS_AFTER`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `BOOLEAN` | `IS` | +| `UUID` | `IS` | +| `TS_VECTOR` | `VECTOR_SEARCH` | + +> Типы полей с похожими названиями могут использовать совершенно разные операнды — типичный пример: `SELECT` и `MULTI_SELECT`. + +### Форма значения для каждого операнда + +Поле `value` всегда содержит значение, сериализуемое в JSON, но ожидаемая форма зависит от операнда: + +| Семейство операндов | Форма значения | Пример | +| ------------------------------------------------------ | ------------------------------------- | ------------------------ | +| `IS`, `IS_NOT` для `SELECT` | массив ключей опций (строки) | `['ACTIVE', 'PENDING']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` для `MULTI_SELECT` | массив ключей опций (строки) | `['TAG_A']` | +| `IS`, `IS_NOT` для `RELATION` | массив идентификаторов записей (uuid) | `['c5a1...']` | +| `CONTAINS`, `DOES_NOT_CONTAIN` для текстовых полей | строка | `'acme'` | +| `IS`, `IS_NOT` для `NUMBER` | строка (значение) | `'5'` | +| `IS` для `RATING` / `UUID` | строка (значение) | `'5'` | +| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | строка (граница) | `'10'` | +| `IS`, `IS_BEFORE`, `IS_AFTER` для `DATE` / `DATE_TIME` | строка в формате ISO 8601 | `'2025-01-01T00:00:00Z'` | +| `IS_EMPTY`, `IS_NOT_EMPTY` | пустая строка | `''` | +| `IS` для `BOOLEAN` | `'true'` или `'false'` | `'true'` | + ## Как представления отображаются в интерфейсе Само по себе представление недоступно из боковой панели. Чтобы оно появилось там, свяжите его с [пунктом навигационного меню](/l/ru/developers/extend/apps/layout/navigation-menu-items) типа `VIEW`, который указывает на `universalIdentifier` представления. Это канонический шаблон: каждый пользовательский объект обычно поставляется с представлением по умолчанию и пунктом боковой панели, который его открывает. diff --git a/packages/twenty-docs/l/ru/user-guide/permissions-access/capabilities/permissions.mdx b/packages/twenty-docs/l/ru/user-guide/permissions-access/capabilities/permissions.mdx index 8f07fd5e898..cf411eb3c0c 100644 --- a/packages/twenty-docs/l/ru/user-guide/permissions-access/capabilities/permissions.mdx +++ b/packages/twenty-docs/l/ru/user-guide/permissions-access/capabilities/permissions.mdx @@ -98,6 +98,22 @@ description: Управляйте доступом к объектам, поля | Opportunities → отключить "Просмотр записей" | Стажёр вообще не видит объект Opportunities | | People → включить "Редактирование записей" | Стажёр может редактировать записи People (но не других объектов) | +### Разрешения на уровне строк + + +Разрешения на уровне строк — это **премиальная функция**, доступная в тарифе **Organization** (Cloud и Self-Hosted). + + +Разрешения на уровне строк позволяют ограничить, какие отдельные записи может просматривать или редактировать роль, на основе динамических критериев. В отличие от разрешений на объекты (которые применяются ко всему типу объекта), разрешения на уровне строк оценивают каждую запись независимо. + +**Примеры случаев использования:** + +* Торговые представители могут видеть только свои сделки +* Менеджеры могут видеть все записи в своём регионе +* Сотрудники службы поддержки могут просматривать только назначенные им заявки + +Чтобы настроить разрешения на уровне строк, откройте роль, перейдите на вкладку **Objects** и используйте раздел **Row-Level** для определения условий фильтрации для конкретного объекта. + ### Разрешения на уровне поля В рамках каждого правила на уровне объекта вы можете дополнительно настроить **разрешения на уровне поля**, чтобы управлять доступом к конкретным полям. diff --git a/packages/twenty-docs/l/ru/user-guide/permissions-access/how-tos/permissions-faq.mdx b/packages/twenty-docs/l/ru/user-guide/permissions-access/how-tos/permissions-faq.mdx index 0075adc5acb..88ee4cd22b7 100644 --- a/packages/twenty-docs/l/ru/user-guide/permissions-access/how-tos/permissions-faq.mdx +++ b/packages/twenty-docs/l/ru/user-guide/permissions-access/how-tos/permissions-faq.mdx @@ -60,7 +60,7 @@ description: Часто задаваемые вопросы о ролях и р -Разрешения на уровне строки будут доступны в тарифе **Organization** к первому кварталу 2026 года. Это позволит ограничивать доступ к отдельным записям на основе критериев (например, видеть только собственные сделки). +Разрешения на уровне строки доступны в тарифе **Organization**. Это позволит ограничивать доступ к отдельным записям на основе критериев (например, видеть только собственные сделки). diff --git a/packages/twenty-docs/l/tr/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/tr/developers/extend/apps/layout/views.mdx index 7f25c446e4b..bd761747834 100644 --- a/packages/twenty-docs/l/tr/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/tr/developers/extend/apps/layout/views.mdx @@ -38,6 +38,60 @@ export default defineView({ * Daha gelişmiş yapılandırmalar için `filters`, `filterGroups`, `groups` ve `fieldGroups` da tanımlayabilirsiniz. * `position`, aynı nesne için birden fazla görünüm olduğunda sıralamayı kontrol eder. +## Filtreler + +Bir görünüm, önceden uygulanmış filtrelerle gelebilir. Her filtrenin üç koordinatı vardır: filtrelenen **alan**, **işleç** (nasıl karşılaştırılacağı) ve **değer** (neyle karşılaştırılacağı). Üçünün de hizalı olması gerekir — bir alan türüne uygulanmayan bir işlecin kullanılması, senkronizasyon sırasında reddedilir. + +```ts +import { ViewFilterOperand } from 'twenty-shared/types'; + +filters: [ + { + universalIdentifier: '...', + fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['ACTIVE'], + }, +], +``` + +### Alan türüne göre desteklenen işleçler + +| Alan tipi | Desteklenen işleçler | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `TEXT`, `EMAILS`, `FULL_NAME`, `ADDRESS`, `LINKS`, `PHONES`, `RAW_JSON`, `FILES`, `ACTOR`, `ARRAY` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `ACTOR.source`, `ACTOR.workspaceMemberId` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `SELECT` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `MULTI_SELECT` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RELATION` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `NUMBER` | `IS`, `IS_NOT`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RATING` | `IS`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY`, `CURRENCY.amountMicros` | `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY.currencyCode` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `DATE`, `DATE_TIME` | `IS`, `IS_RELATIVE`, `IS_IN_PAST`, `IS_IN_FUTURE`, `IS_TODAY`, `IS_BEFORE`, `IS_AFTER`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `BOOLEAN` | `IS` | +| `UUID` | `IS` | +| `TS_VECTOR` | `VECTOR_SEARCH` | + +> Benzer adlara sahip alan türleri tamamen farklı işleçler kullanabilir — buna `SELECT` ve `MULTI_SELECT` yaygın bir örnektir. + +### Her işleç için değer biçimi + +`value` alanı her zaman JSON olarak serileştirilebilir bir değerdir, ancak beklenen biçim işlece bağlıdır: + +| İşleç ailesi | Değer biçimi | Örnek | +| ------------------------------------------------------------- | ------------------------------------- | ------------------------ | +| `SELECT` üzerinde `IS`, `IS_NOT` | seçenek anahtarları dizisi (metinler) | `['ACTIVE', 'PENDING']` | +| `MULTI_SELECT` üzerinde `CONTAINS`, `DOES_NOT_CONTAIN` | seçenek anahtarları dizisi (metinler) | `['TAG_A']` | +| `RELATION` üzerinde `IS`, `IS_NOT` | kayıt kimlikleri dizisi (uuid'ler) | `['c5a1...']` | +| metin benzeri alanlar üzerinde `CONTAINS`, `DOES_NOT_CONTAIN` | metin | `'acme'` | +| `NUMBER` üzerinde `IS`, `IS_NOT` | metin (değer) | `'5'` | +| `RATING` / `UUID` üzerinde `IS` | metin (değer) | `'5'` | +| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | metin (sınır) | `'10'` | +| `DATE` / `DATE_TIME` üzerinde `IS`, `IS_BEFORE`, `IS_AFTER` | ISO 8601 dizesi | `'2025-01-01T00:00:00Z'` | +| `IS_EMPTY`, `IS_NOT_EMPTY` | boş dize | `''` | +| `BOOLEAN` üzerinde `IS` | `'true'` veya `'false'` | `'true'` | + ## Görünümlerin kullanıcı arayüzünde görüntülenme şekli Tek başına bir görünüm, kenar çubuğundan erişilebilir değildir. Orada görünmesini sağlamak için, türü `VIEW` olan ve görünümün `universalIdentifier` değerini işaret eden bir [navigasyon menüsü öğesi](/l/tr/developers/extend/apps/layout/navigation-menu-items) ile eşleştirin. Bu, standart örüntüdür: her özel nesne genellikle varsayılan bir görünüm ve bunu açan bir kenar çubuğu girdisiyle birlikte sunulur. diff --git a/packages/twenty-docs/l/tr/user-guide/permissions-access/capabilities/permissions.mdx b/packages/twenty-docs/l/tr/user-guide/permissions-access/capabilities/permissions.mdx index 8fe8575b103..df50aaefdf4 100644 --- a/packages/twenty-docs/l/tr/user-guide/permissions-access/capabilities/permissions.mdx +++ b/packages/twenty-docs/l/tr/user-guide/permissions-access/capabilities/permissions.mdx @@ -98,6 +98,22 @@ Varsayılanları ayarladıktan sonra, belirli nesneler için varsayılanları ge | Fırsatlar → "Kayıtları Gör"ü devre dışı bırakın | Stajyer Fırsatlar nesnesini hiç göremez | | Kişiler → "Kayıtları Düzenle"yi etkinleştirin | Stajyer Kişiler kayıtlarını düzenleyebilir (ancak diğer nesneleri düzenleyemez) | +### Satır Seviyesi İzinleri + + +Satır seviyesi izinler, **Organization** planında (Bulut ve Kendi Sunucunuzda) sunulan bir **Premium özellik**tir. + + +Satır seviyesi izinler, bir rolün dinamik ölçütlere göre hangi tekil kayıtları görebileceğini veya düzenleyebileceğini kısıtlamanıza olanak tanır. Nesne izinlerinden (tüm bir nesne türüne uygulanan) farklı olarak, satır seviyesi izinler her kaydı bağımsız olarak değerlendirir. + +**Örnek Kullanım Durumları:** + +* Satış temsilcileri yalnızca kendi fırsatlarını görebilir +* Yöneticiler kendi bölgelerindeki tüm kayıtları görebilir +* Destek temsilcileri yalnızca kendilerine atanmış talepleri görüntüleyebilir + +Satır seviyesi izinleri yapılandırmak için bir rol açın, **Nesneler** sekmesine gidin ve belirli bir nesne için filtre koşullarını tanımlamak üzere **Satır Seviyesi** bölümünü kullanın. + ### Alan İzinleri Her nesne düzeyi kuralın içinde, belirli alanlara erişimi denetlemek için **alan düzeyi izinlerini** yapılandırabilirsiniz. diff --git a/packages/twenty-docs/l/tr/user-guide/permissions-access/how-tos/permissions-faq.mdx b/packages/twenty-docs/l/tr/user-guide/permissions-access/how-tos/permissions-faq.mdx index aed0c48211b..d7062fa07b1 100644 --- a/packages/twenty-docs/l/tr/user-guide/permissions-access/how-tos/permissions-faq.mdx +++ b/packages/twenty-docs/l/tr/user-guide/permissions-access/how-tos/permissions-faq.mdx @@ -60,7 +60,7 @@ For fields: -Row-level permissions will be available on the **Organization** plan by Q1 2026. This allows you to restrict access to specific records based on criteria (e.g., only see your own opportunities). +Satır düzeyi izinler **Organization** planında mevcuttur. This allows you to restrict access to specific records based on criteria (e.g., only see your own opportunities). diff --git a/packages/twenty-docs/l/zh/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/l/zh/developers/extend/apps/layout/views.mdx index 8351380ee9f..dbd637ccbff 100644 --- a/packages/twenty-docs/l/zh/developers/extend/apps/layout/views.mdx +++ b/packages/twenty-docs/l/zh/developers/extend/apps/layout/views.mdx @@ -38,6 +38,60 @@ export default defineView({ * 你还可以声明 `filters`、`filterGroups`、`groups` 和 `fieldGroups` 以进行更高级的配置。 * 当同一对象存在多个视图时,`position` 控制其排序。 +## 过滤器 + +视图可以附带预先应用的过滤器。 每个过滤器有三个坐标:被筛选的**字段**、**运算符**(如何比较)以及**值**(与之比较的内容)。 这三者必须全部对齐——在同步时,使用不适用于字段类型的运算符将会被拒绝。 + +```ts +import { ViewFilterOperand } from 'twenty-shared/types'; + +filters: [ + { + universalIdentifier: '...', + fieldMetadataUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER, + operand: ViewFilterOperand.IS, + value: ['ACTIVE'], + }, +], +``` + +### 各字段类型支持的运算符 + +| 字段类型 | 受支持的运算符 | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `TEXT`, `EMAILS`, `FULL_NAME`, `ADDRESS`, `LINKS`, `PHONES`, `RAW_JSON`, `FILES`, `ACTOR`, `ARRAY` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `ACTOR.source`, `ACTOR.workspaceMemberId` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `SELECT` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `MULTI_SELECT` | `CONTAINS`, `DOES_NOT_CONTAIN`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RELATION` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `NUMBER` | `IS`, `IS_NOT`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `RATING` | `IS`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY`, `CURRENCY.amountMicros` | `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `CURRENCY.currencyCode` | `IS`, `IS_NOT`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `DATE`, `DATE_TIME` | `IS`, `IS_RELATIVE`, `IS_IN_PAST`, `IS_IN_FUTURE`, `IS_TODAY`, `IS_BEFORE`, `IS_AFTER`, `IS_EMPTY`, `IS_NOT_EMPTY` | +| `BOOLEAN` | `IS` | +| `UUID` | `IS` | +| `TS_VECTOR` | `VECTOR_SEARCH` | + +> 名称相似的字段类型可以使用完全不同的运算符 —— `SELECT` 和 `MULTI_SELECT` 就是常见情况。 + +### 每个运算符对应的值结构 + +`value` 字段始终是可序列化为 JSON 的值,但其期望的结构取决于所使用的运算符: + +| 运算符类别 | 值结构 | 示例 | +| ----------------------------------------------------- | -------------------- | ------------------------ | +| `SELECT` 上的 `IS`, `IS_NOT` | 选项键(字符串)数组 | `['ACTIVE', 'PENDING']` | +| `MULTI_SELECT` 上的 `CONTAINS`, `DOES_NOT_CONTAIN` | 选项键(字符串)数组 | `['TAG_A']` | +| `RELATION` 上的 `IS`, `IS_NOT` | 记录 ID(uuid)数组 | `['c5a1...']` | +| 文本类字段上的 `CONTAINS`, `DOES_NOT_CONTAIN` | 字符串 | `'acme'` | +| `NUMBER` 上的 `IS`, `IS_NOT` | 字符串(该值) | `'5'` | +| `RATING` / `UUID` 上的 `IS` | 字符串(该值) | `'5'` | +| `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL` | 字符串(边界值) | `'10'` | +| `DATE` / `DATE_TIME` 上的 `IS`, `IS_BEFORE`, `IS_AFTER` | ISO 8601 字符串 | `'2025-01-01T00:00:00Z'` | +| `IS_EMPTY`, `IS_NOT_EMPTY` | 空字符串 | `''` | +| `BOOLEAN` 上的 `IS` | `'true'` 或 `'false'` | `'true'` | + ## 视图在 UI 中如何显示 单独一个视图无法从侧边栏访问。 要让它显示在侧边栏中,请将其与类型为 `VIEW`、指向该视图 `universalIdentifier` 的[导航菜单项](/l/zh/developers/extend/apps/layout/navigation-menu-items)配对。 这是规范用法:每个自定义对象通常都会提供一个默认视图,以及一个在侧边栏中打开该视图的条目。 diff --git a/packages/twenty-docs/l/zh/user-guide/permissions-access/capabilities/permissions.mdx b/packages/twenty-docs/l/zh/user-guide/permissions-access/capabilities/permissions.mdx index 064e68fa0ab..6037406f8f8 100644 --- a/packages/twenty-docs/l/zh/user-guide/permissions-access/capabilities/permissions.mdx +++ b/packages/twenty-docs/l/zh/user-guide/permissions-access/capabilities/permissions.mdx @@ -98,6 +98,22 @@ Twenty 的权限系统允许您控制对三个主要区域的访问: | Opportunities → 禁用 "查看记录" | 实习生完全无法查看 Opportunities 对象 | | People → 启用 "编辑记录" | 实习生可以编辑 People 记录(但不能编辑其他对象) | +### 行级权限 + + +行级权限是 **Premium 功能**,可在 **Organization** 方案(云端和自托管)中使用。 + + +行级权限可以根据动态条件,限制某个角色可以查看或编辑的具体记录。 与对象权限(适用于整个对象类型)不同,行级权限会独立评估每条记录。 + +**示例用例:** + +* 销售代表只能查看自己的销售机会 +* 经理可以查看其所在区域的所有记录 +* 客服人员只能查看分配给他们的工单 + +要配置行级权限,打开某个角色,导航到 **Objects** 选项卡,并使用 **Row-Level** 区域为特定对象定义筛选条件。 + ### 字段权限 在每个对象级规则内,您还可以进一步配置**字段级权限**,以控制对特定字段的访问。 diff --git a/packages/twenty-docs/l/zh/user-guide/permissions-access/how-tos/permissions-faq.mdx b/packages/twenty-docs/l/zh/user-guide/permissions-access/how-tos/permissions-faq.mdx index 10e0108ce49..af7ebd9b87b 100644 --- a/packages/twenty-docs/l/zh/user-guide/permissions-access/how-tos/permissions-faq.mdx +++ b/packages/twenty-docs/l/zh/user-guide/permissions-access/how-tos/permissions-faq.mdx @@ -60,7 +60,7 @@ Twenty 默认提供 **管理员** 和 **成员** 角色。 您可以根据团队 -行级权限将在 2026 年第一季度于 **Organization** 方案中提供。 这将允许您根据条件限制对特定记录的访问(例如,仅查看自己的商机)。 +行级权限在 **Organization** 方案中可用。 这将允许您根据条件限制对特定记录的访问(例如,仅查看自己的商机)。 diff --git a/packages/twenty-docs/navigation/base-structure.json b/packages/twenty-docs/navigation/base-structure.json index c4e5a502dc9..41f3ac2ebdc 100644 --- a/packages/twenty-docs/navigation/base-structure.json +++ b/packages/twenty-docs/navigation/base-structure.json @@ -36,9 +36,7 @@ { "key": "userGuideOverview", "label": "Overview", - "pages": [ - "user-guide/introduction" - ] + "pages": ["user-guide/introduction"] }, { "key": "dataModel", @@ -220,9 +218,7 @@ { "key": "aiHowTos", "label": "How-Tos", - "pages": [ - "user-guide/ai/how-tos/ai-faq" - ] + "pages": ["user-guide/ai/how-tos/ai-faq"] } ] }, @@ -309,9 +305,7 @@ { "key": "permissionsAccessHowTos", "label": "How-Tos", - "pages": [ - "user-guide/permissions-access/how-tos/permissions-faq" - ] + "pages": ["user-guide/permissions-access/how-tos/permissions-faq"] } ] }, @@ -332,9 +326,7 @@ { "key": "billingHowTos", "label": "How-Tos", - "pages": [ - "user-guide/billing/how-tos/billing-faq" - ] + "pages": ["user-guide/billing/how-tos/billing-faq"] } ] }, @@ -359,9 +351,7 @@ { "key": "settingsHowTos", "label": "How-Tos", - "pages": [ - "user-guide/settings/how-tos/settings-faq" - ] + "pages": ["user-guide/settings/how-tos/settings-faq"] } ] } @@ -374,9 +364,7 @@ { "key": "developersOverview", "label": "Overview", - "pages": [ - "developers/introduction" - ] + "pages": ["developers/introduction"] }, { "key": "apps", diff --git a/packages/twenty-docs/navigation/navigation-schema.json b/packages/twenty-docs/navigation/navigation-schema.json index 5ac78aba201..51ca009050c 100644 --- a/packages/twenty-docs/navigation/navigation-schema.json +++ b/packages/twenty-docs/navigation/navigation-schema.json @@ -209,9 +209,7 @@ { "key": "aiHowTos", "label": "How-Tos", - "pages": [ - "user-guide/ai/how-tos/ai-faq" - ] + "pages": ["user-guide/ai/how-tos/ai-faq"] } ] }, @@ -290,9 +288,7 @@ { "key": "permissionsAccessHowTos", "label": "How-Tos", - "pages": [ - "user-guide/permissions-access/how-tos/permissions-faq" - ] + "pages": ["user-guide/permissions-access/how-tos/permissions-faq"] } ] }, @@ -313,9 +309,7 @@ { "key": "billingHowTos", "label": "How-Tos", - "pages": [ - "user-guide/billing/how-tos/billing-faq" - ] + "pages": ["user-guide/billing/how-tos/billing-faq"] } ] }, @@ -340,9 +334,7 @@ { "key": "settingsHowTos", "label": "How-Tos", - "pages": [ - "user-guide/settings/how-tos/settings-faq" - ] + "pages": ["user-guide/settings/how-tos/settings-faq"] } ] } @@ -355,9 +347,7 @@ { "key": "developersGroup", "label": "Developers", - "pages": [ - "developers/introduction" - ] + "pages": ["developers/introduction"] }, { "key": "extend", @@ -433,9 +423,7 @@ { "key": "feedback", "label": "Feedback", - "pages": [ - "twenty-ui/progress-bar" - ] + "pages": ["twenty-ui/progress-bar"] }, { "key": "input", diff --git a/packages/twenty-docs/project.json b/packages/twenty-docs/project.json index 0de792e25f9..b3d7b63bfec 100644 --- a/packages/twenty-docs/project.json +++ b/packages/twenty-docs/project.json @@ -21,11 +21,17 @@ }, "lint": {}, "fmt": { + "executor": "nx:run-commands", + "cache": true, "options": { - "files": "." + "cwd": "{projectRoot}", + "command": "prettier . --check --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata" }, "configurations": { - "fix": {} + "ci": {}, + "fix": { + "command": "prettier . --write --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata" + } } } } diff --git a/packages/twenty-docs/scripts/generate-docs-json.ts b/packages/twenty-docs/scripts/generate-docs-json.ts index 2f9034f3e0d..60be8acda2a 100644 --- a/packages/twenty-docs/scripts/generate-docs-json.ts +++ b/packages/twenty-docs/scripts/generate-docs-json.ts @@ -2,8 +2,8 @@ import fs from 'fs'; import path from 'path'; import { - DEFAULT_LANGUAGE, - SUPPORTED_LANGUAGES, + DEFAULT_LANGUAGE, + SUPPORTED_LANGUAGES, } from '../navigation/supported-languages'; type BasePage = string | BaseGroup; @@ -71,15 +71,11 @@ const baseStructure: BaseStructure = JSON.parse( const docsConfig = JSON.parse(fs.readFileSync(docsPath, 'utf8')); -const collectTranslations = ( - file: TranslationFile | null, -): TranslationMaps => { +const collectTranslations = (file: TranslationFile | null): TranslationMaps => { const tabLabels = new Map(); const groupLabels = new Map(); - const collectGroups = ( - groups?: Record, - ) => { + const collectGroups = (groups?: Record) => { if (!groups) { return; } @@ -99,17 +95,15 @@ const collectTranslations = ( }; const loadTranslationFile = (language: string): TranslationFile | null => { - const translationPath = path.join( - localesRoot, - language, - 'navigation.json', - ); + const translationPath = path.join(localesRoot, language, 'navigation.json'); if (!fs.existsSync(translationPath)) { return null; } - return JSON.parse(fs.readFileSync(translationPath, 'utf8')) as TranslationFile; + return JSON.parse( + fs.readFileSync(translationPath, 'utf8'), + ) as TranslationFile; }; const buildLanguageEntry = (language: string): GeneratedLanguage => { @@ -161,9 +155,8 @@ const hasLocaleContent = (language: string): boolean => { return fs.existsSync(localeDir); }; -const languages = SUPPORTED_LANGUAGES.filter(hasLocaleContent).map( - buildLanguageEntry, -); +const languages = + SUPPORTED_LANGUAGES.filter(hasLocaleContent).map(buildLanguageEntry); if (!docsConfig.navigation) { docsConfig.navigation = {}; diff --git a/packages/twenty-docs/scripts/generate-halftone-illustrations.mjs b/packages/twenty-docs/scripts/generate-halftone-illustrations.mjs index 6eab95f40e4..8f944cd9135 100644 --- a/packages/twenty-docs/scripts/generate-halftone-illustrations.mjs +++ b/packages/twenty-docs/scripts/generate-halftone-illustrations.mjs @@ -53,75 +53,81 @@ async function main() { const base64 = imageBytes.toString('base64'); const mime = sourcePath.endsWith('.png') ? 'image/png' : 'image/jpeg'; - const resultBase64 = await page.evaluate(async ({ b64, m }) => { - const LINE_SPACING = 8; - const MIN_LINE_WIDTH = 0.5; - const MAX_LINE_WIDTH = 6; - const COLOR = { r: 74, g: 56, b: 245 }; // #4A38F5 - const TRANSPARENT_BG = true; + const resultBase64 = await page.evaluate( + async ({ b64, m }) => { + const LINE_SPACING = 8; + const MIN_LINE_WIDTH = 0.5; + const MAX_LINE_WIDTH = 6; + const COLOR = { r: 74, g: 56, b: 245 }; // #4A38F5 + const TRANSPARENT_BG = true; - const img = new Image(); - await new Promise((res, rej) => { - img.onload = res; - img.onerror = rej; - img.src = `data:${m};base64,${b64}`; - }); + const img = new Image(); + await new Promise((res, rej) => { + img.onload = res; + img.onerror = rej; + img.src = `data:${m};base64,${b64}`; + }); - const { width, height } = img; - const scale = 2; - const outW = width * scale; - const outH = height * scale; + const { width, height } = img; + const scale = 2; + const outW = width * scale; + const outH = height * scale; - // Read source pixels - const tmp = document.createElement('canvas'); - tmp.width = width; - tmp.height = height; - const tmpCtx = tmp.getContext('2d'); - tmpCtx.drawImage(img, 0, 0); - const px = tmpCtx.getImageData(0, 0, width, height).data; + // Read source pixels + const tmp = document.createElement('canvas'); + tmp.width = width; + tmp.height = height; + const tmpCtx = tmp.getContext('2d'); + tmpCtx.drawImage(img, 0, 0); + const px = tmpCtx.getImageData(0, 0, width, height).data; - // Output canvas - const out = document.createElement('canvas'); - out.width = outW; - out.height = outH; - const ctx = out.getContext('2d'); - // Transparent background — adapts to both light and dark mode + // Output canvas + const out = document.createElement('canvas'); + out.width = outW; + out.height = outH; + const ctx = out.getContext('2d'); + // Transparent background — adapts to both light and dark mode - const sp = LINE_SPACING * scale; + const sp = LINE_SPACING * scale; - for (let y = sp / 2; y < outH; y += sp) { - const srcY = Math.min(Math.floor(y / scale), height - 1); - let x = 0; + for (let y = sp / 2; y < outH; y += sp) { + const srcY = Math.min(Math.floor(y / scale), height - 1); + let x = 0; - while (x < outW) { - const srcX = Math.min(Math.floor(x / scale), width - 1); - const i = (srcY * width + srcX) * 4; - const lum = (0.299 * px[i] + 0.587 * px[i+1] + 0.114 * px[i+2]) / 255; - const a = px[i+3] / 255; - const dark = (1 - lum) * a; + while (x < outW) { + const srcX = Math.min(Math.floor(x / scale), width - 1); + const i = (srcY * width + srcX) * 4; + const lum = + (0.299 * px[i] + 0.587 * px[i + 1] + 0.114 * px[i + 2]) / 255; + const a = px[i + 3] / 255; + const dark = (1 - lum) * a; - if (dark > 0.2) { - const w = (MIN_LINE_WIDTH + (MAX_LINE_WIDTH - MIN_LINE_WIDTH) * dark) * scale; - const dashLen = sp * (0.3 + 0.7 * dark); - const gap = sp * (0.1 + 0.4 * (1 - dark)); - const alpha = 0.3 + 0.7 * dark; + if (dark > 0.2) { + const w = + (MIN_LINE_WIDTH + (MAX_LINE_WIDTH - MIN_LINE_WIDTH) * dark) * + scale; + const dashLen = sp * (0.3 + 0.7 * dark); + const gap = sp * (0.1 + 0.4 * (1 - dark)); + const alpha = 0.3 + 0.7 * dark; - ctx.strokeStyle = `rgba(${COLOR.r},${COLOR.g},${COLOR.b},${alpha})`; - ctx.lineWidth = w; - ctx.lineCap = 'round'; - ctx.beginPath(); - ctx.moveTo(x, y); - ctx.lineTo(Math.min(x + dashLen, outW), y); - ctx.stroke(); - x += dashLen + gap; - } else { - x += sp * 0.5; + ctx.strokeStyle = `rgba(${COLOR.r},${COLOR.g},${COLOR.b},${alpha})`; + ctx.lineWidth = w; + ctx.lineCap = 'round'; + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(Math.min(x + dashLen, outW), y); + ctx.stroke(); + x += dashLen + gap; + } else { + x += sp * 0.5; + } } } - } - return out.toDataURL('image/png').split(',')[1]; - }, { b64: base64, m: mime }); + return out.toDataURL('image/png').split(',')[1]; + }, + { b64: base64, m: mime }, + ); const outputPath = join(OUTPUT_DIR, `${illust.name}.png`); writeFileSync(outputPath, Buffer.from(resultBase64, 'base64')); diff --git a/packages/twenty-docs/scripts/generate-navigation-template.ts b/packages/twenty-docs/scripts/generate-navigation-template.ts index 7d42daf321f..7ba12d4bcac 100644 --- a/packages/twenty-docs/scripts/generate-navigation-template.ts +++ b/packages/twenty-docs/scripts/generate-navigation-template.ts @@ -46,9 +46,7 @@ const baseStructure: BaseStructure = JSON.parse( fs.readFileSync(baseStructurePath, 'utf8'), ); -const buildGroupMap = ( - groups: BaseGroup[], -): Record => +const buildGroupMap = (groups: BaseGroup[]): Record => groups.reduce>((acc, group) => { const nestedGroups = group.pages.filter( (page): page is BaseGroup => typeof page !== 'string', @@ -78,4 +76,3 @@ const template: TemplateFile = { }; fs.writeFileSync(templatePath, `${JSON.stringify(template, null, 2)}\n`); - diff --git a/packages/twenty-docs/user-guide/permissions-access/capabilities/permissions.mdx b/packages/twenty-docs/user-guide/permissions-access/capabilities/permissions.mdx index 3cb8840592e..6390b0b2cce 100644 --- a/packages/twenty-docs/user-guide/permissions-access/capabilities/permissions.mdx +++ b/packages/twenty-docs/user-guide/permissions-access/capabilities/permissions.mdx @@ -95,6 +95,21 @@ Click **+ Add rule** and select an object to create an exception. | Opportunities → disable "See Records" | Intern cannot see the Opportunities object at all | | People → enable "Edit Records" | Intern can edit People records (but not other objects) | +### Row-Level Permissions + + +Row-level permissions are a **Premium feature** available on the **Organization** plan (Cloud and Self-Hosted). + + +Row-level permissions let you restrict which individual records a role can see or edit, based on dynamic criteria. Unlike object permissions (which apply to an entire object type), row-level permissions evaluate each record independently. + +**Example use cases:** +- Sales reps can only see their own opportunities +- Managers can see all records in their region +- Support agents can only view tickets assigned to them + +To configure row-level permissions, open a role, navigate to the **Objects** tab, and use the **Row-Level** section to define filter conditions for a specific object. + ### Field Permissions Within each object-level rule, you can go further and configure **field-level permissions** to control access to specific fields. diff --git a/packages/twenty-docs/user-guide/permissions-access/how-tos/permissions-faq.mdx b/packages/twenty-docs/user-guide/permissions-access/how-tos/permissions-faq.mdx index 0d5510508a9..18ed1856df7 100644 --- a/packages/twenty-docs/user-guide/permissions-access/how-tos/permissions-faq.mdx +++ b/packages/twenty-docs/user-guide/permissions-access/how-tos/permissions-faq.mdx @@ -60,7 +60,7 @@ For fields: -Row-level permissions will be available on the **Organization** plan by Q1 2026. This allows you to restrict access to specific records based on criteria (e.g., only see your own opportunities). +Row-level permissions are available on the **Organization** plan. This allows you to restrict access to specific records based on criteria (e.g., only see your own opportunities). diff --git a/packages/twenty-front-component-renderer/project.json b/packages/twenty-front-component-renderer/project.json index b246b8cc7d8..9485478cc19 100644 --- a/packages/twenty-front-component-renderer/project.json +++ b/packages/twenty-front-component-renderer/project.json @@ -66,10 +66,16 @@ ], "inputs": [ "{projectRoot}/scripts/front-component-stories/**/*", - "{projectRoot}/src/__stories__/example-sources/*", + "{projectRoot}/src/__stories__/html-tag/**/*", + "{projectRoot}/src/__stories__/host-api/**/*", + "{projectRoot}/src/__stories__/showcase/**/*", + "{projectRoot}/src/__stories__/shared/front-components/**/*", "{workspaceRoot}/packages/twenty-sdk/src/cli/utilities/build/**/*" ], - "outputs": ["{projectRoot}/src/__stories__/example-sources-built/*"], + "outputs": [ + "{projectRoot}/src/__stories__/example-sources-built/*", + "{projectRoot}/src/__stories__/example-sources-built-preact/*" + ], "options": { "command": "tsx {projectRoot}/scripts/front-component-stories/build-source-examples.ts" } diff --git a/packages/twenty-front-component-renderer/scripts/front-component-stories/build-source-examples.ts b/packages/twenty-front-component-renderer/scripts/front-component-stories/build-source-examples.ts index 541820b0034..553cca65046 100644 --- a/packages/twenty-front-component-renderer/scripts/front-component-stories/build-source-examples.ts +++ b/packages/twenty-front-component-renderer/scripts/front-component-stories/build-source-examples.ts @@ -6,10 +6,7 @@ import { fileURLToPath } from 'node:url'; import { getFrontComponentBuildPlugins } from 'twenty-sdk/front-component-renderer/build'; const dirname = path.dirname(fileURLToPath(import.meta.url)); -const exampleSourcesDir = path.resolve( - dirname, - '../../src/__stories__/example-sources', -); +const storiesDir = path.resolve(dirname, '../../src/__stories__'); const exampleSourcesBuiltDir = path.resolve( dirname, '../../src/__stories__/example-sources-built', @@ -19,6 +16,8 @@ const exampleSourcesBuiltPreactDir = path.resolve( '../../src/__stories__/example-sources-built-preact', ); +const SOURCE_SCAN_ROOTS = ['html-tag', 'host-api', 'showcase']; + const rootNodeModules = path.resolve(dirname, '../../../../node_modules'); const twentyUiIndividualIndex = path.resolve( @@ -71,44 +70,69 @@ const storyAlias = { ...twentySharedAliases, }; -const STORY_COMPONENTS = [ - 'static.front-component', - 'interactive.front-component', - 'lifecycle.front-component', - 'chakra-example.front-component', - 'tailwind-example.front-component', - 'emotion-example.front-component', - 'styled-components-example.front-component', - 'shadcn-example.front-component', - 'mui-example.front-component', - 'twenty-ui-example.front-component', - 'sdk-context-example.front-component', - 'form-events.front-component', - 'keyboard-events.front-component', - 'host-api-calls.front-component', - 'caret-preservation.front-component', - 'file-input.front-component', -]; +const ENTRY_POINT_PATTERN = /\.front-component\.tsx$/; + +const findEntryPointFiles = (directory: string): string[] => { + const result: string[] = []; + + if (!fs.existsSync(directory)) { + return result; + } + + for (const dirent of fs.readdirSync(directory, { withFileTypes: true })) { + const absolutePath = path.join(directory, dirent.name); + + if (dirent.isDirectory()) { + if (dirent.name === 'shared') { + continue; + } + + result.push(...findEntryPointFiles(absolutePath)); + continue; + } + + if (!dirent.isFile()) { + continue; + } + + if (ENTRY_POINT_PATTERN.test(dirent.name)) { + result.push(absolutePath); + } + } + + return result; +}; const resolveEntryPoints = (): Record => { + const files = SOURCE_SCAN_ROOTS.flatMap((root) => + findEntryPointFiles(path.join(storiesDir, root)), + ); + const entryPoints: Record = {}; - for (const name of STORY_COMPONENTS) { - const filePath = path.join(exampleSourcesDir, `${name}.tsx`); + for (const filePath of files) { + const basename = path.basename(filePath).replace(/\.tsx$/, ''); - if (!fs.existsSync(filePath)) { + if (entryPoints[basename] !== undefined) { throw new Error( - `Story component source file not found: ${filePath}\n` + - `Ensure the file exists in ${exampleSourcesDir} and the name in STORY_COMPONENTS is correct.`, + `Duplicate front-component basename "${basename}" found at ${filePath} and ${entryPoints[basename]}`, ); } - entryPoints[name] = filePath; + entryPoints[basename] = filePath; + } + + if (Object.keys(entryPoints).length === 0) { + throw new Error( + `No front-component source files found under ${storiesDir} (scanned: ${SOURCE_SCAN_ROOTS.join(', ')})`, + ); } return entryPoints; }; +const STORY_COMPONENTS = Object.keys(resolveEntryPoints()); + type BundleSizeEntry = { name: string; reactBytes: number; diff --git a/packages/twenty-front-component-renderer/scripts/remote-dom/generators/remote-elements.generator.ts b/packages/twenty-front-component-renderer/scripts/remote-dom/generators/remote-elements.generator.ts index 8f8cb7ab380..18d5d9b632b 100644 --- a/packages/twenty-front-component-renderer/scripts/remote-dom/generators/remote-elements.generator.ts +++ b/packages/twenty-front-component-renderer/scripts/remote-dom/generators/remote-elements.generator.ts @@ -107,6 +107,66 @@ const generateCommonEventsType = ( }, ], }); + + sourceFile.addVariableStatement({ + declarationKind: VariableDeclarationKind.Const, + declarations: [ + { + name: 'createSerializedEventConfig', + initializer: (writer) => { + writer.write( + '(eventType: string): RemoteElementEventListenerDefinition => (', + ); + writer.block(() => { + writer.writeLine( + 'dispatchEvent(this: Element, eventData: SerializedEventData) {', + ); + writer.indent(() => { + writer.writeLine('applySerializedEventTargetProperties('); + writer.indent(() => { + writer.writeLine('this as unknown as Record,'); + writer.writeLine('eventData,'); + }); + writer.writeLine(');'); + writer.blankLine(); + writer.writeLine('return new CustomEvent(eventType, {'); + writer.indent(() => { + writer.writeLine('detail: eventData,'); + }); + writer.writeLine('}) as RemoteEvent;'); + }); + writer.writeLine('},'); + }); + writer.write(')'); + }, + }, + ], + }); + + sourceFile.addVariableStatement({ + declarationKind: VariableDeclarationKind.Const, + declarations: [ + { + name: 'HTML_COMMON_EVENTS_CONFIG', + initializer: (writer) => { + writer.writeLine('Object.fromEntries('); + writer.indent(() => { + writer.writeLine( + `${TYPE_NAMES.COMMON_EVENTS_ARRAY}.map((eventType) => [`, + ); + writer.indent(() => { + writer.writeLine('eventType,'); + writer.writeLine('createSerializedEventConfig(eventType),'); + }); + writer.writeLine(']),'); + }); + writer.write( + `) as RemoteElementEventListenersDefinition<${TYPE_NAMES.COMMON_EVENTS}>`, + ); + }, + }, + ], + }); }; const generateCommonPropertiesConfig = ( @@ -246,17 +306,19 @@ const generateElementDefinition = ( } } if (hasEvents) { - const formattedCustomEvents = customEvents - .map((event) => `'${event}'`) - .join(', '); + writer.write('events: '); + writer.block(() => { + if (hasCommonHtmlEvents) { + writer.writeLine('...HTML_COMMON_EVENTS_CONFIG,'); + } - writer.write( - hasCommonHtmlEvents && customEvents.length > 0 - ? `events: [...${TYPE_NAMES.COMMON_EVENTS_ARRAY}, ${formattedCustomEvents}],` - : hasCommonHtmlEvents - ? `events: [...${TYPE_NAMES.COMMON_EVENTS_ARRAY}],` - : `events: [${formattedCustomEvents}],`, - ); + for (const event of customEvents) { + writer.writeLine( + `'${event}': createSerializedEventConfig('${event}'),`, + ); + } + }); + writer.write(','); writer.newLine(); } }); @@ -332,12 +394,17 @@ export const generateRemoteElements = ( INTERNAL_ELEMENT_CLASSES.ROOT, INTERNAL_ELEMENT_CLASSES.FRAGMENT, { name: 'RemoteEvent', isTypeOnly: true }, + { name: 'RemoteElementEventListenerDefinition', isTypeOnly: true }, + { name: 'RemoteElementEventListenersDefinition', isTypeOnly: true }, ], }); sourceFile.addImportDeclaration({ moduleSpecifier: '@/constants/SerializedEventData', - namedImports: [{ name: 'SerializedEventData', isTypeOnly: true }], + namedImports: [ + 'applySerializedEventTargetProperties', + { name: 'SerializedEventData', isTypeOnly: true }, + ], }); const commonPropertyNames = new Set(Object.keys(commonProperties)); diff --git a/packages/twenty-front-component-renderer/src/__stories__/BundleSizes.stories.tsx b/packages/twenty-front-component-renderer/src/__stories__/BundleSizes.stories.tsx index c58e6a5568b..25ad5b3d13b 100644 --- a/packages/twenty-front-component-renderer/src/__stories__/BundleSizes.stories.tsx +++ b/packages/twenty-front-component-renderer/src/__stories__/BundleSizes.stories.tsx @@ -1,6 +1,6 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; -import bundleSizes from './example-sources-built/bundle-sizes.json'; +import bundleSizes from '@/__stories__/example-sources-built/bundle-sizes.json'; type BundleSizeEntry = { name: string; diff --git a/packages/twenty-front-component-renderer/src/__stories__/EventForwarding.stories.tsx b/packages/twenty-front-component-renderer/src/__stories__/EventForwarding.stories.tsx deleted file mode 100644 index a4ba39fcd54..00000000000 --- a/packages/twenty-front-component-renderer/src/__stories__/EventForwarding.stories.tsx +++ /dev/null @@ -1,517 +0,0 @@ -import { type Meta, type StoryObj } from '@storybook/react-vite'; -import { expect, fn, userEvent, waitFor, within } from 'storybook/test'; - -import { FrontComponentRenderer } from '../host/components/FrontComponentRenderer'; - -import { getBuiltStoryComponentPathForRender } from './utils/getBuiltStoryComponentPathForRender'; - -const errorHandler = fn(); - -const createHostApiMocks = () => ({ - navigate: fn().mockResolvedValue(undefined), - enqueueSnackbar: fn().mockResolvedValue(undefined), - openSidePanelPage: fn().mockResolvedValue(undefined), - closeSidePanel: fn().mockResolvedValue(undefined), - unmountFrontComponent: fn().mockResolvedValue(undefined), - updateProgress: fn().mockResolvedValue(undefined), - requestAccessTokenRefresh: fn().mockResolvedValue('refreshed-token'), - openCommandConfirmationModal: fn().mockResolvedValue(undefined), -}); - -const meta: Meta = { - title: 'FrontComponent/EventForwarding', - component: FrontComponentRenderer, - parameters: { - layout: 'centered', - }, - args: { - onError: errorHandler, - applicationAccessToken: 'fake-token', - executionContext: { - frontComponentId: 'storybook-test', - userId: null, - recordId: null, - selectedRecordIds: [], - }, - colorScheme: 'light', - frontComponentHostCommunicationApi: createHostApiMocks(), - }, - beforeEach: () => { - errorHandler.mockClear(); - }, -}; - -export default meta; -type Story = StoryObj; - -const MOUNT_TIMEOUT = 30000; -const INTERACTION_TIMEOUT = 5000; -const HOST_API_TIMEOUT = 10000; - -const createComponentStory = ( - name: string, - options?: { play?: Story['play'] }, -): Story => ({ - args: { - componentUrl: getBuiltStoryComponentPathForRender( - `${name}.front-component`, - ), - }, - ...(options?.play ? { play: options.play } : {}), -}); - -const createHostApiStory = (play: Story['play']): Story => ({ - ...createComponentStory('host-api-calls'), - args: { - ...createComponentStory('host-api-calls').args, - frontComponentHostCommunicationApi: createHostApiMocks(), - }, - play, -}); - -export const FormTextInput: Story = createComponentStory('form-events', { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByTestId( - 'form-events-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const textInput = await canvas.findByTestId('text-input'); - await userEvent.type(textInput, 'hello'); - - expect( - await canvas.findByText('hello', {}, { timeout: INTERACTION_TIMEOUT }), - ).toBeVisible(); - }, -}); - -export const FormCheckbox: Story = createComponentStory('form-events', { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByTestId( - 'form-events-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const checkbox = await canvas.findByTestId('checkbox-input'); - await userEvent.click(checkbox); - - expect( - await canvas.findByText('true', {}, { timeout: INTERACTION_TIMEOUT }), - ).toBeVisible(); - }, -}); - -export const FormFocusAndBlur: Story = createComponentStory('form-events', { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByTestId( - 'form-events-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const textInput = await canvas.findByTestId('text-input'); - await userEvent.click(textInput); - - expect( - await canvas.findByText('focused', {}, { timeout: INTERACTION_TIMEOUT }), - ).toBeVisible(); - - await userEvent.click(await canvas.findByTestId('form-events-component')); - - expect( - await canvas.findByText('blurred', {}, { timeout: INTERACTION_TIMEOUT }), - ).toBeVisible(); - }, -}); - -export const FormSubmission: Story = createComponentStory('form-events', { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByTestId( - 'form-events-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const textInput = await canvas.findByTestId('text-input'); - await userEvent.type(textInput, 'hello'); - - const checkbox = await canvas.findByTestId('checkbox-input'); - await userEvent.click(checkbox); - - const submitButton = await canvas.findByTestId('submit-button'); - await userEvent.click(submitButton); - - expect( - await canvas.findByText( - '{"text":"hello","checkbox":true}', - {}, - { timeout: INTERACTION_TIMEOUT }, - ), - ).toBeVisible(); - }, -}); - -export const KeyboardBasicInput: Story = createComponentStory( - 'keyboard-events', - { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByTestId( - 'keyboard-events-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const input = await canvas.findByTestId('keyboard-input'); - await userEvent.click(input); - - await userEvent.keyboard('a'); - - expect( - await canvas.findByText('a', {}, { timeout: INTERACTION_TIMEOUT }), - ).toBeVisible(); - - expect( - await canvas.findByText('KeyA', {}, { timeout: INTERACTION_TIMEOUT }), - ).toBeVisible(); - - expect( - await canvas.findByText( - /^[1-9]\d*$/, - {}, - { timeout: INTERACTION_TIMEOUT }, - ), - ).toBeVisible(); - }, - }, -); - -export const KeyboardModifiers: Story = createComponentStory( - 'keyboard-events', - { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByTestId( - 'keyboard-events-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const input = await canvas.findByTestId('keyboard-input'); - await userEvent.click(input); - - await userEvent.keyboard('{Shift>}b{/Shift}'); - - expect( - await canvas.findByText('shift', {}, { timeout: INTERACTION_TIMEOUT }), - ).toBeVisible(); - }, - }, -); - -export const HostApiNavigate: Story = createHostApiStory( - async ({ canvasElement, args }) => { - const canvas = within(canvasElement); - const api = args.frontComponentHostCommunicationApi!; - - await canvas.findByTestId( - 'host-api-calls-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const navigateBtn = await canvas.findByTestId('btn-navigate'); - await userEvent.click(navigateBtn); - - await waitFor( - () => { - expect(api.navigate).toHaveBeenCalled(); - }, - { timeout: HOST_API_TIMEOUT }, - ); - - expect( - await canvas.findByText( - 'navigate:success', - {}, - { timeout: INTERACTION_TIMEOUT }, - ), - ).toBeVisible(); - }, -); - -export const HostApiSnackbar: Story = createHostApiStory( - async ({ canvasElement, args }) => { - const canvas = within(canvasElement); - const api = args.frontComponentHostCommunicationApi!; - - await canvas.findByTestId( - 'host-api-calls-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const snackbarBtn = await canvas.findByTestId('btn-snackbar'); - await userEvent.click(snackbarBtn); - - await waitFor( - () => { - expect(api.enqueueSnackbar).toHaveBeenCalledWith({ - message: 'Test notification', - variant: 'success', - }); - }, - { timeout: HOST_API_TIMEOUT }, - ); - - expect( - await canvas.findByText( - 'snackbar:success', - {}, - { timeout: INTERACTION_TIMEOUT }, - ), - ).toBeVisible(); - }, -); - -export const HostApiProgress: Story = createHostApiStory( - async ({ canvasElement, args }) => { - const canvas = within(canvasElement); - const api = args.frontComponentHostCommunicationApi!; - - await canvas.findByTestId( - 'host-api-calls-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const progressBtn = await canvas.findByTestId('btn-progress'); - await userEvent.click(progressBtn); - - await waitFor( - () => { - expect(api.updateProgress).toHaveBeenCalledWith(50); - }, - { timeout: HOST_API_TIMEOUT }, - ); - - expect( - await canvas.findByText( - 'progress:success', - {}, - { timeout: INTERACTION_TIMEOUT }, - ), - ).toBeVisible(); - }, -); - -const TYPING_TIMEOUT = 10000; - -const expectCaretAt = async ( - element: HTMLInputElement | HTMLTextAreaElement, - position: number, -): Promise => { - await waitFor( - () => { - expect(element.selectionStart).toBe(position); - expect(element.selectionEnd).toBe(position); - }, - { timeout: TYPING_TIMEOUT }, - ); -}; - -export const InputCaretPreservedMidString: Story = createComponentStory( - 'caret-preservation', - { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByTestId( - 'caret-preservation-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const input = (await canvas.findByTestId( - 'caret-text-input', - )) as HTMLInputElement; - - await waitFor( - () => { - expect(input.value).toBe('Hello world'); - }, - { timeout: INTERACTION_TIMEOUT }, - ); - - input.focus(); - input.setSelectionRange(4, 4); - - await userEvent.keyboard('X'); - - await waitFor( - () => { - expect(input.value).toBe('HellXo world'); - expect(canvas.getByTestId('caret-text-value').textContent).toBe( - 'HellXo world', - ); - }, - { timeout: TYPING_TIMEOUT }, - ); - - await expectCaretAt(input, 5); - }, - }, -); - -export const TextareaCaretPreservedMidString: Story = createComponentStory( - 'caret-preservation', - { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByTestId( - 'caret-preservation-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const textarea = (await canvas.findByTestId( - 'caret-textarea-input', - )) as HTMLTextAreaElement; - - await waitFor( - () => { - expect(textarea.value).toBe('Hello world'); - }, - { timeout: INTERACTION_TIMEOUT }, - ); - - textarea.focus(); - textarea.setSelectionRange(4, 4); - - await userEvent.keyboard('X'); - - await waitFor( - () => { - expect(textarea.value).toBe('HellXo world'); - expect(canvas.getByTestId('caret-textarea-value').textContent).toBe( - 'HellXo world', - ); - }, - { timeout: TYPING_TIMEOUT }, - ); - - await expectCaretAt(textarea, 5); - }, - }, -); - -export const FileInputSingle: Story = createComponentStory('file-input', { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByTestId( - 'file-input-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const input = (await canvas.findByTestId( - 'single-file-input', - )) as HTMLInputElement; - - const file = new File(['hello world'], 'hello.txt', { - type: 'text/plain', - lastModified: 1700000000000, - }); - - await userEvent.upload(input, file); - - await waitFor( - () => { - expect(canvas.getByTestId('single-file-count').textContent).toBe('1'); - expect(canvas.getByTestId('single-file-name').textContent).toContain( - 'hello.txt', - ); - expect(canvas.getByTestId('single-file-name').textContent).toContain( - 'text/plain', - ); - }, - { timeout: INTERACTION_TIMEOUT }, - ); - }, -}); - -export const FileInputMultiple: Story = createComponentStory('file-input', { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByTestId( - 'file-input-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const input = (await canvas.findByTestId( - 'multi-file-input', - )) as HTMLInputElement; - - const first = new File(['a'], 'one.png', { type: 'image/png' }); - const second = new File(['bb'], 'two.png', { type: 'image/png' }); - - await userEvent.upload(input, [first, second]); - - await waitFor( - () => { - expect(canvas.getByTestId('multi-file-count').textContent).toBe('2'); - const list = canvas.getByTestId('multi-file-list'); - expect(list.textContent).toContain('one.png'); - expect(list.textContent).toContain('two.png'); - }, - { timeout: INTERACTION_TIMEOUT }, - ); - }, -}); - -export const HostApiClosePanel: Story = createHostApiStory( - async ({ canvasElement, args }) => { - const canvas = within(canvasElement); - const api = args.frontComponentHostCommunicationApi!; - - await canvas.findByTestId( - 'host-api-calls-component', - {}, - { timeout: MOUNT_TIMEOUT }, - ); - - const closePanelBtn = await canvas.findByTestId('btn-close-panel'); - await userEvent.click(closePanelBtn); - - await waitFor( - () => { - expect(api.closeSidePanel).toHaveBeenCalled(); - }, - { timeout: HOST_API_TIMEOUT }, - ); - - expect( - await canvas.findByText( - 'closePanel:success', - {}, - { timeout: INTERACTION_TIMEOUT }, - ), - ).toBeVisible(); - }, -); diff --git a/packages/twenty-front-component-renderer/src/__stories__/FrontComponentRenderer.stories.tsx b/packages/twenty-front-component-renderer/src/__stories__/FrontComponentRenderer.stories.tsx index 985bf714441..2a6e0a0fa99 100644 --- a/packages/twenty-front-component-renderer/src/__stories__/FrontComponentRenderer.stories.tsx +++ b/packages/twenty-front-component-renderer/src/__stories__/FrontComponentRenderer.stories.tsx @@ -1,9 +1,8 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; import { expect, fn, userEvent, waitFor, within } from 'storybook/test'; -import { FrontComponentRenderer } from '../host/components/FrontComponentRenderer'; - -import { getBuiltStoryComponentPathForRender } from './utils/getBuiltStoryComponentPathForRender'; +import { FrontComponentRenderer } from '@/host/components/FrontComponentRenderer'; +import { getBuiltStoryComponentPathForRender } from '@/__stories__/utils/getBuiltStoryComponentPathForRender'; const errorHandler = fn(); diff --git a/packages/twenty-front-component-renderer/src/__stories__/UILibraries.stories.tsx b/packages/twenty-front-component-renderer/src/__stories__/UILibraries.stories.tsx index 3c029696174..9829d956608 100644 --- a/packages/twenty-front-component-renderer/src/__stories__/UILibraries.stories.tsx +++ b/packages/twenty-front-component-renderer/src/__stories__/UILibraries.stories.tsx @@ -1,9 +1,8 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; import { expect, fn, userEvent, within } from 'storybook/test'; -import { FrontComponentRenderer } from '../host/components/FrontComponentRenderer'; - -import { getBuiltStoryComponentPathForRender } from './utils/getBuiltStoryComponentPathForRender'; +import { FrontComponentRenderer } from '@/host/components/FrontComponentRenderer'; +import { getBuiltStoryComponentPathForRender } from '@/__stories__/utils/getBuiltStoryComponentPathForRender'; const errorHandler = fn(); diff --git a/packages/twenty-front-component-renderer/src/__stories__/example-sources/caret-preservation.front-component.tsx b/packages/twenty-front-component-renderer/src/__stories__/example-sources/caret-preservation.front-component.tsx deleted file mode 100644 index 9a9ae6b910f..00000000000 --- a/packages/twenty-front-component-renderer/src/__stories__/example-sources/caret-preservation.front-component.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { defineFrontComponent } from 'twenty-sdk/define'; -import { type ChangeEvent, useState } from 'react'; - -const CARD_STYLE = { - padding: 24, - backgroundColor: '#eff6ff', - border: '2px solid #3b82f6', - borderRadius: 12, - fontFamily: 'system-ui, sans-serif', - display: 'flex', - flexDirection: 'column' as const, - gap: 16, - maxWidth: 400, -}; - -const HEADING_STYLE = { - color: '#1e3a8a', - fontWeight: 700, - fontSize: 18, - margin: 0, -}; - -const LABEL_STYLE = { - fontSize: 13, - fontWeight: 600, - color: '#374151', -}; - -const HINT_STYLE = { - fontSize: 13, - color: '#6b7280', - fontFamily: 'monospace', -}; - -const INPUT_STYLE = { - padding: '8px 12px', - border: '1px solid #d1d5db', - borderRadius: 6, - fontSize: 14, - fontFamily: 'monospace', -}; - -const INITIAL_VALUE = 'Hello world'; - -const CaretPreservationComponent = () => { - const [text, setText] = useState(INITIAL_VALUE); - const [textareaText, setTextareaText] = useState(INITIAL_VALUE); - - return ( -
-

Caret Preservation

- -
- - ) => { - const detail = (event as unknown as { detail: { value?: string } }) - .detail; - setText(detail?.value ?? ''); - }} - style={INPUT_STYLE} - /> - - {text} - -
- -
- -