diff --git a/.gitignore b/.gitignore index 21f31cf3a9..b89a0bdce4 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,4 @@ TRANSLATION_QA_REPORT.md .playwright-cli/ output/playwright/ screenshots/ +!**/screenshots/ diff --git a/packages/twenty-apps/internal/twenty-last-contact/.gitignore b/packages/twenty-apps/internal/twenty-last-contact/.gitignore new file mode 100644 index 0000000000..6cd0610624 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/.gitignore @@ -0,0 +1,38 @@ +# 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 +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# typescript +*.tsbuildinfo +*.d.ts diff --git a/packages/twenty-apps/internal/twenty-last-contact/.nvmrc b/packages/twenty-apps/internal/twenty-last-contact/.nvmrc new file mode 100644 index 0000000000..341cb50613 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/.nvmrc @@ -0,0 +1 @@ +24.5.0 diff --git a/packages/twenty-apps/internal/twenty-last-contact/.oxlintrc.json b/packages/twenty-apps/internal/twenty-last-contact/.oxlintrc.json new file mode 100644 index 0000000000..369b9cfd76 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/.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-last-contact/.yarnrc.yml b/packages/twenty-apps/internal/twenty-last-contact/.yarnrc.yml new file mode 100644 index 0000000000..3186f3f079 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/packages/twenty-apps/internal/twenty-last-contact/AGENTS.md b/packages/twenty-apps/internal/twenty-last-contact/AGENTS.md new file mode 100644 index 0000000000..fc6e7f8a51 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/AGENTS.md @@ -0,0 +1,67 @@ +## Base documentation + +- Getting started: + - https://docs.twenty.com/developers/extend/apps/getting-started/quick-start.md + - https://docs.twenty.com/developers/extend/apps/getting-started/concepts.md + - https://docs.twenty.com/developers/extend/apps/getting-started/project-structure.md + - https://docs.twenty.com/developers/extend/apps/getting-started/local-server.md + - https://docs.twenty.com/developers/extend/apps/getting-started/scaffolding.md + - https://docs.twenty.com/developers/extend/apps/getting-started/troubleshooting.md +- Config: + - https://docs.twenty.com/developers/extend/apps/config/overview.md + - https://docs.twenty.com/developers/extend/apps/config/application.md + - https://docs.twenty.com/developers/extend/apps/config/roles.md + - https://docs.twenty.com/developers/extend/apps/config/install-hooks.md + - https://docs.twenty.com/developers/extend/apps/config/public-assets.md +- Data: + - https://docs.twenty.com/developers/extend/apps/data/overview.md + - https://docs.twenty.com/developers/extend/apps/data/objects.md + - https://docs.twenty.com/developers/extend/apps/data/extending-objects.md + - https://docs.twenty.com/developers/extend/apps/data/relations.md +- Logic: + - https://docs.twenty.com/developers/extend/apps/logic/overview.md + - https://docs.twenty.com/developers/extend/apps/logic/logic-functions.md + - https://docs.twenty.com/developers/extend/apps/logic/skills-and-agents.md + - https://docs.twenty.com/developers/extend/apps/logic/connections.md +- Layout: + - https://docs.twenty.com/developers/extend/apps/layout/overview.md + - https://docs.twenty.com/developers/extend/apps/layout/views.md + - https://docs.twenty.com/developers/extend/apps/layout/navigation-menu-items.md + - https://docs.twenty.com/developers/extend/apps/layout/page-layouts.md + - https://docs.twenty.com/developers/extend/apps/layout/front-components.md + - https://docs.twenty.com/developers/extend/apps/layout/command-menu-items.md +- Operations: + - https://docs.twenty.com/developers/extend/apps/operations/overview.md + - https://docs.twenty.com/developers/extend/apps/operations/cli.md + - https://docs.twenty.com/developers/extend/apps/operations/testing.md + - https://docs.twenty.com/developers/extend/apps/operations/publishing.md +- Rich app example: https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples/postcard + +## UUID requirement + +- All generated UUIDs must be valid UUID v4. + +## Common Pitfalls + +- Creating an object without an index view associated. Unless this is a technical object, user will need to visualize it. +- Creating a view without a navigationMenuItem associated. This will make the view available on the left sidebar. +- Creating a front-end component that has a scroll instead of being responsive to its fixed widget height and width, unless it is specifically meant to be used in a canvas tab. + +## Best practice + +It's highly recommended to create new app entities using `yarn twenty dev:add`. These are the options: + +| Entity type | Command | Generated file | +| -------------------- | ---------------------------------------- | ------------------------------------- | +| Object | `yarn twenty dev:add object` | `src/objects/.ts` | +| Field | `yarn twenty dev:add field` | `src/fields/.ts` | +| Logic function | `yarn twenty dev:add logicFunction` | `src/logic-functions/.ts` | +| Front component | `yarn twenty dev:add frontComponent` | `src/front-components/.tsx` | +| Role | `yarn twenty dev:add role` | `src/roles/.ts` | +| Skill | `yarn twenty dev:add skill` | `src/skills/.ts` | +| Agent | `yarn twenty dev:add agent` | `src/agents/.ts` | +| View | `yarn twenty dev:add view` | `src/views/.ts` | +| Navigation menu item | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/.ts` | +| Page layout | `yarn twenty dev:add pageLayout` | `src/page-layouts/.ts` | + +This helps automatically generate required IDs etc. diff --git a/packages/twenty-apps/internal/twenty-last-contact/CLAUDE.md b/packages/twenty-apps/internal/twenty-last-contact/CLAUDE.md new file mode 100644 index 0000000000..fc6e7f8a51 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/CLAUDE.md @@ -0,0 +1,67 @@ +## Base documentation + +- Getting started: + - https://docs.twenty.com/developers/extend/apps/getting-started/quick-start.md + - https://docs.twenty.com/developers/extend/apps/getting-started/concepts.md + - https://docs.twenty.com/developers/extend/apps/getting-started/project-structure.md + - https://docs.twenty.com/developers/extend/apps/getting-started/local-server.md + - https://docs.twenty.com/developers/extend/apps/getting-started/scaffolding.md + - https://docs.twenty.com/developers/extend/apps/getting-started/troubleshooting.md +- Config: + - https://docs.twenty.com/developers/extend/apps/config/overview.md + - https://docs.twenty.com/developers/extend/apps/config/application.md + - https://docs.twenty.com/developers/extend/apps/config/roles.md + - https://docs.twenty.com/developers/extend/apps/config/install-hooks.md + - https://docs.twenty.com/developers/extend/apps/config/public-assets.md +- Data: + - https://docs.twenty.com/developers/extend/apps/data/overview.md + - https://docs.twenty.com/developers/extend/apps/data/objects.md + - https://docs.twenty.com/developers/extend/apps/data/extending-objects.md + - https://docs.twenty.com/developers/extend/apps/data/relations.md +- Logic: + - https://docs.twenty.com/developers/extend/apps/logic/overview.md + - https://docs.twenty.com/developers/extend/apps/logic/logic-functions.md + - https://docs.twenty.com/developers/extend/apps/logic/skills-and-agents.md + - https://docs.twenty.com/developers/extend/apps/logic/connections.md +- Layout: + - https://docs.twenty.com/developers/extend/apps/layout/overview.md + - https://docs.twenty.com/developers/extend/apps/layout/views.md + - https://docs.twenty.com/developers/extend/apps/layout/navigation-menu-items.md + - https://docs.twenty.com/developers/extend/apps/layout/page-layouts.md + - https://docs.twenty.com/developers/extend/apps/layout/front-components.md + - https://docs.twenty.com/developers/extend/apps/layout/command-menu-items.md +- Operations: + - https://docs.twenty.com/developers/extend/apps/operations/overview.md + - https://docs.twenty.com/developers/extend/apps/operations/cli.md + - https://docs.twenty.com/developers/extend/apps/operations/testing.md + - https://docs.twenty.com/developers/extend/apps/operations/publishing.md +- Rich app example: https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples/postcard + +## UUID requirement + +- All generated UUIDs must be valid UUID v4. + +## Common Pitfalls + +- Creating an object without an index view associated. Unless this is a technical object, user will need to visualize it. +- Creating a view without a navigationMenuItem associated. This will make the view available on the left sidebar. +- Creating a front-end component that has a scroll instead of being responsive to its fixed widget height and width, unless it is specifically meant to be used in a canvas tab. + +## Best practice + +It's highly recommended to create new app entities using `yarn twenty dev:add`. These are the options: + +| Entity type | Command | Generated file | +| -------------------- | ---------------------------------------- | ------------------------------------- | +| Object | `yarn twenty dev:add object` | `src/objects/.ts` | +| Field | `yarn twenty dev:add field` | `src/fields/.ts` | +| Logic function | `yarn twenty dev:add logicFunction` | `src/logic-functions/.ts` | +| Front component | `yarn twenty dev:add frontComponent` | `src/front-components/.tsx` | +| Role | `yarn twenty dev:add role` | `src/roles/.ts` | +| Skill | `yarn twenty dev:add skill` | `src/skills/.ts` | +| Agent | `yarn twenty dev:add agent` | `src/agents/.ts` | +| View | `yarn twenty dev:add view` | `src/views/.ts` | +| Navigation menu item | `yarn twenty dev:add navigationMenuItem` | `src/navigation-menu-items/.ts` | +| Page layout | `yarn twenty dev:add pageLayout` | `src/page-layouts/.ts` | + +This helps automatically generate required IDs etc. diff --git a/packages/twenty-apps/internal/twenty-last-contact/README.md b/packages/twenty-apps/internal/twenty-last-contact/README.md new file mode 100644 index 0000000000..92a274c352 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/README.md @@ -0,0 +1,23 @@ +# Last contacted at + +A [Twenty](https://twenty.com) official application that adds a `lastContactAt` field to the standard Person object and keeps it in sync with email and calendar activity. + +## What it does + +- Adds a **Last Contact** (`lastContactAt`, `DATE_TIME`) field on Person, visible in the All People view. +- Sets the field to the most recent interaction whenever a synced email or calendar event is linked to a person. +- Counts a meeting as contact when it starts, via a cron-triggered logic function. +- Backfills the field from existing message and calendar history right after install. + +### Application variables + +| Variable | Default | Description | +| --- | --- | --- | +| `CALENDAR_CRON_INTERVAL_MINUTES` | `5` | Interval between runs of `on-calendar-event-started`. The cron scans events that started within the last interval plus a 5-minute safety overlap. | + + +## Learn more + +- [Twenty Apps documentation](https://docs.twenty.com/developers/extend/apps/getting-started/quick-start) +- [twenty-sdk CLI reference](https://www.npmjs.com/package/twenty-sdk) +- [Discord](https://discord.gg/cx5n4Jzs57) diff --git a/packages/twenty-apps/internal/twenty-last-contact/package.json b/packages/twenty-apps/internal/twenty-last-contact/package.json new file mode 100644 index 0000000000..b7b66048ce --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/package.json @@ -0,0 +1,34 @@ +{ + "name": "last-contacted-at", + "version": "1.0.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", + "test:unit": "vitest run --config vitest.unit.config.ts" + }, + "devDependencies": { + "@types/node": "^24.7.2", + "@types/react": "^19.0.0", + "oxlint": "^0.16.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "twenty-sdk": "^2.10.1", + "typescript": "^5.9.3", + "vite-tsconfig-paths": "^4.2.1", + "vitest": "^3.1.1" + }, + "dependencies": { + "twenty-client-sdk": "^2.10.1" + } +} diff --git a/packages/twenty-apps/internal/twenty-last-contact/public/gallery/cover.png b/packages/twenty-apps/internal/twenty-last-contact/public/gallery/cover.png new file mode 100644 index 0000000000..83c6a1683e Binary files /dev/null and b/packages/twenty-apps/internal/twenty-last-contact/public/gallery/cover.png differ diff --git a/packages/twenty-apps/internal/twenty-last-contact/public/logo.png b/packages/twenty-apps/internal/twenty-last-contact/public/logo.png new file mode 100644 index 0000000000..fa9876266d Binary files /dev/null and b/packages/twenty-apps/internal/twenty-last-contact/public/logo.png differ diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/__tests__/global-setup.ts b/packages/twenty-apps/internal/twenty-last-contact/src/__tests__/global-setup.ts new file mode 100644 index 0000000000..e0f8c23b05 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/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 docker: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-last-contact/src/__tests__/last-contact.integration-test.ts b/packages/twenty-apps/internal/twenty-last-contact/src/__tests__/last-contact.integration-test.ts new file mode 100644 index 0000000000..fa61a2e158 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/__tests__/last-contact.integration-test.ts @@ -0,0 +1,377 @@ +import { CoreApiClient } from 'twenty-client-sdk/core'; +import { MetadataApiClient } from 'twenty-client-sdk/metadata'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; + +import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import onCalendarInteraction from 'src/logic-functions/on-calendar-interaction'; +import onEmailInteraction from 'src/logic-functions/on-email-interaction'; + +const calendarHandler = onCalendarInteraction.config.handler as ( + event: unknown, +) => Promise; +const emailHandler = onEmailInteraction.config.handler as ( + event: unknown, +) => Promise; + +const DAY_IN_MS = 24 * 60 * 60 * 1000; + +const requireId = (id: string | null | undefined, what: string): string => { + if (!id) throw new Error(`${what} returned no id`); + return id; +}; + +const asTime = (value: string | null | undefined): number | null => + value ? new Date(value).getTime() : null; + +const createPerson = async (client: CoreApiClient): Promise => { + const result = await client.mutation({ + createPerson: { + __args: { + data: { + name: { firstName: 'Test', lastName: `LastContact-${Date.now()}` }, + }, + }, + id: true, + }, + }); + + return requireId(result.createPerson?.id, 'createPerson'); +}; + +const createCalendarEvent = async ( + client: CoreApiClient, + { startsAt, isCanceled = false }: { startsAt: string; isCanceled?: boolean }, +): Promise => { + const result = await client.mutation({ + createCalendarEvent: { + __args: { + data: { + title: `[test-last-contact] event ${Date.now()}`, + startsAt, + isCanceled, + }, + }, + id: true, + }, + }); + + return requireId(result.createCalendarEvent?.id, 'createCalendarEvent'); +}; + +const createCalendarEventParticipant = async ( + client: CoreApiClient, + { calendarEventId, personId }: { calendarEventId: string; personId: string }, +): Promise => { + const result = await client.mutation({ + createCalendarEventParticipant: { + __args: { data: { calendarEventId, personId } }, + id: true, + }, + }); + + return requireId( + result.createCalendarEventParticipant?.id, + 'createCalendarEventParticipant', + ); +}; + +const getAnyMessageChannelId = async ( + client: CoreApiClient, +): Promise => { + const result = await client.query({ + messageChannelMessageAssociations: { + __args: { first: 1 }, + edges: { node: { id: true, messageChannelId: true } }, + }, + }); + + const messageChannelId = + result.messageChannelMessageAssociations?.edges?.[0]?.node + ?.messageChannelId; + + if (!messageChannelId) { + throw new Error( + 'No message channel found — run against a workspace with seeded messaging data', + ); + } + + return messageChannelId; +}; + +const createMessage = async ( + client: CoreApiClient, + { receivedAt }: { receivedAt: string }, +): Promise => { + const result = await client.mutation({ + createMessage: { + __args: { + data: { + subject: `[test-last-contact] message ${Date.now()}`, + receivedAt, + }, + }, + id: true, + }, + }); + + return requireId(result.createMessage?.id, 'createMessage'); +}; + +const createMessageChannelAssociation = async ( + client: CoreApiClient, + { messageId, messageChannelId }: { messageId: string; messageChannelId: string }, +): Promise => { + const result = await client.mutation({ + createMessageChannelMessageAssociation: { + __args: { data: { messageId, messageChannelId } }, + id: true, + }, + }); + + return requireId( + result.createMessageChannelMessageAssociation?.id, + 'createMessageChannelMessageAssociation', + ); +}; + +const getPersonLastContactAt = async ( + client: CoreApiClient, + personId: string, +): Promise => { + const result = await client.query({ + person: { + __args: { filter: { id: { eq: personId } } }, + id: true, + lastContactAt: true, + }, + }); + + return ( + (result.person as { lastContactAt?: string | null })?.lastContactAt ?? null + ); +}; + +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 matchingApplication = result.findManyApplications.find( + (application: { universalIdentifier: string }) => + application.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER, + ); + + expect(matchingApplication).toBeDefined(); + }); +}); + +describe('last contact handlers', () => { + let client: CoreApiClient; + + const createdParticipantIds: string[] = []; + const createdCalendarEventIds: string[] = []; + const createdMessageAssociationIds: string[] = []; + const createdMessageIds: string[] = []; + const createdPersonIds: string[] = []; + + const createLinkedMessage = async (receivedAt: string): Promise => { + const messageId = await createMessage(client, { receivedAt }); + createdMessageIds.push(messageId); + const messageChannelId = await getAnyMessageChannelId(client); + const associationId = await createMessageChannelAssociation(client, { + messageId, + messageChannelId, + }); + createdMessageAssociationIds.push(associationId); + + return messageId; + }; + + beforeEach(() => { + client = new CoreApiClient(); + }); + + afterEach(async () => { + for (const id of createdParticipantIds) { + await client + .mutation({ + destroyCalendarEventParticipant: { __args: { id }, id: true }, + }) + .catch(() => {}); + } + createdParticipantIds.length = 0; + + for (const id of createdCalendarEventIds) { + await client + .mutation({ destroyCalendarEvent: { __args: { id }, id: true } }) + .catch(() => {}); + } + createdCalendarEventIds.length = 0; + + for (const id of createdMessageAssociationIds) { + await client + .mutation({ + destroyMessageChannelMessageAssociation: { __args: { id }, id: true }, + }) + .catch(() => {}); + } + createdMessageAssociationIds.length = 0; + + for (const id of createdMessageIds) { + await client + .mutation({ destroyMessage: { __args: { id }, id: true } }) + .catch(() => {}); + } + createdMessageIds.length = 0; + + for (const id of createdPersonIds) { + await client + .mutation({ destroyPerson: { __args: { id }, id: true } }) + .catch(() => {}); + } + createdPersonIds.length = 0; + }); + + it('should expose a lastContactAt field on people, unset by default', async () => { + const personId = await createPerson(client); + createdPersonIds.push(personId); + + expect(await getPersonLastContactAt(client, personId)).toBeNull(); + }); + + it('should set lastContactAt to the event startsAt when a person attended a past calendar event', async () => { + const startsAt = new Date(Date.now() - DAY_IN_MS).toISOString(); + const personId = await createPerson(client); + createdPersonIds.push(personId); + const calendarEventId = await createCalendarEvent(client, { startsAt }); + createdCalendarEventIds.push(calendarEventId); + const participantId = await createCalendarEventParticipant(client, { + calendarEventId, + personId, + }); + createdParticipantIds.push(participantId); + + await calendarHandler({ + recordId: participantId, + properties: { + updatedFields: ['personId'], + after: { id: participantId, personId }, + }, + }); + + expect(asTime(await getPersonLastContactAt(client, personId))).toBe( + asTime(startsAt), + ); + }); + + it('should not set lastContactAt when the calendar event is in the future', async () => { + const startsAt = new Date(Date.now() + DAY_IN_MS).toISOString(); + const personId = await createPerson(client); + createdPersonIds.push(personId); + const calendarEventId = await createCalendarEvent(client, { startsAt }); + createdCalendarEventIds.push(calendarEventId); + const participantId = await createCalendarEventParticipant(client, { + calendarEventId, + personId, + }); + createdParticipantIds.push(participantId); + + await calendarHandler({ + recordId: participantId, + properties: { + updatedFields: ['personId'], + after: { id: participantId, personId }, + }, + }); + + expect(await getPersonLastContactAt(client, personId)).toBeNull(); + }); + + it('should not set lastContactAt when the past calendar event is canceled', async () => { + const startsAt = new Date(Date.now() - DAY_IN_MS).toISOString(); + const personId = await createPerson(client); + createdPersonIds.push(personId); + const calendarEventId = await createCalendarEvent(client, { + startsAt, + isCanceled: true, + }); + createdCalendarEventIds.push(calendarEventId); + const participantId = await createCalendarEventParticipant(client, { + calendarEventId, + personId, + }); + createdParticipantIds.push(participantId); + + await calendarHandler({ + recordId: participantId, + properties: { + updatedFields: ['personId'], + after: { id: participantId, personId }, + }, + }); + + expect(await getPersonLastContactAt(client, personId)).toBeNull(); + }); + + it('should set lastContactAt to the message receivedAt when a person is matched on an email', async () => { + const receivedAt = new Date(Date.now() - DAY_IN_MS).toISOString(); + const personId = await createPerson(client); + createdPersonIds.push(personId); + const messageId = await createLinkedMessage(receivedAt); + + await emailHandler({ + recordId: 'unused-participant-id', + properties: { + updatedFields: ['personId'], + after: { id: 'unused-participant-id', personId, messageId }, + }, + }); + + expect(asTime(await getPersonLastContactAt(client, personId))).toBe( + asTime(receivedAt), + ); + }); + + it('should not overwrite a newer lastContactAt with an older interaction', async () => { + const newerReceivedAt = new Date(Date.now() - DAY_IN_MS).toISOString(); + const olderReceivedAt = new Date(Date.now() - 3 * DAY_IN_MS).toISOString(); + const personId = await createPerson(client); + createdPersonIds.push(personId); + const newerMessageId = await createLinkedMessage(newerReceivedAt); + const olderMessageId = await createLinkedMessage(olderReceivedAt); + + await emailHandler({ + recordId: 'unused-participant-id', + properties: { + updatedFields: ['personId'], + after: { + id: 'unused-participant-id', + personId, + messageId: newerMessageId, + }, + }, + }); + await emailHandler({ + recordId: 'unused-participant-id', + properties: { + updatedFields: ['personId'], + after: { + id: 'unused-participant-id', + personId, + messageId: olderMessageId, + }, + }, + }); + + expect(asTime(await getPersonLastContactAt(client, personId))).toBe( + asTime(newerReceivedAt), + ); + }); +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/application-config.ts b/packages/twenty-apps/internal/twenty-last-contact/src/application-config.ts new file mode 100644 index 0000000000..71a44f2429 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/application-config.ts @@ -0,0 +1,28 @@ +import { defineApplication } from 'twenty-sdk/define'; + +import { + APP_DESCRIPTION, + APP_DISPLAY_NAME, + APPLICATION_UNIVERSAL_IDENTIFIER, + CALENDAR_CRON_INTERVAL_MINUTES_VARIABLE_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; +import { ABOUT_DESCRIPTION } from 'src/constants/about-description.constant'; + +export default defineApplication({ + universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, + logoUrl: 'public/logo.png', + screenshots: ['public/gallery/cover.png'], + displayName: APP_DISPLAY_NAME, + description: APP_DESCRIPTION, + aboutDescription: ABOUT_DESCRIPTION, + applicationVariables: { + CALENDAR_CRON_INTERVAL_MINUTES: { + universalIdentifier: + CALENDAR_CRON_INTERVAL_MINUTES_VARIABLE_UNIVERSAL_IDENTIFIER, + description: + 'Interval in minutes between runs of the on-calendar-event-started cron.', + value: '5', + isSecret: false, + }, + }, +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/constants/about-description.constant.ts b/packages/twenty-apps/internal/twenty-last-contact/src/constants/about-description.constant.ts new file mode 100644 index 0000000000..b034aeb3c1 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/constants/about-description.constant.ts @@ -0,0 +1,21 @@ +export const ABOUT_DESCRIPTION = `# Never let a relationship go cold + +**Last contacted** answers the question every CRM should answer instantly: *when did we last talk to this person?* + +It adds a **Last Contact** field to your People — and keeps it accurate without anyone logging anything, ever. + +## Why teams install it + +- **Zero manual logging** — every synced email and calendar meeting updates the field automatically, in real time. +- **Useful from minute one** — on install, your entire email and meeting history is backfilled. No empty columns, no waiting. +- **Spot cold relationships instantly** — sort or filter any People view by Last Contact to build follow-up lists in seconds. +- **Meeting-aware** — a meeting counts as contact the moment it starts, not whenever someone remembers to update the CRM. + +## How it works + +1. A **Last Contact** date field is added to your people. +2. Logic functions listen to the emails and calendar events Twenty already syncs, and keep the field set to the most recent interaction. +3. A built-in scheduler marks meetings as contact as soon as they begin. +4. A post-install backfill fills the field from your existing history. + +No setup. No configuration. Install it, open People, and you immediately know who needs a follow-up.`; diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/constants/universal-identifiers.ts b/packages/twenty-apps/internal/twenty-last-contact/src/constants/universal-identifiers.ts new file mode 100644 index 0000000000..39b9114247 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/constants/universal-identifiers.ts @@ -0,0 +1,19 @@ +export const APP_DISPLAY_NAME = 'Last contact'; +export const APP_DESCRIPTION = + 'Always know when you last talked to anyone. Adds a Last Contact field to People, kept up to date automatically from your synced emails and meetings.'; +export const APPLICATION_UNIVERSAL_IDENTIFIER = '66a504cc-0a75-410e-a43f-cdeae1db1522'; +export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = '34187abe-1b98-4153-85cd-4808e0aebf30'; +export const LAST_CONTACT_AT_FIELD_UNIVERSAL_IDENTIFIER = + '4e0fd7ff-0bbc-47b2-baab-5fe2c0d12557'; +export const LAST_CONTACT_AT_VIEW_FIELD_UNIVERSAL_IDENTIFIER = + '8fc4d931-426c-413b-bd7e-0c10d1ad8d0a'; +export const EMAIL_INTERACTION_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER = + '87c8926b-15e3-43ed-a303-60f6d072f351'; +export const CALENDAR_INTERACTION_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER = + 'ff4e9fc0-a5d3-4a3d-87f9-81c23598589e'; +export const BACKFILL_POST_INSTALL_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER = + 'c94f671f-b3fa-47a2-8de6-dde94d13f8d1'; +export const CALENDAR_EVENT_STARTED_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER = + 'c56013d7-208b-46e2-a91f-27f481645591'; +export const CALENDAR_CRON_INTERVAL_MINUTES_VARIABLE_UNIVERSAL_IDENTIFIER = + '71cb72d1-9b5d-40fb-82d0-079495af32b1'; diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/default-role.ts b/packages/twenty-apps/internal/twenty-last-contact/src/default-role.ts new file mode 100644 index 0000000000..b1bd057299 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/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: false, + canDestroyAllObjectRecords: false, +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/fields/last-contact-at.field.ts b/packages/twenty-apps/internal/twenty-last-contact/src/fields/last-contact-at.field.ts new file mode 100644 index 0000000000..68e3a942c9 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/fields/last-contact-at.field.ts @@ -0,0 +1,20 @@ +import { + defineField, + FieldType, + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, +} from 'twenty-sdk/define'; + +import { LAST_CONTACT_AT_FIELD_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; + +export default defineField({ + universalIdentifier: LAST_CONTACT_AT_FIELD_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier, + name: 'lastContactAt', + type: FieldType.DATE_TIME, + label: 'Last Contact', + description: + 'When the most recent interaction (email or calendar event) with this person occurred.', + icon: 'IconClock', + isNullable: true, +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/__tests__/on-calendar-event-started.test.ts b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/__tests__/on-calendar-event-started.test.ts new file mode 100644 index 0000000000..89acd6d73b --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/__tests__/on-calendar-event-started.test.ts @@ -0,0 +1,170 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const { queryMock, mutationMock } = vi.hoisted(() => ({ + queryMock: vi.fn(), + mutationMock: vi.fn(), +})); +vi.mock('twenty-client-sdk/core', () => ({ + CoreApiClient: vi.fn(() => ({ query: queryMock, mutation: mutationMock })), +})); + +import onCalendarEventStarted from '../on-calendar-event-started'; + +const PERSON_ID_1 = '11111111-1111-1111-1111-111111111111'; +const PERSON_ID_2 = '22222222-2222-2222-2222-222222222222'; +const PAST_EVENT_STARTS_AT = '2026-06-10T09:00:00.000Z'; + +const handler = onCalendarEventStarted.config.handler as () => Promise; + +type Page = { + edges: { node: Record }[]; + pageInfo: { hasNextPage: boolean; endCursor: string | null }; +}; + +const setupQueryMock = ({ + calendarEventsPages, + participantsPages, +}: { + calendarEventsPages: Page[]; + participantsPages: Page[]; +}) => { + const remainingEventsPages = [...calendarEventsPages]; + const remainingParticipantsPages = [...participantsPages]; + + queryMock.mockImplementation((query) => { + if (query.calendarEvents) { + return Promise.resolve({ calendarEvents: remainingEventsPages.shift() }); + } + + if (query.calendarEventParticipants.__args.filter.calendarEventId) { + return Promise.resolve({ + calendarEventParticipants: remainingParticipantsPages.shift(), + }); + } + + return Promise.resolve({ + calendarEventParticipants: { + edges: [ + { + node: { + id: 'participant-latest', + calendarEvent: { + id: 'event-latest', + startsAt: PAST_EVENT_STARTS_AT, + }, + }, + }, + ], + }, + }); + }); +}; + +const singlePage = (nodes: Record[]): Page => ({ + edges: nodes.map((node) => ({ node })), + pageInfo: { hasNextPage: false, endCursor: null }, +}); + +beforeEach(() => { + queryMock.mockReset(); + mutationMock.mockReset(); + mutationMock.mockResolvedValue({ updatePeople: [] }); +}); + +describe('on-calendar-event-started definition', () => { + it('should be valid and run on a cron schedule', () => { + expect(onCalendarEventStarted.success).toBe(true); + expect(onCalendarEventStarted.config.cronTriggerSettings).toEqual({ + pattern: '*/5 * * * *', + }); + }); +}); + +describe('on-calendar-event-started handler', () => { + it('should paginate calendar events past the query record cap', async () => { + setupQueryMock({ + calendarEventsPages: [ + { + edges: [{ node: { id: 'event-1' } }], + pageInfo: { hasNextPage: true, endCursor: 'events-cursor-1' }, + }, + singlePage([{ id: 'event-2' }]), + ], + participantsPages: [singlePage([])], + }); + + await handler(); + + const calendarEventsCalls = queryMock.mock.calls.filter( + ([query]) => query.calendarEvents, + ); + expect(calendarEventsCalls).toHaveLength(2); + expect(calendarEventsCalls[0][0].calendarEvents.__args.first).toBe(200); + expect(calendarEventsCalls[0][0].calendarEvents.__args.after).toBeUndefined(); + expect(calendarEventsCalls[1][0].calendarEvents.__args.after).toBe( + 'events-cursor-1', + ); + + const participantsCall = queryMock.mock.calls.find( + ([query]) => + query.calendarEventParticipants?.__args.filter.calendarEventId, + ); + expect( + participantsCall?.[0].calendarEventParticipants.__args.filter + .calendarEventId, + ).toEqual({ in: ['event-1', 'event-2'] }); + }); + + it('should paginate participants and update each person once', async () => { + setupQueryMock({ + calendarEventsPages: [singlePage([{ id: 'event-1' }])], + participantsPages: [ + { + edges: [ + { node: { id: 'participant-1', personId: PERSON_ID_1 } }, + { node: { id: 'participant-2', personId: null } }, + ], + pageInfo: { hasNextPage: true, endCursor: 'participants-cursor-1' }, + }, + singlePage([ + { id: 'participant-3', personId: PERSON_ID_1 }, + { id: 'participant-4', personId: PERSON_ID_2 }, + ]), + ], + }); + + await handler(); + + const participantsByEventCalls = queryMock.mock.calls.filter( + ([query]) => + query.calendarEventParticipants?.__args.filter.calendarEventId, + ); + expect(participantsByEventCalls).toHaveLength(2); + expect( + participantsByEventCalls[1][0].calendarEventParticipants.__args.after, + ).toBe('participants-cursor-1'); + + const perPersonCalls = queryMock.mock.calls.filter( + ([query]) => query.calendarEventParticipants?.__args.filter.personId, + ); + expect( + perPersonCalls.map( + ([query]) => + query.calendarEventParticipants.__args.filter.personId.eq, + ), + ).toEqual([PERSON_ID_1, PERSON_ID_2]); + expect(mutationMock).toHaveBeenCalledTimes(2); + }); + + it('should do nothing when no event started in the time window', async () => { + setupQueryMock({ + calendarEventsPages: [singlePage([])], + participantsPages: [], + }); + + await handler(); + + expect(queryMock).toHaveBeenCalledTimes(1); + expect(mutationMock).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/__tests__/on-calendar-interaction.test.ts b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/__tests__/on-calendar-interaction.test.ts new file mode 100644 index 0000000000..46dacd2011 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/__tests__/on-calendar-interaction.test.ts @@ -0,0 +1,82 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const { queryMock, mutationMock } = vi.hoisted(() => ({ + queryMock: vi.fn(), + mutationMock: vi.fn(), +})); +vi.mock('twenty-client-sdk/core', () => ({ + CoreApiClient: vi.fn(() => ({ query: queryMock, mutation: mutationMock })), +})); + +import onCalendarInteraction from '../on-calendar-interaction'; + +const PERSON_ID = '11111111-1111-1111-1111-111111111111'; +const PAST_EVENT_STARTS_AT = '2026-06-10T09:00:00.000Z'; + +const handler = onCalendarInteraction.config.handler as ( + event: unknown, +) => Promise; + +const buildEvent = (personId: string | null) => ({ + recordId: 'participant-1', + properties: { + updatedFields: ['personId'], + after: { id: 'participant-1', personId }, + }, +}); + +beforeEach(() => { + queryMock.mockReset(); + mutationMock.mockReset(); + mutationMock.mockResolvedValue({ updatePeople: [] }); +}); + +describe('on-calendar-interaction definition', () => { + it('should be valid and only trigger on personId updates', () => { + expect(onCalendarInteraction.success).toBe(true); + expect(onCalendarInteraction.config.databaseEventTriggerSettings).toEqual({ + eventName: 'calendarEventParticipant.updated', + updatedFields: ['personId'], + }); + }); +}); + +describe('on-calendar-interaction handler', () => { + it('should update the person from the event payload without re-querying the participant', async () => { + queryMock.mockResolvedValue({ + calendarEventParticipants: { + edges: [ + { + node: { + id: 'participant-1', + calendarEvent: { + id: 'event-1', + startsAt: PAST_EVENT_STARTS_AT, + }, + }, + }, + ], + }, + }); + + await handler(buildEvent(PERSON_ID)); + + expect(queryMock).toHaveBeenCalledTimes(1); + const queryArgs = queryMock.mock.calls[0][0]; + expect(queryArgs.calendarEventParticipants.__args.filter.personId).toEqual( + { eq: PERSON_ID }, + ); + expect(mutationMock).toHaveBeenCalledTimes(1); + const mutationArgs = mutationMock.mock.calls[0][0]; + expect(mutationArgs.updatePeople.__args.data).toEqual({ + lastContactAt: PAST_EVENT_STARTS_AT, + }); + }); + + it('should do nothing when the participant has no personId', async () => { + await handler(buildEvent(null)); + + expect(queryMock).not.toHaveBeenCalled(); + expect(mutationMock).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/__tests__/on-email-interaction.test.ts b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/__tests__/on-email-interaction.test.ts new file mode 100644 index 0000000000..ce19e6f654 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/__tests__/on-email-interaction.test.ts @@ -0,0 +1,100 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const { queryMock, mutationMock } = vi.hoisted(() => ({ + queryMock: vi.fn(), + mutationMock: vi.fn(), +})); +vi.mock('twenty-client-sdk/core', () => ({ + CoreApiClient: vi.fn(() => ({ query: queryMock, mutation: mutationMock })), +})); + +import onEmailInteraction from '../on-email-interaction'; + +const PERSON_ID = '11111111-1111-1111-1111-111111111111'; +const MESSAGE_ID = '22222222-2222-2222-2222-222222222222'; +const RECEIVED_AT = '2026-06-10T09:00:00.000Z'; + +const handler = onEmailInteraction.config.handler as ( + event: unknown, +) => Promise; + +const buildEvent = ({ + personId, + messageId, +}: { + personId: string | null; + messageId: string | null; +}) => ({ + recordId: 'participant-1', + properties: { + updatedFields: ['personId'], + after: { id: 'participant-1', personId, messageId }, + }, +}); + +beforeEach(() => { + queryMock.mockReset(); + mutationMock.mockReset(); + mutationMock.mockResolvedValue({ updatePeople: [] }); +}); + +describe('on-email-interaction definition', () => { + it('should be valid and only trigger on personId updates', () => { + expect(onEmailInteraction.success).toBe(true); + expect(onEmailInteraction.config.databaseEventTriggerSettings).toEqual({ + eventName: 'messageParticipant.updated', + updatedFields: ['personId'], + }); + }); +}); + +describe('on-email-interaction handler', () => { + it('should update the person with the triggering message receivedAt', async () => { + queryMock.mockResolvedValue({ + message: { id: MESSAGE_ID, receivedAt: RECEIVED_AT }, + }); + + await handler(buildEvent({ personId: PERSON_ID, messageId: MESSAGE_ID })); + + expect(queryMock).toHaveBeenCalledTimes(1); + expect(queryMock).toHaveBeenCalledWith({ + message: { + __args: { filter: { id: { eq: MESSAGE_ID } } }, + id: true, + receivedAt: true, + }, + }); + expect(mutationMock).toHaveBeenCalledTimes(1); + const mutationArgs = mutationMock.mock.calls[0][0]; + expect(mutationArgs.updatePeople.__args.data).toEqual({ + lastContactAt: RECEIVED_AT, + }); + expect(mutationArgs.updatePeople.__args.filter.and).toContainEqual({ + id: { eq: PERSON_ID }, + }); + }); + + it('should do nothing when the participant has no personId', async () => { + await handler(buildEvent({ personId: null, messageId: MESSAGE_ID })); + + expect(queryMock).not.toHaveBeenCalled(); + expect(mutationMock).not.toHaveBeenCalled(); + }); + + it('should do nothing when the participant has no messageId', async () => { + await handler(buildEvent({ personId: PERSON_ID, messageId: null })); + + expect(queryMock).not.toHaveBeenCalled(); + expect(mutationMock).not.toHaveBeenCalled(); + }); + + it('should not update the person when the message has no receivedAt', async () => { + queryMock.mockResolvedValue({ + message: { id: MESSAGE_ID, receivedAt: null }, + }); + + await handler(buildEvent({ personId: PERSON_ID, messageId: MESSAGE_ID })); + + expect(mutationMock).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/backfill-last-contact-at.ts b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/backfill-last-contact-at.ts new file mode 100644 index 0000000000..0010395735 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/backfill-last-contact-at.ts @@ -0,0 +1,192 @@ +import { definePostInstallLogicFunction } from 'twenty-sdk/define'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +import { BACKFILL_POST_INSTALL_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; + +const PAGE_SIZE = 200; +const UPDATE_BATCH_SIZE = 20; + +type LastContactAtByPersonId = Map; + +const recordContact = ( + contacts: LastContactAtByPersonId, + personId: string, + contactedAt: string, +): void => { + const current = contacts.get(personId); + if (!current || contactedAt > current) { + contacts.set(personId, contactedAt); + } +}; + +const chunk = (items: T[], size: number): T[][] => { + const chunks: T[][] = []; + for (let i = 0; i < items.length; i += size) { + chunks.push(items.slice(i, i + size)); + } + return chunks; +}; + +const collectEmailContacts = async ( + client: CoreApiClient, + contacts: LastContactAtByPersonId, +): Promise => { + let after: string | undefined; + + do { + const { messageParticipants } = await client.query({ + messageParticipants: { + __args: { + filter: { personId: { is: 'NOT_NULL' } }, + first: PAGE_SIZE, + after, + }, + edges: { + node: { + id: true, + personId: true, + message: { + id: true, + receivedAt: true, + }, + }, + }, + pageInfo: { hasNextPage: true, endCursor: true }, + }, + }); + + for (const edge of messageParticipants?.edges ?? []) { + const { personId, message } = edge.node; + if (personId && message?.receivedAt) { + recordContact(contacts, personId, message.receivedAt); + } + } + + after = messageParticipants?.pageInfo.hasNextPage + ? (messageParticipants.pageInfo.endCursor ?? undefined) + : undefined; + } while (after); +}; + +const collectCalendarContacts = async ( + client: CoreApiClient, + contacts: LastContactAtByPersonId, +): Promise => { + const now = new Date().toISOString(); + let after: string | undefined; + + do { + const { calendarEventParticipants } = await client.query({ + calendarEventParticipants: { + __args: { + filter: { personId: { is: 'NOT_NULL' } }, + first: PAGE_SIZE, + after, + }, + edges: { + node: { + id: true, + personId: true, + calendarEvent: { + id: true, + startsAt: true, + isCanceled: true, + }, + }, + }, + pageInfo: { hasNextPage: true, endCursor: true }, + }, + }); + + for (const edge of calendarEventParticipants?.edges ?? []) { + const { personId, calendarEvent } = edge.node; + if ( + personId && + calendarEvent?.startsAt && + !calendarEvent.isCanceled && + calendarEvent.startsAt <= now + ) { + recordContact(contacts, personId, calendarEvent.startsAt); + } + } + + after = calendarEventParticipants?.pageInfo.hasNextPage + ? (calendarEventParticipants.pageInfo.endCursor ?? undefined) + : undefined; + } while (after); +}; + +const findPersonsToUpdate = async ( + client: CoreApiClient, + contacts: LastContactAtByPersonId, +): Promise<{ personId: string; lastContactAt: string }[]> => { + const updates: { personId: string; lastContactAt: string }[] = []; + + for (const personIds of chunk([...contacts.keys()], PAGE_SIZE)) { + const { people } = await client.query({ + people: { + __args: { + filter: { id: { in: personIds } }, + first: personIds.length, + }, + edges: { + node: { + id: true, + lastContactAt: true, + }, + }, + }, + }); + + for (const edge of people?.edges ?? []) { + const { id, lastContactAt: currentLastContactAt } = edge.node; + const lastContactAt = contacts.get(id); + if ( + lastContactAt && + (!currentLastContactAt || currentLastContactAt < lastContactAt) + ) { + updates.push({ personId: id, lastContactAt }); + } + } + } + + return updates; +}; + +const handler = async (): Promise => { + const client = new CoreApiClient(); + const contacts: LastContactAtByPersonId = new Map(); + + await Promise.all([ + collectEmailContacts(client, contacts), + collectCalendarContacts(client, contacts), + ]); + + const updates = await findPersonsToUpdate(client, contacts); + + for (const batch of chunk(updates, UPDATE_BATCH_SIZE)) { + await Promise.all( + batch.map(({ personId, lastContactAt }) => + client.mutation({ + updatePerson: { + __args: { + id: personId, + data: { lastContactAt }, + }, + id: true, + }, + }), + ), + ); + } +}; + +export default definePostInstallLogicFunction({ + universalIdentifier: BACKFILL_POST_INSTALL_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER, + name: 'backfill-last-contact-at', + description: + 'Fills person last-contacted fields from existing messages and calendar events after installation.', + timeoutSeconds: 300, + shouldRunOnVersionUpgrade: false, + handler, +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/on-calendar-event-started.ts b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/on-calendar-event-started.ts new file mode 100644 index 0000000000..78c0b72dd9 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/on-calendar-event-started.ts @@ -0,0 +1,122 @@ +import { defineLogicFunction } from 'twenty-sdk/define'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +import { CALENDAR_EVENT_STARTED_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { updatePersonLastContactAtFromCalendar } from 'src/utils/update-person-last-contact-at-from-calendar'; + +const CRON_INTERVAL_MINUTES = Math.min( + Math.max(Number(process.env.CALENDAR_CRON_INTERVAL_MINUTES ?? 5), 1), + 60 * 24, +); + +const SECURITY_OVERLAP_MINUTES = 5; + +const QUERY_MAX_RECORDS = 200; + +const handler = async (): Promise => { + const client = new CoreApiClient(); + + const now = new Date(); + const windowStart = new Date( + now.getTime() - + (CRON_INTERVAL_MINUTES + SECURITY_OVERLAP_MINUTES) * 60 * 1000, + ); + + const calendarEventIds: string[] = []; + let calendarEventsCursor: string | undefined; + let calendarEventsHasNextPage = true; + + while (calendarEventsHasNextPage) { + const { calendarEvents } = await client.query({ + calendarEvents: { + __args: { + filter: { + and: [ + { startsAt: { gt: windowStart.toISOString() } }, + { startsAt: { lte: now.toISOString() } }, + { isCanceled: { eq: false } }, + ], + }, + first: QUERY_MAX_RECORDS, + after: calendarEventsCursor, + }, + edges: { + node: { + id: true, + }, + }, + pageInfo: { + hasNextPage: true, + endCursor: true, + }, + }, + }); + + calendarEventIds.push( + ...(calendarEvents?.edges.map((edge) => edge.node.id) ?? []), + ); + calendarEventsHasNextPage = calendarEvents?.pageInfo.hasNextPage ?? false; + calendarEventsCursor = calendarEvents?.pageInfo.endCursor ?? undefined; + } + + if (calendarEventIds.length === 0) { + return; + } + + const personIds = new Set(); + let participantsCursor: string | undefined; + let participantsHasNextPage = true; + + while (participantsHasNextPage) { + const { calendarEventParticipants } = await client.query({ + calendarEventParticipants: { + __args: { + filter: { calendarEventId: { in: calendarEventIds } }, + first: QUERY_MAX_RECORDS, + after: participantsCursor, + }, + edges: { + node: { + id: true, + personId: true, + }, + }, + pageInfo: { + hasNextPage: true, + endCursor: true, + }, + }, + }); + + for (const edge of calendarEventParticipants?.edges ?? []) { + const personId = edge.node.personId; + + if (personId !== null && personId !== undefined) { + personIds.add(personId); + } + } + participantsHasNextPage = + calendarEventParticipants?.pageInfo.hasNextPage ?? false; + participantsCursor = + calendarEventParticipants?.pageInfo.endCursor ?? undefined; + } + + await Promise.all( + [...personIds].map((personId) => + updatePersonLastContactAtFromCalendar(client, personId), + ), + ); +}; + +export default defineLogicFunction({ + universalIdentifier: + CALENDAR_EVENT_STARTED_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER, + name: 'on-calendar-event-started', + description: + 'Updates last-contacted fields for participants of calendar events whose start time just passed.', + timeoutSeconds: 60, + cronTriggerSettings: { + pattern: `*/${CRON_INTERVAL_MINUTES} * * * *`, + }, + handler, +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/on-calendar-interaction.ts b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/on-calendar-interaction.ts new file mode 100644 index 0000000000..2c7eefa786 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/on-calendar-interaction.ts @@ -0,0 +1,33 @@ +import { defineLogicFunction, ObjectRecordUpdateEvent } from 'twenty-sdk/define'; +import type { DatabaseEventPayload } from 'twenty-sdk/logic-function'; +import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; + +import { CALENDAR_INTERACTION_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { updatePersonLastContactAtFromCalendar } from 'src/utils/update-person-last-contact-at-from-calendar'; + +const handler = async (event: DatabaseEventPayload< + ObjectRecordUpdateEvent +>): Promise => { + const personId = event.properties.after.personId; + + if (!personId) { + return; + } + + const client = new CoreApiClient(); + + await updatePersonLastContactAtFromCalendar(client, personId); +}; + +export default defineLogicFunction({ + universalIdentifier: CALENDAR_INTERACTION_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER, + name: 'on-calendar-interaction', + description: + 'Updates a person\'s last-contacted fields when a new calendar event participant is created (past events only).', + timeoutSeconds: 60, + databaseEventTriggerSettings: { + eventName: 'calendarEventParticipant.updated', + updatedFields: ['personId'], + }, + handler, +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/on-email-interaction.ts b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/on-email-interaction.ts new file mode 100644 index 0000000000..e8349d047d --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/logic-functions/on-email-interaction.ts @@ -0,0 +1,46 @@ +import { defineLogicFunction, ObjectRecordUpdateEvent } from 'twenty-sdk/define'; +import type { DatabaseEventPayload } from 'twenty-sdk/logic-function'; +import { CoreApiClient, CoreSchema } from 'twenty-client-sdk/core'; + +import { EMAIL_INTERACTION_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { updatePersonLastContactAtIfNewer } from 'src/utils/update-person-last-contact-at'; + +const handler = async (event: DatabaseEventPayload>): Promise => { + const personId = event.properties.after.personId; + const messageId = event.properties.after.messageId; + + if (!personId || !messageId) { + return; + } + + const client = new CoreApiClient(); + + const { message } = await client.query({ + message: { + __args: { filter: { id: { eq: messageId } } }, + id: true, + receivedAt: true, + }, + }); + + const lastContactAt = message?.receivedAt ?? null; + + if (!lastContactAt) { + return; + } + + await updatePersonLastContactAtIfNewer(client, personId, lastContactAt); +}; + +export default defineLogicFunction({ + universalIdentifier: EMAIL_INTERACTION_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER, + name: 'on-email-interaction', + description: + 'Updates a person\'s last-contacted fields when a new email participant is created.', + timeoutSeconds: 60, + databaseEventTriggerSettings: { + eventName: 'messageParticipant.updated', + updatedFields: ['personId'] + }, + handler, +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/utils/__tests__/update-person-last-contact-at-from-calendar.test.ts b/packages/twenty-apps/internal/twenty-last-contact/src/utils/__tests__/update-person-last-contact-at-from-calendar.test.ts new file mode 100644 index 0000000000..93e01d60f7 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/utils/__tests__/update-person-last-contact-at-from-calendar.test.ts @@ -0,0 +1,99 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import type { CoreApiClient } from 'twenty-client-sdk/core'; + +import { updatePersonLastContactAtFromCalendar } from 'src/utils/update-person-last-contact-at-from-calendar'; + +const PERSON_ID = '11111111-1111-1111-1111-111111111111'; +const NOW = '2026-06-12T12:00:00.000Z'; +const PAST_EVENT_STARTS_AT = '2026-06-10T09:00:00.000Z'; + +const buildClient = (queryResult: unknown) => { + const queryMock = vi.fn().mockResolvedValue(queryResult); + const mutationMock = vi.fn().mockResolvedValue({ updatePeople: [] }); + const client = { + query: queryMock, + mutation: mutationMock, + } as unknown as CoreApiClient; + + return { client, queryMock, mutationMock }; +}; + +beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date(NOW)); +}); + +afterEach(() => { + vi.useRealTimers(); +}); + +describe('updatePersonLastContactAtFromCalendar', () => { + it('should filter on past non-canceled events in the query and only fetch the latest one', async () => { + const { client, queryMock } = buildClient({ + calendarEventParticipants: { edges: [] }, + }); + + await updatePersonLastContactAtFromCalendar(client, PERSON_ID); + + expect(queryMock).toHaveBeenCalledWith({ + calendarEventParticipants: { + __args: { + filter: { + personId: { eq: PERSON_ID }, + calendarEvent: { + startsAt: { lte: NOW }, + isCanceled: { eq: false }, + }, + }, + orderBy: [{ calendarEvent: { startsAt: 'DescNullsLast' } }], + first: 1, + }, + edges: { + node: { + id: true, + calendarEvent: { + id: true, + startsAt: true, + }, + }, + }, + }, + }); + }); + + it('should update the person with the latest past event startsAt', async () => { + const { client, mutationMock } = buildClient({ + calendarEventParticipants: { + edges: [ + { + node: { + id: 'participant-1', + calendarEvent: { + id: 'event-1', + startsAt: PAST_EVENT_STARTS_AT, + }, + }, + }, + ], + }, + }); + + await updatePersonLastContactAtFromCalendar(client, PERSON_ID); + + expect(mutationMock).toHaveBeenCalledTimes(1); + const mutationArgs = mutationMock.mock.calls[0][0]; + expect(mutationArgs.updatePeople.__args.data).toEqual({ + lastContactAt: PAST_EVENT_STARTS_AT, + }); + }); + + it('should not update the person when they have no past events', async () => { + const { client, mutationMock } = buildClient({ + calendarEventParticipants: { edges: [] }, + }); + + await updatePersonLastContactAtFromCalendar(client, PERSON_ID); + + expect(mutationMock).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/utils/__tests__/update-person-last-contact-at.test.ts b/packages/twenty-apps/internal/twenty-last-contact/src/utils/__tests__/update-person-last-contact-at.test.ts new file mode 100644 index 0000000000..7b7678e9b4 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/utils/__tests__/update-person-last-contact-at.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it, vi } from 'vitest'; +import type { CoreApiClient } from 'twenty-client-sdk/core'; + +import { updatePersonLastContactAtIfNewer } from 'src/utils/update-person-last-contact-at'; + +const PERSON_ID = '11111111-1111-1111-1111-111111111111'; +const LAST_CONTACT_AT = '2026-06-01T10:00:00.000Z'; + +describe('updatePersonLastContactAtIfNewer', () => { + it('should update lastContactAt only when the value is newer or unset', async () => { + const mutationMock = vi.fn().mockResolvedValue({ updatePeople: [] }); + const client = { mutation: mutationMock } as unknown as CoreApiClient; + + await updatePersonLastContactAtIfNewer(client, PERSON_ID, LAST_CONTACT_AT); + + expect(mutationMock).toHaveBeenCalledTimes(1); + expect(mutationMock).toHaveBeenCalledWith({ + updatePeople: { + __args: { + data: { lastContactAt: LAST_CONTACT_AT }, + filter: { + and: [ + { id: { eq: PERSON_ID } }, + { + or: [ + { lastContactAt: { is: 'NULL' } }, + { lastContactAt: { lt: LAST_CONTACT_AT } }, + ], + }, + ], + }, + }, + id: true, + }, + }); + }); +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/utils/update-person-last-contact-at-from-calendar.ts b/packages/twenty-apps/internal/twenty-last-contact/src/utils/update-person-last-contact-at-from-calendar.ts new file mode 100644 index 0000000000..69f8ec0c95 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/utils/update-person-last-contact-at-from-calendar.ts @@ -0,0 +1,44 @@ +import { CoreApiClient } from 'twenty-client-sdk/core'; + +import { updatePersonLastContactAtIfNewer } from 'src/utils/update-person-last-contact-at'; + +export const updatePersonLastContactAtFromCalendar = async ( + client: CoreApiClient, + personId: string, +): Promise => { + const now = new Date().toISOString(); + + const { calendarEventParticipants } = await client.query({ + calendarEventParticipants: { + __args: { + filter: { + personId: { eq: personId }, + calendarEvent: { + startsAt: { lte: now }, + isCanceled: { eq: false }, + }, + }, + orderBy: [{ calendarEvent: { startsAt: 'DescNullsLast' } }], + first: 1, + }, + edges: { + node: { + id: true, + calendarEvent: { + id: true, + startsAt: true, + }, + }, + }, + }, + }); + + const lastContactAt = + calendarEventParticipants?.edges[0]?.node?.calendarEvent?.startsAt ?? null; + + if (!lastContactAt) { + return; + } + + await updatePersonLastContactAtIfNewer(client, personId, lastContactAt); +}; diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/utils/update-person-last-contact-at.ts b/packages/twenty-apps/internal/twenty-last-contact/src/utils/update-person-last-contact-at.ts new file mode 100644 index 0000000000..ffbb55deb0 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/utils/update-person-last-contact-at.ts @@ -0,0 +1,27 @@ +import { CoreApiClient } from 'twenty-client-sdk/core'; + +export const updatePersonLastContactAtIfNewer = async ( + client: CoreApiClient, + personId: string, + lastContactAt: string, +): Promise => { + await client.mutation({ + updatePeople: { + __args: { + data: { lastContactAt }, + filter: { + and: [ + { id: { eq: personId } }, + { + or: [ + { lastContactAt: { is: 'NULL' } }, + { lastContactAt: { lt: lastContactAt } }, + ], + }, + ], + }, + }, + id: true, + }, + }); +}; diff --git a/packages/twenty-apps/internal/twenty-last-contact/src/view-fields/last-contact-at.view-field.ts b/packages/twenty-apps/internal/twenty-last-contact/src/view-fields/last-contact-at.view-field.ts new file mode 100644 index 0000000000..c678085b03 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/src/view-fields/last-contact-at.view-field.ts @@ -0,0 +1,20 @@ +import { + defineViewField, + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, +} from 'twenty-sdk/define'; + +import { + LAST_CONTACT_AT_FIELD_UNIVERSAL_IDENTIFIER, + LAST_CONTACT_AT_VIEW_FIELD_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default defineViewField({ + universalIdentifier: LAST_CONTACT_AT_VIEW_FIELD_UNIVERSAL_IDENTIFIER, + viewUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.views.allPeople + .universalIdentifier, + fieldMetadataUniversalIdentifier: LAST_CONTACT_AT_FIELD_UNIVERSAL_IDENTIFIER, + position: 8, + isVisible: true, + size: 150, +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/tsconfig.json b/packages/twenty-apps/internal/twenty-last-contact/tsconfig.json new file mode 100644 index 0000000000..d574c8c810 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/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-last-contact/tsconfig.spec.json b/packages/twenty-apps/internal/twenty-last-contact/tsconfig.spec.json new file mode 100644 index 0000000000..ea69a6c010 --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/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-last-contact/vitest.config.ts b/packages/twenty-apps/internal/twenty-last-contact/vitest.config.ts new file mode 100644 index 0000000000..055af77dbe --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/vitest.config.ts @@ -0,0 +1,31 @@ +import tsconfigPaths from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020'; +const TWENTY_API_KEY = + process.env.TWENTY_API_KEY ?? + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjQ4OTE0NDk2MDAsImp0aSI6IjIwMjAyMDIwLWY0MDEtNGQ4YS1hNzMxLTY0ZDAwN2MyN2JhZCJ9.bfQjfyN0NEtTCLE_xPyNcwonDzlSXFoP8kdCQTdnuDc'; + +// Make env vars available to globalSetup (test.env only applies to workers) +process.env.TWENTY_API_URL = TWENTY_API_URL; +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, + }, + }, +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/vitest.unit.config.ts b/packages/twenty-apps/internal/twenty-last-contact/vitest.unit.config.ts new file mode 100644 index 0000000000..e0d140015b --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/vitest.unit.config.ts @@ -0,0 +1,14 @@ +import tsconfigPaths from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [ + tsconfigPaths({ + projects: ['tsconfig.spec.json'], + ignoreConfigErrors: true, + }), + ], + test: { + include: ['src/**/*.test.ts'], + }, +}); diff --git a/packages/twenty-apps/internal/twenty-last-contact/yarn.lock b/packages/twenty-apps/internal/twenty-last-contact/yarn.lock new file mode 100644 index 0000000000..56bdb53f5c --- /dev/null +++ b/packages/twenty-apps/internal/twenty-last-contact/yarn.lock @@ -0,0 +1,3283 @@ +# 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 + +"@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 + +"@inquirer/ansi@npm:^2.0.7": + version: 2.0.7 + resolution: "@inquirer/ansi@npm:2.0.7" + checksum: 10c0/a574f97a899f0d9346fa26b528b3f4a9ba6dcb9172288efb6b4314d8486470ed53d2f538200f66a25b843c6e0cbf83688c6d5174a8dc6eca853b291b09609c5a + languageName: node + linkType: hard + +"@inquirer/checkbox@npm:^5.2.1": + version: 5.2.1 + resolution: "@inquirer/checkbox@npm:5.2.1" + dependencies: + "@inquirer/ansi": "npm:^2.0.7" + "@inquirer/core": "npm:^11.2.1" + "@inquirer/figures": "npm:^2.0.7" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/e3a845156c718fbbec228a0e7fd2a4f10775185615eac1f405b3a2bb2ae607dda6baf178fbd6487db0e12e5e33014536e81acefe65de57cd476a7aeaea6fb35d + languageName: node + linkType: hard + +"@inquirer/confirm@npm:^6.1.1": + version: 6.1.1 + resolution: "@inquirer/confirm@npm:6.1.1" + dependencies: + "@inquirer/core": "npm:^11.2.1" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/4684406161c09327df830b4026f3165b31e13831276d215051586408ed434423263b15686393ce95a4b55058c1b7f9b08aa4b66f5ac930b47523fff75051d36f + languageName: node + linkType: hard + +"@inquirer/core@npm:^11.2.1": + version: 11.2.1 + resolution: "@inquirer/core@npm:11.2.1" + dependencies: + "@inquirer/ansi": "npm:^2.0.7" + "@inquirer/figures": "npm:^2.0.7" + "@inquirer/type": "npm:^4.0.7" + cli-width: "npm:^4.1.0" + fast-wrap-ansi: "npm:^0.2.0" + mute-stream: "npm:^3.0.0" + signal-exit: "npm:^4.1.0" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/b5be386cecd9e441ac2f9d3417a6ae1c4658b3ee6cdf5dae791211400f4de158851f81fca2245e2062833716f95366b9e1717770828cb7365e756c16e822f0d2 + languageName: node + linkType: hard + +"@inquirer/editor@npm:^5.2.2": + version: 5.2.2 + resolution: "@inquirer/editor@npm:5.2.2" + dependencies: + "@inquirer/core": "npm:^11.2.1" + "@inquirer/external-editor": "npm:^3.0.3" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/a75e7012aad3ccc3ec84893463ed689924515a6cbaee8e2a475769fb27c12bcf359fcdd5f62e92107fc4be88fd69444c15adf13071982efc140c7015a6604d9a + languageName: node + linkType: hard + +"@inquirer/expand@npm:^5.1.1": + version: 5.1.1 + resolution: "@inquirer/expand@npm:5.1.1" + dependencies: + "@inquirer/core": "npm:^11.2.1" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/4de661a042147759ce351971a4f92010ab66cb76450424e7d8aec5de79b449d14e8751f54f557d0b047180bca2b76707626f4835ee46603c6200139c31b59652 + languageName: node + linkType: hard + +"@inquirer/external-editor@npm:^3.0.3": + version: 3.0.3 + resolution: "@inquirer/external-editor@npm:3.0.3" + dependencies: + chardet: "npm:^2.1.1" + iconv-lite: "npm:^0.7.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/fc24ddbff15f0874db6498c16d2fe153bb19a670d83605f480486d6ff0ea872ae2f32a548fd555797989db09850fa019a6b5e49a8d40cfee0d7deacad17edc1e + languageName: node + linkType: hard + +"@inquirer/figures@npm:^2.0.7": + version: 2.0.7 + resolution: "@inquirer/figures@npm:2.0.7" + checksum: 10c0/e0573dc9ad25fa3628d5164745e52852d8cd832a9918605b7716df2e37a0005a0aaf40b6d81cef2ca09cb708b200e61b82d1dcd17003f572577e233c19a9ec7b + languageName: node + linkType: hard + +"@inquirer/input@npm:^5.1.2": + version: 5.1.2 + resolution: "@inquirer/input@npm:5.1.2" + dependencies: + "@inquirer/core": "npm:^11.2.1" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/9c3614f8d79fc2bec07a088858a58967a162046c9292b0c6f0c6f63396cf391181cfb54bb636f5b3dce8d23924c24ee4a0310183a493c94190e4750218f3744d + languageName: node + linkType: hard + +"@inquirer/number@npm:^4.1.1": + version: 4.1.1 + resolution: "@inquirer/number@npm:4.1.1" + dependencies: + "@inquirer/core": "npm:^11.2.1" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/06210dd70bf89d35242af43009b5e3f76a5f07d6275a9d842bbed61536032f5f349ecba1c20012975d7b0362deb89746d5903e90f21516da10bfd8c2055829a9 + languageName: node + linkType: hard + +"@inquirer/password@npm:^5.1.1": + version: 5.1.1 + resolution: "@inquirer/password@npm:5.1.1" + dependencies: + "@inquirer/ansi": "npm:^2.0.7" + "@inquirer/core": "npm:^11.2.1" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/e4cb3a53b56743808f83958a8fe85738d721599690a4bc7640eaa07fb8f4765bc6f174e40c16efcc8a5958a7169667e240714a706a36e831f9c7a6822cbe8b55 + languageName: node + linkType: hard + +"@inquirer/prompts@npm:^8.5.2": + version: 8.5.2 + resolution: "@inquirer/prompts@npm:8.5.2" + dependencies: + "@inquirer/checkbox": "npm:^5.2.1" + "@inquirer/confirm": "npm:^6.1.1" + "@inquirer/editor": "npm:^5.2.2" + "@inquirer/expand": "npm:^5.1.1" + "@inquirer/input": "npm:^5.1.2" + "@inquirer/number": "npm:^4.1.1" + "@inquirer/password": "npm:^5.1.1" + "@inquirer/rawlist": "npm:^5.3.1" + "@inquirer/search": "npm:^4.2.1" + "@inquirer/select": "npm:^5.2.1" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/253b92e31c6a1f8f00a778eda8196bb53fc931723f7db4a03937f38ccd4d07c987766d4f60b9250b5d90bfe30b04747dfa73927a9f6fb886bd48091ccb202535 + languageName: node + linkType: hard + +"@inquirer/rawlist@npm:^5.3.1": + version: 5.3.1 + resolution: "@inquirer/rawlist@npm:5.3.1" + dependencies: + "@inquirer/core": "npm:^11.2.1" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/a05eb6a16633bd7b32b3b6b2c1f645e6e28d955475b21ba7222e7e66806b1be15be9f91235b2933e8d27e04fdad81ebfb2d64fc1fc0d4b8d82dcd2718817b2b9 + languageName: node + linkType: hard + +"@inquirer/search@npm:^4.2.1": + version: 4.2.1 + resolution: "@inquirer/search@npm:4.2.1" + dependencies: + "@inquirer/core": "npm:^11.2.1" + "@inquirer/figures": "npm:^2.0.7" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/eae9b2d524aae58266f96987696be22ad62f608661d28763c413f2560094d571a39d72a40630d2470f503ad5e6e50d8c574a653cc028bf79b51104fa91f59a67 + languageName: node + linkType: hard + +"@inquirer/select@npm:^5.2.1": + version: 5.2.1 + resolution: "@inquirer/select@npm:5.2.1" + dependencies: + "@inquirer/ansi": "npm:^2.0.7" + "@inquirer/core": "npm:^11.2.1" + "@inquirer/figures": "npm:^2.0.7" + "@inquirer/type": "npm:^4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/3c6d0151b5a29111254a58eaf8b93bb4c476e68b0751526d8bff61a4bea457bdbe782890ae33977c5d2015f436596b5d32a8bb0677bfdf610f5f5998e65f5a92 + languageName: node + linkType: hard + +"@inquirer/type@npm:^4.0.7": + version: 4.0.7 + resolution: "@inquirer/type@npm:4.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/80678ac1c6e19ce309909e4a54a69adc95697ea3abc2cb92f17b1bc52f4caadbcb4003ae7339fb5a70c0d36d3bde975e1bb4450069662f41c953a0d28695bb70 + 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 + +"@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.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.61.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-android-arm64@npm:4.61.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.61.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.61.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-arm64@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.61.0" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-x64@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-freebsd-x64@npm:4.61.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.61.0" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.61.0" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.61.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.61.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.61.0" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-musl@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.61.0" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-gnu@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.61.0" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-musl@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.61.0" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.61.0" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.61.0" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.61.0" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.61.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.61.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-openbsd-x64@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-openbsd-x64@npm:4.61.0" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.61.0" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.61.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.61.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.61.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.61.0": + version: 4.61.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.61.0" + conditions: os=win32 & cpu=x64 + 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.9, @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/node@npm:*": + version: 25.9.1 + resolution: "@types/node@npm:25.9.1" + dependencies: + undici-types: "npm:>=7.24.0 <7.24.7" + checksum: 10c0/9a04682842bebbcf21a1779dfeab9aa733d7bd7bbc0a0edb641ab3a9a3d43eac543225acf669c334f458f1956443ebc072bc3c72840c543b8b356cab5c82d456 + 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.15 + resolution: "@types/react@npm:19.2.15" + dependencies: + csstype: "npm:^3.2.2" + checksum: 10c0/b554eab715bb14e581f0ae60e5cefe91e1a5e06c31022b543a9806cf224aa056f21e4fb46208e46eb934d86ca0b247ebc82377192a0dead303cb28b8764c6e67 + 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.6": + version: 3.2.6 + resolution: "@vitest/expect@npm:3.2.6" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:3.2.6" + "@vitest/utils": "npm:3.2.6" + chai: "npm:^5.2.0" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/bb51fa6a1f0740b3ee994347bc5067c8a17c2f3dea56b76a8c2c328885cdbfbafbb99b0b94b8166dc605eedbb9f467d37a6a0e0c81855eb18e232417cca4ccbd + languageName: node + linkType: hard + +"@vitest/mocker@npm:3.2.6": + version: 3.2.6 + resolution: "@vitest/mocker@npm:3.2.6" + dependencies: + "@vitest/spy": "npm:3.2.6" + 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/0429d72e52ca1cc25d7ac44fbc251d0486974d8e0e59860c605d72456c0a17fa1d464b2a5e34690c17afcc7be562d7c22595f53503244a858f1f5903998da100 + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:3.2.6, @vitest/pretty-format@npm:^3.2.6": + version: 3.2.6 + resolution: "@vitest/pretty-format@npm:3.2.6" + dependencies: + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/9fc2887ef4206c90392de4e205d0109add35382446914d0124c53d1da327f49b9e9535fb336cb260394c176e4bb14b1b3aba0a42ab12b0f8b95034ccd4fed4eb + languageName: node + linkType: hard + +"@vitest/runner@npm:3.2.6": + version: 3.2.6 + resolution: "@vitest/runner@npm:3.2.6" + dependencies: + "@vitest/utils": "npm:3.2.6" + pathe: "npm:^2.0.3" + strip-literal: "npm:^3.0.0" + checksum: 10c0/bfc552ee2424ec62e4d6c075d000348a8187eb1be7ce9ae4673139c2f4a71e271ac15bef4d6af27cb5a29f4776dd437bea9d5819f62f0b5ece3c6dd857fa300d + languageName: node + linkType: hard + +"@vitest/snapshot@npm:3.2.6": + version: 3.2.6 + resolution: "@vitest/snapshot@npm:3.2.6" + dependencies: + "@vitest/pretty-format": "npm:3.2.6" + magic-string: "npm:^0.30.17" + pathe: "npm:^2.0.3" + checksum: 10c0/b8f73cc2bc50ca6cd013e2dae1f531aa2132d053e52d2055da8544290e6e8a92dd06f8cf07e8fce15137f27e8156f2936eb6bd9fa63c76e6f6a9813cf0820022 + languageName: node + linkType: hard + +"@vitest/spy@npm:3.2.6": + version: 3.2.6 + resolution: "@vitest/spy@npm:3.2.6" + dependencies: + tinyspy: "npm:^4.0.3" + checksum: 10c0/b4b022546523168f361cd640dff68feb9709b259aacc05e12055a4f278d07e58fbb280ae70454425199f91e62729b03f0a16bdfe880c5a0b1b10c02bc334fc30 + languageName: node + linkType: hard + +"@vitest/utils@npm:3.2.6": + version: 3.2.6 + resolution: "@vitest/utils@npm:3.2.6" + dependencies: + "@vitest/pretty-format": "npm:3.2.6" + loupe: "npm:^3.1.4" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/452da757082ebcadf43ce697bf4077d546ea06e094763969440c3cb0d7bb950be0f69d6dbdab297cd0307bdf956294e20b0184d0218a4ef942d3d253995d44ed + 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:^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:^6.2.2": + version: 6.2.2 + resolution: "ansi-regex@npm:6.2.2" + checksum: 10c0/05d4acb1d2f59ab2cf4b794339c7b168890d44dda4bf0ce01152a8da0213aca207802f930442ce8cd22d7a92f44907664aac6508904e75e038fa944d2601b30f + 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 + +"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.16.0": + version: 1.17.0 + resolution: "axios@npm:1.17.0" + 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/c4fa19ff3a3a63bde48beec03ad816b133b9a6385cccffffe172577ab18c6a70e299280d57f12c80c867fe25df41f92cb91d3a8258708a6d2be3e9e085f92650 + 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 + +"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 + +"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:^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:^2.1.1": + version: 2.1.1 + resolution: "chardet@npm:2.1.1" + checksum: 10c0/d8391dd412338442b3de0d3a488aa9327f8bcf74b62b8723d6bd0b85c4084d50b731320e0a7c710edb1d44de75969995d2784b80e4c13b004a6c7a0db4c6e793 + 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:^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:^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 + +"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 + +"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 + +"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 + +"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 + +"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 + +"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 + +"emoji-regex@npm:^10.3.0": + version: 10.6.0 + resolution: "emoji-regex@npm:10.6.0" + checksum: 10c0/1e4aa097bb007301c3b4b1913879ae27327fdc48e93eeefefe3b87e495eb33c5af155300be951b4349ff6ac084f4403dc9eff970acba7c1c572d89396a9a32d7 + 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.2 + resolution: "es-object-atoms@npm:1.1.2" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10c0/1772861f094f739d6f41b579cfb9a18579daffeb434552a370a5fbef50a32d22227e27b63fdbb757b7ddd429d1b42fe52ccae7966d9302a2ec221b6f1b41bbc4 + 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.47.0 + resolution: "es-toolkit@npm:1.47.0" + dependenciesMeta: + "@trivago/prettier-plugin-sort-imports@4.3.0": + unplugged: true + prettier-plugin-sort-re-exports@0.0.1: + unplugged: true + vitepress-plugin-sandpack@1.1.4: + unplugged: true + checksum: 10c0/6edc9709fccc409fa4adddd318971d8a18335de9225f2dc99021aacba44fe66a2437a831923589c643865caab261a087bd974338294a60bb5a74932caa102901 + 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:^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 + +"fast-string-truncated-width@npm:^3.0.2": + version: 3.0.3 + resolution: "fast-string-truncated-width@npm:3.0.3" + checksum: 10c0/043b8663397d14a3880ce4f3407bcda60b40db9bbeafe62863a35d1f9c69ea17c8da3fcd72de235553e6c9cd053128cde9e24ca0d4a7463208f48db3cd23d981 + languageName: node + linkType: hard + +"fast-string-width@npm:^3.0.2": + version: 3.0.2 + resolution: "fast-string-width@npm:3.0.2" + dependencies: + fast-string-truncated-width: "npm:^3.0.2" + checksum: 10c0/c8822d175315bb353ebe782b65214ac53b13e3bf704e03b132ea7bdfa8de6a636375b3ab7a4097545393d109381c37c4f387c72a462c90b61412dbc4632f39a7 + languageName: node + linkType: hard + +"fast-wrap-ansi@npm:^0.2.0": + version: 0.2.2 + resolution: "fast-wrap-ansi@npm:0.2.2" + dependencies: + fast-string-width: "npm:^3.0.2" + checksum: 10c0/1aa7be4f7cb86f4bdb14691cb6bcc0b8df8b3b89df142ade3ae1602332dcf6f990cd750a923cd581ca0847808cb4ec1aa5afaafa7a72f849e87a2a62c98fa370 + 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 + +"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 + +"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-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.6": + 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 + +"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.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.8.1": + version: 16.14.0 + resolution: "graphql@npm:16.14.0" + checksum: 10c0/baaa368fcfeb7bf2cdf94b9f31bf916e97eaa9e7e82148a7046f31cbd49b760b38190f22393b024cbdd9ca0f4f46287515de0136b6a0cc164074b36ad7b50618 + 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.4 + resolution: "hasown@npm:2.0.4" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/2d8de939e270b70618f8cebb69746620db10617dbb495bc66ddad326955ea24d3ca4af133aff3eb7c1853e0218f867bc2b050ec26fe02e3aea58f880ffc5e506 + 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.7.2": + version: 0.7.2 + resolution: "iconv-lite@npm:0.7.2" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10c0/3c228920f3bd307f56bf8363706a776f4a060eb042f131cd23855ceca962951b264d0997ab38a1ad340e1c5df8499ed26e1f4f0db6b2a2ad9befaff22f14b722 + 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 + +"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:^14.0.0": + version: 14.0.2 + resolution: "inquirer@npm:14.0.2" + dependencies: + "@inquirer/ansi": "npm:^2.0.7" + "@inquirer/core": "npm:^11.2.1" + "@inquirer/prompts": "npm:^8.5.2" + "@inquirer/type": "npm:^4.0.7" + mute-stream: "npm:^3.0.0" + run-async: "npm:^4.0.6" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/31ec80b7d599dcb19568540d694865b1da116f701b75503a23ed38189d96fca70eb1fb6ccb1dbd994f6f79d407d51dc1a94d06dcef5370644ce736792414781b + 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-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 + +"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 + +"last-contacted-at@workspace:.": + version: 0.0.0-use.local + resolution: "last-contacted-at@workspace:." + dependencies: + "@types/node": "npm:^24.7.2" + "@types/react": "npm:^19.0.0" + oxlint: "npm:^0.16.0" + react: "npm:^19.0.0" + react-dom: "npm:^19.0.0" + twenty-client-sdk: "npm:^2.10.1" + twenty-sdk: "npm:^2.10.1" + typescript: "npm:^5.9.3" + vite-tsconfig-paths: "npm:^4.2.1" + vitest: "npm:^3.1.1" + languageName: unknown + linkType: soft + +"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 + +"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 + +"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:^2.1.0": + version: 2.1.0 + resolution: "mimic-fn@npm:2.1.0" + checksum: 10c0/b26f5479d7ec6cc2bce275a08f146cf78f5e7b661b18114e2506dd91ec7ec47e7a25bf4360e5438094db0560bcc868079fb3b1fb3892b833c1ecbf63f80c95a4 + 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 + +"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:^3.0.0": + version: 3.0.0 + resolution: "mute-stream@npm:3.0.0" + checksum: 10c0/12cdb36a101694c7a6b296632e6d93a30b74401873cf7507c88861441a090c71c77a58f213acadad03bc0c8fa186639dec99d68a14497773a8744320c136e701 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.12": + version: 3.3.12 + resolution: "nanoid@npm:3.3.12" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/ba142b7b39e11e80c16dd74b0365d407880c87c1cf7e1480956981ae940ee36060fa5b6f092cd1e315184dd19244c657bd017d03327bd3c62247d691c5e8edfb + 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 + +"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 + +"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 + +"patch-console@npm:^2.0.0": + version: 2.0.0 + resolution: "patch-console@npm:2.0.0" + checksum: 10c0/486602591a0af7af8d4c76d8eea42cad32b6de7200488819c6383c75e43733ca7bdc80e30f2e68ce05f06a1607cce1683a1706c6672ca27dada1921b366e8f1c + 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:^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.15 + resolution: "postcss@npm:8.5.15" + dependencies: + nanoid: "npm:^3.3.12" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/7f2e63ae22fbe43aace1bf652bd99da4e90737c64194d49e51ddc9cd0f9e51ff2861a7d734379b494deffa03a880a5c65eec70bc29ee9ebaa7136dde3eee8f31 + languageName: node + linkType: hard + +"preact@npm:^10.28.3": + version: 10.29.2 + resolution: "preact@npm:10.29.2" + checksum: 10c0/2ee161274e475804524608d42bba8c79920636b322293248796a9e5bf4c3332336f8c0eca1b1566ed09cdee95e408eb20b5b7613d95820ea6b6eb8fc090ef60a + languageName: node + linkType: hard + +"prettier@npm:^2.8.8": + 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 + +"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 + +"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 + +"rollup@npm:^4.43.0": + version: 4.61.0 + resolution: "rollup@npm:4.61.0" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.61.0" + "@rollup/rollup-android-arm64": "npm:4.61.0" + "@rollup/rollup-darwin-arm64": "npm:4.61.0" + "@rollup/rollup-darwin-x64": "npm:4.61.0" + "@rollup/rollup-freebsd-arm64": "npm:4.61.0" + "@rollup/rollup-freebsd-x64": "npm:4.61.0" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.61.0" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.61.0" + "@rollup/rollup-linux-arm64-gnu": "npm:4.61.0" + "@rollup/rollup-linux-arm64-musl": "npm:4.61.0" + "@rollup/rollup-linux-loong64-gnu": "npm:4.61.0" + "@rollup/rollup-linux-loong64-musl": "npm:4.61.0" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.61.0" + "@rollup/rollup-linux-ppc64-musl": "npm:4.61.0" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.61.0" + "@rollup/rollup-linux-riscv64-musl": "npm:4.61.0" + "@rollup/rollup-linux-s390x-gnu": "npm:4.61.0" + "@rollup/rollup-linux-x64-gnu": "npm:4.61.0" + "@rollup/rollup-linux-x64-musl": "npm:4.61.0" + "@rollup/rollup-openbsd-x64": "npm:4.61.0" + "@rollup/rollup-openharmony-arm64": "npm:4.61.0" + "@rollup/rollup-win32-arm64-msvc": "npm:4.61.0" + "@rollup/rollup-win32-ia32-msvc": "npm:4.61.0" + "@rollup/rollup-win32-x64-gnu": "npm:4.61.0" + "@rollup/rollup-win32-x64-msvc": "npm:4.61.0" + "@types/estree": "npm:1.0.9" + 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/55d70077c608e4979a4a9aaa83231e29f77f26014930a812cc17142d5aea1b2b81883cd2c43cd2a23fbf8eebc70a9b0683e4c982c385b8830a4b9a0e45f6e59d + languageName: node + linkType: hard + +"run-async@npm:^4.0.6": + version: 4.0.6 + resolution: "run-async@npm:4.0.6" + checksum: 10c0/3e512c689d356238a06a59839deddeb09aec23bc66f780fe970fcf12b64bfc00c6880e9530ea22b8cf88a927145561f5a43343d8be87166e849ec0daaa3d4cf4 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3.0.0": + 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.1 + resolution: "semver@npm:7.8.1" + bin: + semver: bin/semver.js + checksum: 10c0/92d6871d6347e1f99d0ba396a70f2545ccf2a032cda3d378fa0699edf7506b5c6d266aed55c8b88e72bd91a30d2351e4f39db479375374430fcdc4b58f4e3c1a + 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 + +"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:^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:^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 + +"symbol-observable@npm:^1.0.4": + 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.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + 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 + +"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.3": + version: 1.14.1 + resolution: "tslib@npm:1.14.1" + checksum: 10c0/69ae09c49eea644bc5ebe1bca4fa4cc2c82b7b3e02f43b84bd891504edf66dbc6b2ec0eef31a957042de2269139e4acff911e6d186a258fb14069cd7f6febce2 + languageName: node + linkType: hard + +"tslib@npm:^2.0.0": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + +"twenty-client-sdk@npm:2.10.1, twenty-client-sdk@npm:^2.10.1": + version: 2.10.1 + resolution: "twenty-client-sdk@npm:2.10.1" + dependencies: + "@genql/runtime": "npm:^2.10.0" + esbuild: "npm:^0.28.0" + graphql: "npm:^16.8.1" + lodash: "npm:^4.17.21" + prettier: "npm:^2.8.8" + checksum: 10c0/7ca0db8f8f769bc39d4d2c51fc23cf9b5731d24c4bafa5fd804a67ac7040fd51b1ef47e52849773503d92a1c9ecc1730e32c26929f0d51568c3d510f7873563b + languageName: node + linkType: hard + +"twenty-sdk@npm:^2.10.1": + version: 2.10.1 + resolution: "twenty-sdk@npm:2.10.1" + dependencies: + "@sniptt/guards": "npm:^0.2.0" + axios: "npm:^1.16.0" + chalk: "npm:^5.3.0" + chokidar: "npm:^4.0.0" + commander: "npm:^12.0.0" + esbuild: "npm:^0.25.0" + graphql: "npm:^16.8.1" + graphql-sse: "npm:^2.5.4" + ink: "npm:^6.8.0" + inquirer: "npm:^14.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.10.1" + typescript: "npm:^5.9.3" + uuid: "npm:^13.0.0" + bin: + twenty: dist/cli.cjs + checksum: 10c0/8cfe513daebd4a835573543f1d3e6d5474316102579b308daf859f5fa04f39126cc0f87235069e43e0b54953502e580d1fd40fc486161ca5798808aec04b1cdc + languageName: node + linkType: hard + +"type-fest@npm:^5.4.1": + version: 5.7.0 + resolution: "type-fest@npm:5.7.0" + dependencies: + tagged-tag: "npm:^1.0.0" + checksum: 10c0/f71ed17b753649421e419db8cc2e140f930333a1467b1d9cca2e0e4052900fd442f2360bae73f3a6bf9340d949ac46d9a1598c709b4c8089272e7624df9c8716 + languageName: node + linkType: hard + +"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.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:~7.16.0": + version: 7.16.0 + resolution: "undici-types@npm:7.16.0" + checksum: 10c0/3033e2f2b5c9f1504bdc5934646cb54e37ecaca0f9249c983f7b1fc2e87c6d18399ebb05dc7fd5419e02b2e915f734d872a65da2e3eeed1813951c427d33cc9a + languageName: node + linkType: hard + +"undici@npm:^6.25.0": + version: 6.26.0 + resolution: "undici@npm:6.26.0" + checksum: 10c0/cf2b4caf58c33d6582970991290cc7a6486d6e738845f25dcdd16952d708ec844815c6d30362919764fcaf30f719891289341f1ada496f003ce2700310453a47 + 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 + +"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 + +"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": + version: 7.3.5 + resolution: "vite@npm:7.3.5" + 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/4ad700649ed2ebd1e726d32f3f6e41eecbec4e29bcb5977805f3d43a5659280518aa431b0fc61adc1879df4fe1978d7cfc7dbbe54cdf014022385052967721e8 + languageName: node + linkType: hard + +"vitest@npm:^3.1.1": + version: 3.2.6 + resolution: "vitest@npm:3.2.6" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/expect": "npm:3.2.6" + "@vitest/mocker": "npm:3.2.6" + "@vitest/pretty-format": "npm:^3.2.6" + "@vitest/runner": "npm:3.2.6" + "@vitest/snapshot": "npm:3.2.6" + "@vitest/spy": "npm:3.2.6" + "@vitest/utils": "npm:3.2.6" + 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.6 + "@vitest/ui": 3.2.6 + 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/ea222dcd7310188479e37dbea4fbcbdcdb8db97f952b4813e7e7b370b329485c2de4622551e61076ffd75d4d811a6800a0da1e31520067614c51528f26704758 + 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@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:^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 + +"ws@npm:^5.2.0 || ^6.0.0 || ^7.0.0": + version: 7.5.11 + resolution: "ws@npm:7.5.11" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/7972670b676fb1ccba73b0899ca3c2e04e8c2075629c2614cced7f556536f96a672bbf4619fc5a06c8b8720bb839a47ca88c69c95dc14c9c61a99fbecba1c866 + languageName: node + linkType: hard + +"ws@npm:^6.1.4": + version: 6.2.4 + resolution: "ws@npm:6.2.4" + dependencies: + async-limiter: "npm:~1.0.0" + checksum: 10c0/5c2b9474164f9cb68c7776a1d10b0461c186f3a69bffb1028fca33eba5ab7206a09173fb0b311d6c5a81c8cf148406f8deb0b7d899542ab8ca67407d99717dad + languageName: node + linkType: hard + +"ws@npm:^8.18.0": + version: 8.21.0 + resolution: "ws@npm:8.21.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/ef4a243476283fc49bc7550966c4af4aa0eef56273837211e700de3b664e08604a760cdddcb5ba43c049140e74ccfec5b0ee0bb439e08c2adf9138902fdde5f9 + 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 + +"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