diff --git a/packages/twenty-apps/.gitignore b/packages/twenty-apps/.gitignore new file mode 100644 index 00000000000..86d4c2dd380 --- /dev/null +++ b/packages/twenty-apps/.gitignore @@ -0,0 +1 @@ +generated diff --git a/packages/twenty-apps/hello-world/.env.example b/packages/twenty-apps/hello-world/.env.example index cfbb8aab89d..1e91e5f5051 100644 --- a/packages/twenty-apps/hello-world/.env.example +++ b/packages/twenty-apps/hello-world/.env.example @@ -1 +1,2 @@ -TWENTY_API_KEY= +TWENTY_API_KEY= +TWENTY_API_URL= diff --git a/packages/twenty-apps/hello-world/README.md b/packages/twenty-apps/hello-world/README.md index 518c0c80d0a..c3d8d756761 100644 --- a/packages/twenty-apps/hello-world/README.md +++ b/packages/twenty-apps/hello-world/README.md @@ -18,7 +18,7 @@ This example will gradually gain complexity and capabilities as the twenty-cli m cp .env.example .env ``` -- replace `` with your apiKey +- replace `` and `` accordingly ```bash twenty auth login diff --git a/packages/twenty-apps/hello-world/application.config.ts b/packages/twenty-apps/hello-world/application.config.ts index 7ca7f5bb78f..64e8c97f8af 100644 --- a/packages/twenty-apps/hello-world/application.config.ts +++ b/packages/twenty-apps/hello-world/application.config.ts @@ -11,6 +11,11 @@ const config: ApplicationConfig = { description: 'Twenty API Key', isSecret: true, }, + TWENTY_API_URL: { + universalIdentifier: 'ef8ab489-e68a-4841-b402-261f440e6185', + description: 'Twenty API Url', + isSecret: false, + }, }, }; diff --git a/packages/twenty-apps/hello-world/package.json b/packages/twenty-apps/hello-world/package.json index 1954a9fcab2..46408575a29 100644 --- a/packages/twenty-apps/hello-world/package.json +++ b/packages/twenty-apps/hello-world/package.json @@ -9,7 +9,6 @@ }, "packageManager": "yarn@4.9.2", "dependencies": { - "axios": "^1.12.2", "twenty-sdk": "0.0.5" }, "devDependencies": { diff --git a/packages/twenty-apps/hello-world/src/actions/create-new-post-card.ts b/packages/twenty-apps/hello-world/src/actions/create-new-post-card.ts index cc3ffec2c43..6f7b7ae3218 100644 --- a/packages/twenty-apps/hello-world/src/actions/create-new-post-card.ts +++ b/packages/twenty-apps/hello-world/src/actions/create-new-post-card.ts @@ -1,25 +1,27 @@ -import axios from 'axios'; import { type ServerlessFunctionConfig } from 'twenty-sdk/application'; +import { createClient } from '../../generated'; -export const main = async (params: { recipient: string }): Promise => { - const { recipient } = params; - - const options = { - method: 'POST', - url: 'http://localhost:3000/rest/postCards', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${process.env.TWENTY_API_KEY}`, - }, - data: { name: recipient ?? 'Unknown' }, - }; - +export const main = async (params: { recipient?: string }) => { try { - const { data } = await axios.request(options); - - console.log(`New post card to "${recipient}" created`); - - return data; + const client = createClient({ + url: `${process.env.TWENTY_API_URL}/graphql`, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${process.env.TWENTY_API_KEY}`, + }, + }); + const createPostCard = await client.mutation({ + createPostCard: { + __args: { + data: { + name: params.recipient ?? 'Hello-world', + }, + }, + name: true, + id: true, + }, + }); + return createPostCard; } catch (error) { console.error(error); throw error; diff --git a/packages/twenty-apps/hello-world/tsconfig.json b/packages/twenty-apps/hello-world/tsconfig.json index 2750650a88b..0164e2b1b74 100644 --- a/packages/twenty-apps/hello-world/tsconfig.json +++ b/packages/twenty-apps/hello-world/tsconfig.json @@ -19,12 +19,7 @@ "lib": ["es2020", "dom"], "skipLibCheck": true, "skipDefaultLibCheck": true, - "resolveJsonModule": true + "resolveJsonModule": true, }, - "exclude": [ - "node_modules", - "dist", - "**/*.test.ts", - "**/*.spec.ts" - ] + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"] } diff --git a/packages/twenty-apps/hello-world/yarn.lock b/packages/twenty-apps/hello-world/yarn.lock index 429a933e687..44e135d0e58 100644 --- a/packages/twenty-apps/hello-world/yarn.lock +++ b/packages/twenty-apps/hello-world/yarn.lock @@ -224,7 +224,6 @@ __metadata: dependencies: "@types/node": "npm:^24.7.2" axios: "npm:^1.12.2" - twenty-sdk: "npm:^0.0.4" languageName: unknown linkType: soft @@ -258,13 +257,6 @@ __metadata: languageName: node linkType: hard -"twenty-sdk@npm:^0.0.4": - version: 0.0.4 - resolution: "twenty-sdk@npm:0.0.4" - checksum: 10c0/550f1d85bf0701396c9dd2d4c6bc55ba1b067fce13636f8540eec60ab6a4257c6d7cd86cb3f62e0974bf99467bc31270d92b17b9681a1b7a6281b7ef97224080 - languageName: node - linkType: hard - "undici-types@npm:~7.16.0": version: 7.16.0 resolution: "undici-types@npm:7.16.0" diff --git a/packages/twenty-cli/README.md b/packages/twenty-cli/README.md index 0a0303d5fc0..e0a8ff7d1d3 100644 --- a/packages/twenty-cli/README.md +++ b/packages/twenty-cli/README.md @@ -36,7 +36,7 @@ yarn add axios # Start dev mode: automatically syncs changes to your Twenty workspace, so you can test new functions/objects instantly. twenty app dev -# Or use one time sync +# Or use one time sync (also generates SDK automatically) twenty app sync # List all available commands @@ -82,5 +82,5 @@ Our team reviews contributions for quality, security, and reusability before mer ## Contributing -- see our [Hacktoberfest 2025 notion page](https://twentycrm.notion.site/Hacktoberfest-27711d8417038037a149d4638a9cc510) +- see our [Hacktoberfest 2025 notion page](https://twentycrm.notion.site/Hacktoberfest-27711d8417038037a149d4638a9cc510) - our [Discord](https://discord.gg/cx5n4Jzs57) diff --git a/packages/twenty-cli/package.json b/packages/twenty-cli/package.json index d5642105ef3..68fcc6ea74d 100644 --- a/packages/twenty-cli/package.json +++ b/packages/twenty-cli/package.json @@ -1,6 +1,6 @@ { "name": "twenty-cli", - "version": "0.2.1", + "version": "0.2.2", "description": "Command-line interface for Twenty application development", "main": "dist/cli.js", "bin": { @@ -25,6 +25,7 @@ ], "license": "AGPL-3.0", "dependencies": { + "@genql/cli": "^3.0.3", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "axios": "^1.6.0", @@ -33,6 +34,7 @@ "commander": "^12.0.0", "dotenv": "^16.4.0", "fs-extra": "^11.2.0", + "graphql": "^16.8.1", "inquirer": "^10.0.0", "jsonc-parser": "^3.2.0", "lodash.camelcase": "^4.3.0", @@ -51,7 +53,6 @@ "@types/node": "^20.0.0", "jest": "^29.5.0", "tsx": "^4.7.0", - "twenty-sdk": "workspace:*", "wait-on": "^7.2.0" }, "engines": { diff --git a/packages/twenty-cli/project.json b/packages/twenty-cli/project.json index f01f9325c69..392c97eca1a 100644 --- a/packages/twenty-cli/project.json +++ b/packages/twenty-cli/project.json @@ -10,7 +10,8 @@ "options": { "cwd": "packages/twenty-cli", "commands": ["rimraf dist", "tsc --project tsconfig.lib.json"] - } + }, + "dependsOn": ["^build"] }, "build": { "executor": "nx:run-commands", @@ -22,7 +23,7 @@ "cp -R src/constants/schemas dist/constants" ] }, - "dependsOn": ["^build", "before-build"] + "dependsOn": ["before-build"] }, "dev": { "executor": "nx:run-commands", @@ -45,7 +46,8 @@ "options": { "cwd": "packages/twenty-cli", "command": "tsc --noEmit --project tsconfig.lib.json" - } + }, + "dependsOn": ["build"] }, "lint": { "options": { diff --git a/packages/twenty-cli/src/commands/app-dev.command.ts b/packages/twenty-cli/src/commands/app-dev.command.ts index 304004fa971..21e185e9820 100644 --- a/packages/twenty-cli/src/commands/app-dev.command.ts +++ b/packages/twenty-cli/src/commands/app-dev.command.ts @@ -1,11 +1,10 @@ import chalk from 'chalk'; import * as chokidar from 'chokidar'; -import { ApiService } from '../services/api.service'; import { CURRENT_EXECUTION_DIRECTORY } from '../constants/current-execution-directory'; -import { loadManifest } from '../utils/load-manifest'; +import { AppSyncCommand } from './app-sync.command'; export class AppDevCommand { - private apiService = new ApiService(); + private syncCommand = new AppSyncCommand(); async execute(options: { appPath?: string; @@ -18,13 +17,7 @@ export class AppDevCommand { this.logStartupInfo(appPath, debounceMs); - const { manifest, packageJson, yarnLock } = await loadManifest(appPath); - - await this.apiService.syncApplication({ - manifest, - packageJson, - yarnLock, - }); + await this.syncCommand.execute(appPath); const watcher = this.setupFileWatcher(appPath, debounceMs); @@ -64,13 +57,7 @@ export class AppDevCommand { timeout = setTimeout(async () => { console.log(chalk.blue('🔄 Changes detected, syncing...')); - const { manifest, packageJson, yarnLock } = await loadManifest(appPath); - - await this.apiService.syncApplication({ - manifest, - packageJson, - yarnLock, - }); + await this.syncCommand.execute(appPath); console.log( chalk.gray('👀 Watching for changes... (Press Ctrl+C to stop)'), diff --git a/packages/twenty-cli/src/commands/app-generate.command.ts b/packages/twenty-cli/src/commands/app-generate.command.ts new file mode 100644 index 00000000000..62832ec0df3 --- /dev/null +++ b/packages/twenty-cli/src/commands/app-generate.command.ts @@ -0,0 +1,19 @@ +import chalk from 'chalk'; +import { GenerateService } from '../services/generate.service'; +import { CURRENT_EXECUTION_DIRECTORY } from '../constants/current-execution-directory'; + +export class AppGenerateCommand { + private generateService = new GenerateService(); + + async execute(appPath: string = CURRENT_EXECUTION_DIRECTORY) { + try { + await this.generateService.generateClient(appPath); + } catch (error) { + console.error( + chalk.red('Generate Twenty client failed:'), + error instanceof Error ? error.message : error, + ); + throw error; + } + } +} diff --git a/packages/twenty-cli/src/commands/app-sync.command.ts b/packages/twenty-cli/src/commands/app-sync.command.ts index 492f905f218..fea5fe42513 100644 --- a/packages/twenty-cli/src/commands/app-sync.command.ts +++ b/packages/twenty-cli/src/commands/app-sync.command.ts @@ -3,11 +3,12 @@ import { CURRENT_EXECUTION_DIRECTORY } from '../constants/current-execution-dire import { ApiService } from '../services/api.service'; import { ApiResponse } from '../types/config.types'; import { loadManifest } from '../utils/load-manifest'; +import { GenerateService } from '../services/generate.service'; export class AppSyncCommand { private apiService = new ApiService(); + private generateService = new GenerateService(); - // TODO improve typing async execute( appPath: string = CURRENT_EXECUTION_DIRECTORY, ): Promise> { @@ -16,21 +17,7 @@ export class AppSyncCommand { console.log(chalk.gray(`📁 App Path: ${appPath}`)); console.log(''); - const { manifest, packageJson, yarnLock } = await loadManifest(appPath); - - const result = await this.apiService.syncApplication({ - manifest, - packageJson, - yarnLock, - }); - - if (!result.success) { - console.error(chalk.red('❌ Sync failed:'), result.error); - } else { - console.log(chalk.green('✅ Application synced successfully')); - } - - return result; + return await this.synchronize({ appPath }); } catch (error) { console.error( chalk.red('Sync failed:'), @@ -39,4 +26,38 @@ export class AppSyncCommand { throw error; } } + + private async synchronize({ appPath }: { appPath: string }) { + const { manifest, packageJson, yarnLock, isTwentyClientUsed } = + await loadManifest(appPath); + + let serverlessSyncResult = await this.apiService.syncApplication({ + manifest, + packageJson, + yarnLock, + }); + + if (isTwentyClientUsed) { + await this.generateService.generateClient(appPath); + + const { manifest: manifestWithClient } = await loadManifest(appPath); + + serverlessSyncResult = await this.apiService.syncApplication({ + manifest: manifestWithClient, + packageJson, + yarnLock, + }); + } + + if (!serverlessSyncResult.success) { + console.error( + chalk.red('❌ Serverless functions Sync failed:'), + serverlessSyncResult.error, + ); + } else { + console.log(chalk.green('✅ Serverless functions synced successfully')); + } + + return serverlessSyncResult; + } } diff --git a/packages/twenty-cli/src/commands/app.command.ts b/packages/twenty-cli/src/commands/app.command.ts index 7df185b2e62..b09cdea1762 100644 --- a/packages/twenty-cli/src/commands/app.command.ts +++ b/packages/twenty-cli/src/commands/app.command.ts @@ -10,6 +10,7 @@ import { AppDevCommand } from './app-dev.command'; import { AppInitCommand } from './app-init.command'; import { AppSyncCommand } from './app-sync.command'; import { formatPath } from '../utils/format-path'; +import { AppGenerateCommand } from './app-generate.command'; export class AppCommand { private devCommand = new AppDevCommand(); @@ -17,6 +18,7 @@ export class AppCommand { private deleteCommand = new AppDeleteCommand(); private initCommand = new AppInitCommand(); private addCommand = new AppAddCommand(); + private generateCommand = new AppGenerateCommand(); getCommand(): Command { const appCommand = new Command('app'); @@ -96,6 +98,13 @@ export class AppCommand { await this.addCommand.execute(entityType as SyncableEntity); }); + appCommand + .command('generate [outputPath]') + .description('Generate Twenty client') + .action(async (appPath?: string) => { + await this.generateCommand.execute(formatPath(appPath)); + }); + return appCommand; } } diff --git a/packages/twenty-cli/src/constants/base-application-project/tsconfig.json b/packages/twenty-cli/src/constants/base-application-project/tsconfig.json index 9054b045752..92a2a7a90ab 100644 --- a/packages/twenty-cli/src/constants/base-application-project/tsconfig.json +++ b/packages/twenty-cli/src/constants/base-application-project/tsconfig.json @@ -20,12 +20,8 @@ "lib": ["es2020", "dom"], "skipLibCheck": true, "skipDefaultLibCheck": true, - "resolveJsonModule": true + "resolveJsonModule": true, }, - "exclude": [ - "node_modules", - "dist", - "**/*.test.ts", - "**/*.spec.ts" - ] + + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"] } diff --git a/packages/twenty-cli/src/services/api.service.ts b/packages/twenty-cli/src/services/api.service.ts index 5670ac905d5..d26e3898413 100644 --- a/packages/twenty-cli/src/services/api.service.ts +++ b/packages/twenty-cli/src/services/api.service.ts @@ -6,6 +6,11 @@ import { type PackageJson, } from '../types/config.types'; import { ConfigService } from './config.service'; +import { + buildClientSchema, + getIntrospectionQuery, + printSchema, +} from 'graphql/index'; export class ApiService { private client: AxiosInstance; @@ -188,4 +193,48 @@ export class ApiService { throw error; } } + + async getSchema(): Promise> { + try { + const introspectionQuery = getIntrospectionQuery(); + + const response = await this.client.post( + '/graphql', + { + query: introspectionQuery, + }, + { + headers: { + 'Content-Type': 'application/json', + Accept: '*/*', + }, + }, + ); + + if (response.data.errors) { + return { + success: false, + error: `GraphQL introspection errors: ${JSON.stringify(response.data.errors)}`, + }; + } + + const schema = buildClientSchema(response.data.data); + + return { + success: true, + data: printSchema(schema), + message: 'Successfully load schema', + }; + } catch (error) { + if (axios.isAxiosError(error) && error.response) { + return { + success: false, + error: + error.response.data.errors[0]?.message || + 'Failed to load graphql Schema', + }; + } + throw error; + } + } } diff --git a/packages/twenty-cli/src/services/generate.service.ts b/packages/twenty-cli/src/services/generate.service.ts new file mode 100644 index 00000000000..64215ecad5c --- /dev/null +++ b/packages/twenty-cli/src/services/generate.service.ts @@ -0,0 +1,57 @@ +import chalk from 'chalk'; +import { generate } from '@genql/cli'; +import { join, resolve } from 'path'; +import { ConfigService } from './config.service'; +import { ApiService } from './api.service'; + +export const GENERATED_FOLDER_NAME = 'generated'; + +export class GenerateService { + private configService: ConfigService; + private apiService: ApiService; + + constructor() { + this.configService = new ConfigService(); + this.apiService = new ApiService(); + } + + async generateClient(appPath: string): Promise { + const outputPath = join(appPath, GENERATED_FOLDER_NAME); + + console.log(chalk.blue('📦 Generating Twenty client...')); + console.log(chalk.gray(`📁 Output Path: ${outputPath}`)); + console.log(''); + const config = await this.configService.getConfig(); + + const url = config.apiUrl; + const token = config.apiKey; + + if (!url || !token) { + console.log( + chalk.yellow( + '⚠️ Skipping Client generation: API URL or token not configured', + ), + ); + return; + } + + console.log(chalk.gray(`API URL: ${url}`)); + console.log(chalk.gray(`Output: ${outputPath}`)); + + const { data: schema } = await this.apiService.getSchema(); + + await generate({ + schema, + output: resolve(outputPath), + scalarTypes: { + DateTime: 'string', + JSON: 'Record', + UUID: 'string', + }, + verbose: true, + }); + + console.log(chalk.green('✓ Client generated successfully!')); + console.log(chalk.gray(`Generated files at: ${outputPath}`)); + } +} diff --git a/packages/twenty-cli/src/utils/__tests__/load-manifest.spec.ts b/packages/twenty-cli/src/utils/__tests__/load-manifest.spec.ts index 3526c5ffe01..e0bc8c6dae3 100644 --- a/packages/twenty-cli/src/utils/__tests__/load-manifest.spec.ts +++ b/packages/twenty-cli/src/utils/__tests__/load-manifest.spec.ts @@ -1,6 +1,6 @@ +import { ensureDirSync, removeSync, writeFileSync } from 'fs-extra'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; -import { ensureDirSync, writeFileSync, removeSync } from 'fs-extra'; import { copyBaseApplicationProject } from '../app-template'; import { loadManifest } from '../load-manifest'; @@ -24,13 +24,13 @@ const tsLibMock = `declare module 'tslib' { const twentySdkTypesMock = ` declare module 'twenty-sdk/application' { export type SyncableEntityOptions = { universalIdentifier: string }; - + type ApplicationVariable = SyncableEntityOptions & { value?: string; description?: string; isSecret?: boolean; }; - + export type ApplicationConfig = SyncableEntityOptions & { displayName?: string; description?: string; @@ -44,27 +44,27 @@ declare module 'twenty-sdk/application' { httpMethod: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; isAuthRequired: boolean; }; - + type CronTrigger = { type: 'cron'; pattern: string; }; - + type DatabaseEventTrigger = { type: 'databaseEvent'; eventName: string; }; - + type ServerlessFunctionTrigger = SyncableEntityOptions & (RouteTrigger | CronTrigger | DatabaseEventTrigger); - + export type ServerlessFunctionConfig = SyncableEntityOptions & { name?: string; description?: string; timeoutSeconds?: number; triggers?: ServerlessFunctionTrigger[]; }; - + type ObjectMetadataOptions = SyncableEntityOptions & { nameSingular: string; namePlural: string; @@ -73,7 +73,7 @@ declare module 'twenty-sdk/application' { description?: string; icon?: string; }; - + export const ObjectMetadata = (_: ObjectMetadataOptions): ClassDecorator => { return () => {}; }; @@ -412,11 +412,8 @@ export const format = async (params: any): Promise => { ]); }); - it('fails fast if TS validation fails', async () => { - write(appDirectory, 'src/utils/broken.ts', `const x: number = 'oops';`); - - await expect(loadManifest(appDirectory)).rejects.toThrow( - /TypeScript validation failed/, - ); + it('manifest should contains typescript sources', async () => { + const { isTwentyClientUsed } = await loadManifest(appDirectory); + expect(isTwentyClientUsed).toBe(false); }); }); diff --git a/packages/twenty-cli/src/utils/format-and-warn-ts-diagnostics.ts b/packages/twenty-cli/src/utils/format-and-warn-ts-diagnostics.ts new file mode 100644 index 00000000000..af420fbe80d --- /dev/null +++ b/packages/twenty-cli/src/utils/format-and-warn-ts-diagnostics.ts @@ -0,0 +1,20 @@ +import ts, { formatDiagnosticsWithColorAndContext, sys } from 'typescript'; + +export const formatAndWarnTsDiagnostics = ({ + diagnostics, +}: { + diagnostics: ts.Diagnostic[]; +}) => { + if (diagnostics.length > 0) { + const formattedDiagnostics = formatDiagnosticsWithColorAndContext( + diagnostics, + { + getCanonicalFileName: (f) => f, + getCurrentDirectory: sys.getCurrentDirectory, + getNewLine: () => sys.newLine, + }, + ); + + console.warn(formattedDiagnostics); + } +}; diff --git a/packages/twenty-cli/src/utils/get-ts-program-and-diagnostics.ts b/packages/twenty-cli/src/utils/get-ts-program-and-diagnostics.ts new file mode 100644 index 00000000000..25a78295054 --- /dev/null +++ b/packages/twenty-cli/src/utils/get-ts-program-and-diagnostics.ts @@ -0,0 +1,58 @@ +import ts from 'typescript'; +import { join } from 'path'; +import { + createProgram, + formatDiagnosticsWithColorAndContext, + parseJsonConfigFileContent, + readConfigFile, + sys, +} from 'typescript'; + +const getProgramFromTsconfig = ({ + appPath, + tsconfigPath = 'tsconfig.json', +}: { + appPath: string; + tsconfigPath?: string; +}) => { + const configFile = readConfigFile(join(appPath, tsconfigPath), sys.readFile); + if (configFile.error) + throw new Error( + formatDiagnosticsWithColorAndContext([configFile.error], { + getCanonicalFileName: (f) => f, + getCurrentDirectory: sys.getCurrentDirectory, + getNewLine: () => sys.newLine, + }), + ); + const parsed = parseJsonConfigFileContent(configFile.config, sys, appPath); + if (parsed.errors.length) { + throw new Error( + formatDiagnosticsWithColorAndContext(parsed.errors, { + getCanonicalFileName: (f) => f, + getCurrentDirectory: sys.getCurrentDirectory, + getNewLine: () => sys.newLine, + }), + ); + } + return createProgram(parsed.fileNames, parsed.options); +}; + +export const getTsProgramAndDiagnostics = async ({ + appPath, +}: { + appPath: string; +}): Promise<{ program: ts.Program; diagnostics: ts.Diagnostic[] }> => { + const program = getProgramFromTsconfig({ + appPath, + tsconfigPath: 'tsconfig.json', + }); + + return { + diagnostics: [ + ...program.getSyntacticDiagnostics(), + ...program.getSemanticDiagnostics(), + ...program.getGlobalDiagnostics(), + ], + program, + }; +}; diff --git a/packages/twenty-cli/src/utils/load-manifest.ts b/packages/twenty-cli/src/utils/load-manifest.ts index e15bd34a239..40fb76848c9 100644 --- a/packages/twenty-cli/src/utils/load-manifest.ts +++ b/packages/twenty-cli/src/utils/load-manifest.ts @@ -1,52 +1,52 @@ import * as fs from 'fs-extra'; +import { posix, relative, sep } from 'path'; import { - sys, - getDecorators, - readConfigFile, - parseJsonConfigFileContent, - formatDiagnosticsWithColorAndContext, - createProgram, Decorator, - isPropertyAccessExpression, - isNumericLiteral, - SyntaxKind, - isArrayLiteralExpression, Expression, - isPropertyAssignment, - isComputedPropertyName, - isStringLiteralLike, - isShorthandPropertyAssignment, - isIdentifier, FunctionDeclaration, - VariableDeclaration, - Program, - Node, - isClassDeclaration, - isCallExpression, - isObjectLiteralExpression, - forEachChild, - SourceFile, - isVariableStatement, - isArrowFunction, - isFunctionExpression, - isExportAssignment, Modifier, - isPropertyDeclaration, + Node, + Program, + SourceFile, + SyntaxKind, + VariableDeclaration, + forEachChild, + getDecorators, + isArrayLiteralExpression, + isArrowFunction, + isCallExpression, + isClassDeclaration, + isComputedPropertyName, + isExportAssignment, + isFunctionExpression, + isIdentifier, isNoSubstitutionTemplateLiteral, + isNumericLiteral, + isObjectLiteralExpression, + isPropertyAccessExpression, + isPropertyAssignment, + isPropertyDeclaration, + isShorthandPropertyAssignment, + isStringLiteralLike, isTemplateExpression, + isVariableStatement, + isImportDeclaration, + NamedImports, } from 'typescript'; import { AppManifest, Application, + FieldMetadata, ObjectManifest, PackageJson, ServerlessFunctionManifest, Sources, - FieldMetadata, } from '../types/config.types'; -import { posix, relative, sep, resolve, join } from 'path'; -import { parseJsoncFile, parseTextFile } from '../utils/jsonc-parser'; import { findPathFile } from '../utils/find-path-file'; +import { parseJsoncFile, parseTextFile } from '../utils/jsonc-parser'; +import { formatAndWarnTsDiagnostics } from './format-and-warn-ts-diagnostics'; +import { getTsProgramAndDiagnostics } from '../utils/get-ts-program-and-diagnostics'; +import { GENERATED_FOLDER_NAME } from '../services/generate.service'; type JSONValue = | string @@ -56,32 +56,6 @@ type JSONValue = | JSONValue[] | { [k: string]: JSONValue }; -const getProgramFromTsconfig = ( - appPath: string, - tsconfigPath = 'tsconfig.json', -) => { - const configFile = readConfigFile(join(appPath, tsconfigPath), sys.readFile); - if (configFile.error) - throw new Error( - formatDiagnosticsWithColorAndContext([configFile.error], { - getCanonicalFileName: (f) => f, - getCurrentDirectory: sys.getCurrentDirectory, - getNewLine: () => sys.newLine, - }), - ); - const parsed = parseJsonConfigFileContent(configFile.config, sys, appPath); - if (parsed.errors.length) { - throw new Error( - formatDiagnosticsWithColorAndContext(parsed.errors, { - getCanonicalFileName: (f) => f, - getCurrentDirectory: sys.getCurrentDirectory, - getNewLine: () => sys.newLine, - }), - ); - } - return createProgram(parsed.fileNames, parsed.options); -}; - const isDecoratorNamed = (node: Decorator, name: string): node is Decorator => { const expr = node.expression; if (isCallExpression(expr)) { @@ -392,23 +366,6 @@ const collectServerlessFunctions = (program: Program, appPath: string) => { return serverlessFunctions; }; -const validateProgram = (program: Program) => { - const diagnostics = [ - ...program.getSyntacticDiagnostics(), - ...program.getSemanticDiagnostics(), - ...program.getGlobalDiagnostics(), - ]; - - if (diagnostics.length > 0) { - const formatted = formatDiagnosticsWithColorAndContext(diagnostics, { - getCanonicalFileName: (f) => f, - getCurrentDirectory: sys.getCurrentDirectory, - getNewLine: () => sys.newLine, - }); - throw new Error(`TypeScript validation failed:\n${formatted}`); - } -}; - const setNested = (root: Sources, parts: string[], value: string) => { let cur: Sources = root; for (let i = 0; i < parts.length; i++) { @@ -423,14 +380,10 @@ const setNested = (root: Sources, parts: string[], value: string) => { }; const loadFolderContentIntoJson = async ( - sourcePath = '.', - tsconfigPath = 'tsconfig.json', + program: Program, + appPath: string, ): Promise => { const sources: Sources = {}; - const baseAbs = resolve(sourcePath); - - // Build the program from tsconfig (uses your getProgramFromTsconfig) - const program: Program = getProgramFromTsconfig(baseAbs, tsconfigPath); // Iterate only files the TS program knows about. for (const sf of program.getSourceFiles()) { @@ -438,7 +391,7 @@ const loadFolderContentIntoJson = async ( // Skip .d.ts and anything outside sourcePath if (sf.isDeclarationFile) continue; - if (!abs.startsWith(baseAbs + sep) && abs !== baseAbs) continue; + if (!abs.startsWith(appPath + sep) && abs !== appPath) continue; // Keep only TS/TSX files if (!(abs.endsWith('.ts') || abs.endsWith('.tsx'))) continue; @@ -446,7 +399,7 @@ const loadFolderContentIntoJson = async ( // Optional extra guard (usually unnecessary if tsconfig excludes node_modules) if (abs.includes(`${sep}node_modules${sep}`)) continue; - const relFromRoot = relative(baseAbs, abs); + const relFromRoot = relative(appPath, abs); const parts = relFromRoot.split(sep); const content = await fs.readFile(abs, 'utf8'); @@ -495,15 +448,67 @@ export const extractTwentyAppConfig = (program: Program): Application => { throw new Error('Could not find default exported ApplicationConfig'); }; +const isTwentyClientUsedInProgram = (program: Program): boolean => { + for (const sf of program.getSourceFiles()) { + if (sf.isDeclarationFile) continue; + + let found = false; + + const visit = (node: Node): void => { + if (found) return; + + if (isImportDeclaration(node)) { + const moduleSpecifier = node.moduleSpecifier; + + if (isStringLiteralLike(moduleSpecifier)) { + const moduleText = moduleSpecifier.text; + + // Match ../../generated, ../generated, ./foo/generated, etc. + const isGeneratedModule = + moduleText === GENERATED_FOLDER_NAME || + moduleText.endsWith(`/${GENERATED_FOLDER_NAME}`); + + if ( + isGeneratedModule && + node.importClause && + node.importClause.namedBindings && + node.importClause.namedBindings.kind === SyntaxKind.NamedImports + ) { + const namedImports = node.importClause + .namedBindings as NamedImports; + + for (const element of namedImports.elements) { + const importedName = + element.propertyName?.text ?? element.name.text; + + if (importedName === 'createClient') { + found = true; + return; + } + } + } + } + } + + forEachChild(node, visit); + }; + + visit(sf); + + if (found) return true; + } + + return false; +}; + export const loadManifest = async ( - path?: string, + appPath: string, ): Promise<{ packageJson: PackageJson; yarnLock: string; manifest: AppManifest; + isTwentyClientUsed: boolean; }> => { - const appPath = path ?? process.cwd(); - const packageJson = await parseJsoncFile( await findPathFile(appPath, 'package.json'), ); @@ -512,17 +517,22 @@ export const loadManifest = async ( await findPathFile(appPath, 'yarn.lock'), ); - const program = getProgramFromTsconfig(appPath, 'tsconfig.json'); + const { diagnostics, program } = await getTsProgramAndDiagnostics({ + appPath, + }); - validateProgram(program); + formatAndWarnTsDiagnostics({ + diagnostics, + }); - const [objects, serverlessFunctions, application, sources] = - await Promise.all([ - Promise.resolve(collectObjects(program)), - Promise.resolve(collectServerlessFunctions(program, appPath)), - Promise.resolve(extractTwentyAppConfig(program)), - loadFolderContentIntoJson(appPath), - ]); + const [objects, serverlessFunctions, application, sources] = [ + collectObjects(program), + collectServerlessFunctions(program, appPath), + extractTwentyAppConfig(program), + await loadFolderContentIntoJson(program, appPath), + ]; + + const isTwentyClientUsed = isTwentyClientUsedInProgram(program); return { packageJson, @@ -533,5 +543,6 @@ export const loadManifest = async ( serverlessFunctions, sources, }, + isTwentyClientUsed, }; }; diff --git a/packages/twenty-cli/tsconfig.json b/packages/twenty-cli/tsconfig.json index 1de5590dcb5..12034cd5027 100644 --- a/packages/twenty-cli/tsconfig.json +++ b/packages/twenty-cli/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "composite": true }, - "include": ["src/**/*"], + "include": ["src"], "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts", "**/*.e2e-spec.ts", "**/__tests__/**"] } diff --git a/packages/twenty-cli/tsconfig.lib.json b/packages/twenty-cli/tsconfig.lib.json index f03691f8a42..8cbb3cf1e2e 100644 --- a/packages/twenty-cli/tsconfig.lib.json +++ b/packages/twenty-cli/tsconfig.lib.json @@ -16,6 +16,6 @@ "declarationMap": true, "sourceMap": true }, - "include": ["src/**/*"], + "include": ["src"], "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts", "**/*.e2e-spec.ts", "**/__tests__/**"] } diff --git a/packages/twenty-cli/tsconfig.spec.json b/packages/twenty-cli/tsconfig.spec.json new file mode 100644 index 00000000000..cee8b1e5b88 --- /dev/null +++ b/packages/twenty-cli/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["jest", "node"] + }, + "include": [ + "src/**/*", + "src/**/__tests__/**/*.spec.ts" + ], + "exclude": [ + "node_modules", + "dist", + ] +} diff --git a/packages/twenty-sdk/package.json b/packages/twenty-sdk/package.json index 698aa34d6e0..801c6f22792 100644 --- a/packages/twenty-sdk/package.json +++ b/packages/twenty-sdk/package.json @@ -13,9 +13,14 @@ "build": "vite build" }, "devDependencies": { + "@types/node": "^24.0.0", "typescript": "5.9.2", "vite": "^7.0.0", - "vite-plugin-dts": "3.8.1" + "vite-plugin-dts": "3.8.1", + "vite-tsconfig-paths": "^4.2.1" + }, + "dependencies": { + "twenty-shared": "workspace:*" }, "exports": { ".": { diff --git a/packages/twenty-sdk/project.json b/packages/twenty-sdk/project.json index 2d03ec2497a..d0ade3dae7d 100644 --- a/packages/twenty-sdk/project.json +++ b/packages/twenty-sdk/project.json @@ -3,15 +3,10 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "packages/twenty-sdk/src", "projectType": "library", - "tags": [ - "scope:sdk" - ], + "tags": ["scope:sdk"], "targets": { "build": { - "dependsOn": [ - "generateBarrels", - "^build" - ], + "dependsOn": ["generateBarrels", "^build"], "outputs": [ "{projectRoot}/dist", "{projectRoot}/application/package.json", @@ -21,10 +16,7 @@ "generateBarrels": { "executor": "nx:run-commands", "cache": true, - "inputs": [ - "production", - "{projectRoot}/scripts/generateBarrels.ts" - ], + "inputs": ["production", "{projectRoot}/scripts/generateBarrels.ts"], "outputs": [ "{projectRoot}/src/index.ts", "{projectRoot}/src/*/index.ts", diff --git a/packages/twenty-sdk/src/application/field-metadata/composite-fields.ts b/packages/twenty-sdk/src/application/field-metadata/composite-fields.ts index 24ba4523176..06695ba2abf 100644 --- a/packages/twenty-sdk/src/application/field-metadata/composite-fields.ts +++ b/packages/twenty-sdk/src/application/field-metadata/composite-fields.ts @@ -1,4 +1,4 @@ -export { +export type { ActorMetadata as ActorField, AddressMetadata as AddressField, CurrencyMetadata as CurrencyField, @@ -6,5 +6,6 @@ export { FullNameMetadata as FullNameField, LinksMetadata as LinksField, PhonesMetadata as PhonesField, - RichTextV2Metadata as RichTextField, + RichTextV2Metadata as RichTextField } from 'twenty-shared/types'; + diff --git a/packages/twenty-sdk/src/application/index.ts b/packages/twenty-sdk/src/application/index.ts index eef8610eb63..92b197b81e0 100644 --- a/packages/twenty-sdk/src/application/index.ts +++ b/packages/twenty-sdk/src/application/index.ts @@ -8,7 +8,7 @@ */ export type { ApplicationConfig } from './application-config'; -export { +export type { ActorField, AddressField, CurrencyField, diff --git a/packages/twenty-sdk/vite.config.ts b/packages/twenty-sdk/vite.config.ts index 282d19a13a9..0ba884e6493 100644 --- a/packages/twenty-sdk/vite.config.ts +++ b/packages/twenty-sdk/vite.config.ts @@ -1,25 +1,77 @@ -// @ts-ignore import path from 'path'; import { defineConfig } from 'vite'; import dts from 'vite-plugin-dts'; +import tsconfigPaths from 'vite-tsconfig-paths'; +import packageJson from './package.json'; -export default defineConfig({ - resolve: { - alias: { - '@': path.resolve(__dirname, 'src'), +const moduleEntries = Object.keys((packageJson as any).exports || {}) + .filter( + (key) => key !== './style.css' && key !== '.' && !key.startsWith('./src/'), + ) + .map((module) => `src/${module.replace(/^\.\//, '')}/index.ts`); + +const entries = ['src/index.ts', ...moduleEntries]; + +const entryFileNames = (chunk: any, extension: 'cjs' | 'mjs') => { + if (!chunk.isEntry) { + throw new Error( + `Should never occurs, encountered a non entry chunk ${chunk.facadeModuleId}`, + ); + } + + const splitFaceModuleId = chunk.facadeModuleId?.split('/'); + if (splitFaceModuleId === undefined) { + throw new Error( + `Should never occurs splitFaceModuleId is undefined ${chunk.facadeModuleId}`, + ); + } + + const moduleDirectory = splitFaceModuleId[splitFaceModuleId?.length - 2]; + if (moduleDirectory === 'src') { + return `${chunk.name}.${extension}`; + } + return `${moduleDirectory}.${extension}`; +}; +export default defineConfig(() => { + const tsConfigPath = path.resolve(__dirname, './tsconfig.json'); + + return { + root: __dirname, + cacheDir: '../../node_modules/.vite/packages/twenty-sdk', + plugins: [ + tsconfigPaths({ + root: __dirname, + }), + dts({ entryRoot: './src', tsconfigPath: tsConfigPath }), + ], + build: { + outDir: 'dist', + lib: { entry: entries, name: 'twenty-sdk' }, + rollupOptions: { + external: [ + ...Object.keys((packageJson as any).dependencies || {}), + 'path', + 'fs', + 'crypto', + 'stream', + 'util', + 'os', + ], + output: [ + { + format: 'es', + entryFileNames: (chunk) => entryFileNames(chunk, 'mjs'), + }, + { + format: 'cjs', + interop: 'auto', + esModule: true, + exports: 'named', + entryFileNames: (chunk) => entryFileNames(chunk, 'cjs'), + }, + ], + }, }, - }, - build: { - lib: { - entry: 'src/index.ts', - name: 'twenty-sdk', - formats: ['es', 'cjs'], - fileName: (format) => `index.${format === 'es' ? 'mjs' : 'cjs'}`, - }, - }, - plugins: [ - dts({ - entryRoot: 'src', - }), - ], + logLevel: 'warn', + }; }); diff --git a/yarn.lock b/yarn.lock index e5acfb27794..4eac8f66539 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6325,6 +6325,13 @@ __metadata: languageName: node linkType: hard +"@fastify/busboy@npm:^2.0.0": + version: 2.1.1 + resolution: "@fastify/busboy@npm:2.1.1" + checksum: 10c0/6f8027a8cba7f8f7b736718b013f5a38c0476eea67034c94a0d3c375e2b114366ad4419e6a6fa7ffc2ef9c6d3e0435d76dd584a7a1cbac23962fda7650b579e3 + languageName: node + linkType: hard + "@fig/complete-commander@npm:^3.0.0": version: 3.2.0 resolution: "@fig/complete-commander@npm:3.2.0" @@ -6471,6 +6478,30 @@ __metadata: languageName: node linkType: hard +"@genql/cli@npm:^3.0.3": + version: 3.0.5 + resolution: "@genql/cli@npm:3.0.5" + dependencies: + "@graphql-tools/graphql-file-loader": "npm:^7.5.11" + "@graphql-tools/load": "npm:^7.8.6" + fs-extra: "npm:^10.1.0" + graphql: "npm:^16.6.0" + kleur: "npm:^4.1.5" + listr: "npm:^0.14.3" + lodash: "npm:^4.17.21" + mkdirp: "npm:^0.5.1" + native-fetch: "npm:^4.0.2" + prettier: "npm:^2.8.0" + qs: "npm:^6.11.0" + rimraf: "npm:^2.6.3" + undici: "npm:^5.18.0" + yargs: "npm:^15.3.1" + bin: + genql: dist/cli.js + checksum: 10c0/646d4da8986741a8f1b610bd8cfb5bc26cd9a856de671461e4ca87fb05443f37dd0edfb3b30ceaa34cd049077873afa07febfd4714dd2e0681fb07484e19a080 + languageName: node + linkType: hard + "@gitbeaker/core@npm:^38.12.1": version: 38.12.1 resolution: "@gitbeaker/core@npm:38.12.1" @@ -7125,7 +7156,7 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/graphql-file-loader@npm:^7.3.7, @graphql-tools/graphql-file-loader@npm:^7.5.0": +"@graphql-tools/graphql-file-loader@npm:^7.3.7, @graphql-tools/graphql-file-loader@npm:^7.5.0, @graphql-tools/graphql-file-loader@npm:^7.5.11": version: 7.5.17 resolution: "@graphql-tools/graphql-file-loader@npm:7.5.17" dependencies: @@ -7183,7 +7214,7 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/load@npm:^7.5.5, @graphql-tools/load@npm:^7.8.0": +"@graphql-tools/load@npm:^7.5.5, @graphql-tools/load@npm:^7.8.0, @graphql-tools/load@npm:^7.8.6": version: 7.8.14 resolution: "@graphql-tools/load@npm:7.8.14" dependencies: @@ -19141,6 +19172,20 @@ __metadata: languageName: node linkType: hard +"@samverschueren/stream-to-observable@npm:^0.3.0": + version: 0.3.1 + resolution: "@samverschueren/stream-to-observable@npm:0.3.1" + dependencies: + any-observable: "npm:^0.3.0" + peerDependenciesMeta: + rxjs: + optional: true + zen-observable: + optional: true + checksum: 10c0/0d874453f6bc2460d71783292291f52feb36c2a75314b1072a6ffe6206562f33e9d664a554348d565a6b54da9041d75070371052545bc329caaa52f64216987f + languageName: node + linkType: hard + "@scalar/api-client@npm:2.2.62": version: 2.2.62 resolution: "@scalar/api-client@npm:2.2.62" @@ -27147,7 +27192,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^3.1.0": +"ansi-escapes@npm:^3.0.0, ansi-escapes@npm:^3.1.0": version: 3.2.0 resolution: "ansi-escapes@npm:3.2.0" checksum: 10c0/084e1ce38139ad2406f18a8e7efe2b850ddd06ce3c00f633392d1ce67756dab44fe290e573d09ef3c9a0cb13c12881e0e35a8f77a017d39a0a4ab85ae2fae04f @@ -27186,6 +27231,13 @@ __metadata: languageName: node linkType: hard +"ansi-regex@npm:^3.0.0": + version: 3.0.1 + resolution: "ansi-regex@npm:3.0.1" + checksum: 10c0/d108a7498b8568caf4a46eea4f1784ab4e0dfb2e3f3938c697dee21443d622d765c958f2b7e2b9f6b9e55e2e2af0584eaa9915d51782b89a841c28e744e7a167 + languageName: node + linkType: hard + "ansi-regex@npm:^4.1.0": version: 4.1.1 resolution: "ansi-regex@npm:4.1.1" @@ -27207,6 +27259,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^2.2.1": + version: 2.2.1 + resolution: "ansi-styles@npm:2.2.1" + checksum: 10c0/7c68aed4f1857389e7a12f85537ea5b40d832656babbf511cc7ecd9efc52889b9c3e5653a71a6aade783c3c5e0aa223ad4ff8e83c27ac8a666514e6c79068cab + languageName: node + linkType: hard + "ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -27267,6 +27326,13 @@ __metadata: languageName: node linkType: hard +"any-observable@npm:^0.3.0": + version: 0.3.0 + resolution: "any-observable@npm:0.3.0" + checksum: 10c0/104c2b79c2ac7e6c75b35f8fd62babf73015668f22bd25336c6f848350d91f9e7daf2fddbf1c1b76fe795e89fbc91b49f70a2aec5c69f1acf0562c344f36042b + languageName: node + linkType: hard + "any-promise@npm:^1.0.0": version: 1.3.0 resolution: "any-promise@npm:1.3.0" @@ -29505,6 +29571,19 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^1.0.0, chalk@npm:^1.1.3": + version: 1.1.3 + resolution: "chalk@npm:1.1.3" + dependencies: + ansi-styles: "npm:^2.2.1" + escape-string-regexp: "npm:^1.0.2" + has-ansi: "npm:^2.0.0" + strip-ansi: "npm:^3.0.0" + supports-color: "npm:^2.0.0" + checksum: 10c0/28c3e399ec286bb3a7111fd4225ebedb0d7b813aef38a37bca7c498d032459c265ef43404201d5fbb8d888d29090899c95335b4c0cda13e8b126ff15c541cef8 + languageName: node + linkType: hard + "chalk@npm:^2.3.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -29943,6 +30022,15 @@ __metadata: languageName: node linkType: hard +"cli-cursor@npm:^2.0.0, cli-cursor@npm:^2.1.0": + version: 2.1.0 + resolution: "cli-cursor@npm:2.1.0" + dependencies: + restore-cursor: "npm:^2.0.0" + checksum: 10c0/09ee6d8b5b818d840bf80ec9561eaf696672197d3a02a7daee2def96d5f52ce6e0bbe7afca754ccf14f04830b5a1b4556273e983507d5029f95bba3016618eda + languageName: node + linkType: hard + "cli-cursor@npm:^4.0.0": version: 4.0.0 resolution: "cli-cursor@npm:4.0.0" @@ -30026,6 +30114,16 @@ __metadata: languageName: node linkType: hard +"cli-truncate@npm:^0.2.1": + version: 0.2.1 + resolution: "cli-truncate@npm:0.2.1" + dependencies: + slice-ansi: "npm:0.0.4" + string-width: "npm:^1.0.1" + checksum: 10c0/c6caa5e2b70d841c42f4a2270d6fc7129df915f8911e4afa90c79231ccc857cd819a2c90e0707fde04e51ce56b4d71646b843f6cbaff4f7cdcb3b91ed51f6e89 + languageName: node + linkType: hard + "cli-truncate@npm:^2.1.0": version: 2.1.0 resolution: "cli-truncate@npm:2.1.0" @@ -31809,6 +31907,13 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:^1.27.2": + version: 1.30.1 + resolution: "date-fns@npm:1.30.1" + checksum: 10c0/bad6ad7c15180121e15d61ad62a4a214c108d66f35b35f5eeb6ade837a3c29aa4444b9528a93a5374b95ba11231c142276351bf52f4d168676f9a1e17ce3726a + languageName: node + linkType: hard + "date-fns@npm:^3.3.1, date-fns@npm:^3.6.0": version: 3.6.0 resolution: "date-fns@npm:3.6.0" @@ -32929,6 +33034,13 @@ __metadata: languageName: node linkType: hard +"elegant-spinner@npm:^1.0.1": + version: 1.0.1 + resolution: "elegant-spinner@npm:1.0.1" + checksum: 10c0/df607c83c20fc3ce56c514175dd5d1ee7f667da00cee13d04d32c70d55e76555091fa236689e691cf7dedba17b0020fec635e499cdde84dbea2ef8639314e5f8 + languageName: node + linkType: hard + "elliptic@npm:^6.5.3, elliptic@npm:^6.5.5": version: 6.6.1 resolution: "elliptic@npm:6.6.1" @@ -34062,7 +34174,7 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:^1.0.5": +"escape-string-regexp@npm:^1.0.2, escape-string-regexp@npm:^1.0.5": version: 1.0.5 resolution: "escape-string-regexp@npm:1.0.5" checksum: 10c0/a968ad453dd0c2724e14a4f20e177aaf32bb384ab41b674a8454afe9a41c5e6fe8903323e0a1052f56289d04bd600f81278edf140b0fcc02f5cac98d0f5b5371 @@ -35436,6 +35548,25 @@ __metadata: languageName: node linkType: hard +"figures@npm:^1.7.0": + version: 1.7.0 + resolution: "figures@npm:1.7.0" + dependencies: + escape-string-regexp: "npm:^1.0.5" + object-assign: "npm:^4.1.0" + checksum: 10c0/a10942b0eec3372bf61822ab130d2bbecdf527d551b0b013fbe7175b7a0238ead644ee8930a1a3cb872fb9ab2ec27df30e303765a3b70b97852e2e9ee43bdff3 + languageName: node + linkType: hard + +"figures@npm:^2.0.0": + version: 2.0.0 + resolution: "figures@npm:2.0.0" + dependencies: + escape-string-regexp: "npm:^1.0.5" + checksum: 10c0/5dc5a75fec3e7e04ae65d6ce51d28b3e70d4656c51b06996b6fdb2cb5b542df512e3b3c04482f5193a964edddafa5521479ff948fa84e12ff556e53e094ab4ce + languageName: node + linkType: hard + "file-entry-cache@npm:^8.0.0": version: 8.0.0 resolution: "file-entry-cache@npm:8.0.0" @@ -36087,7 +36218,7 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^10.0.0": +"fs-extra@npm:^10.0.0, fs-extra@npm:^10.1.0": version: 10.1.0 resolution: "fs-extra@npm:10.1.0" dependencies: @@ -37310,6 +37441,15 @@ __metadata: languageName: node linkType: hard +"has-ansi@npm:^2.0.0": + version: 2.0.0 + resolution: "has-ansi@npm:2.0.0" + dependencies: + ansi-regex: "npm:^2.0.0" + checksum: 10c0/f54e4887b9f8f3c4bfefd649c48825b3c093987c92c27880ee9898539e6f01aed261e82e73153c3f920fde0db5bf6ebd58deb498ed1debabcb4bc40113ccdf05 + languageName: node + linkType: hard + "has-bigints@npm:^1.0.2": version: 1.0.2 resolution: "has-bigints@npm:1.0.2" @@ -38553,7 +38693,7 @@ __metadata: languageName: node linkType: hard -"indent-string@npm:^3.2.0": +"indent-string@npm:^3.0.0, indent-string@npm:^3.2.0": version: 3.2.0 resolution: "indent-string@npm:3.2.0" checksum: 10c0/91b6d61621d24944c5c4d365d6f1ff4a490264ccaf1162a602faa0d323e69231db2180ad4ccc092c2f49cf8888cdb3da7b73e904cc0fdeec40d0bfb41ceb9478 @@ -39199,6 +39339,13 @@ __metadata: languageName: node linkType: hard +"is-fullwidth-code-point@npm:^2.0.0": + version: 2.0.0 + resolution: "is-fullwidth-code-point@npm:2.0.0" + checksum: 10c0/e58f3e4a601fc0500d8b2677e26e9fe0cd450980e66adb29d85b6addf7969731e38f8e43ed2ec868a09c101a55ac3d8b78902209269f38c5286bc98f5bc1b4d9 + languageName: node + linkType: hard + "is-fullwidth-code-point@npm:^3.0.0": version: 3.0.0 resolution: "is-fullwidth-code-point@npm:3.0.0" @@ -39394,6 +39541,15 @@ __metadata: languageName: node linkType: hard +"is-observable@npm:^1.1.0": + version: 1.1.0 + resolution: "is-observable@npm:1.1.0" + dependencies: + symbol-observable: "npm:^1.1.0" + checksum: 10c0/cf3166b0822f70ad06e7851e09430166ce658349d54aaa64c93a03320420b9285735821b23164bdce741ff83a86730ac3e53035ce4e2511ed843dbff4105bfa2 + languageName: node + linkType: hard + "is-online@npm:^10.0.0": version: 10.0.0 resolution: "is-online@npm:10.0.0" @@ -39464,6 +39620,13 @@ __metadata: languageName: node linkType: hard +"is-promise@npm:^2.1.0": + version: 2.2.2 + resolution: "is-promise@npm:2.2.2" + checksum: 10c0/2dba959812380e45b3df0fb12e7cb4d4528c989c7abb03ececb1d1fd6ab1cbfee956ca9daa587b9db1d8ac3c1e5738cf217bdb3dfd99df8c691be4c00ae09069 + languageName: node + linkType: hard + "is-promise@npm:^4.0.0": version: 4.0.0 resolution: "is-promise@npm:4.0.0" @@ -41636,7 +41799,7 @@ __metadata: languageName: node linkType: hard -"kleur@npm:^4.0.3": +"kleur@npm:^4.0.3, kleur@npm:^4.1.5": version: 4.1.5 resolution: "kleur@npm:4.1.5" checksum: 10c0/e9de6cb49657b6fa70ba2d1448fd3d691a5c4370d8f7bbf1c2f64c24d461270f2117e1b0afe8cb3114f13bbd8e51de158c2a224953960331904e636a5e4c0f2a @@ -41924,6 +42087,43 @@ __metadata: languageName: node linkType: hard +"listr-silent-renderer@npm:^1.1.1": + version: 1.1.1 + resolution: "listr-silent-renderer@npm:1.1.1" + checksum: 10c0/a13e08ebf863516a757bce4887f05290070772113d89095e9f51a07cf0b11a43a7563a67ff3b287c752c08f6d781fdb2123b02957534e3e0675fb564f2a42e1b + languageName: node + linkType: hard + +"listr-update-renderer@npm:^0.5.0": + version: 0.5.0 + resolution: "listr-update-renderer@npm:0.5.0" + dependencies: + chalk: "npm:^1.1.3" + cli-truncate: "npm:^0.2.1" + elegant-spinner: "npm:^1.0.1" + figures: "npm:^1.7.0" + indent-string: "npm:^3.0.0" + log-symbols: "npm:^1.0.2" + log-update: "npm:^2.3.0" + strip-ansi: "npm:^3.0.1" + peerDependencies: + listr: ^0.14.2 + checksum: 10c0/8ade44bf3dc6146c8e0178000619439e8889792c4689b66be6ce82bd459f5fe462ecb34b05147fb206a8ad60e6d4e6f34c9f48038e18366f867fd972688b8edc + languageName: node + linkType: hard + +"listr-verbose-renderer@npm:^0.5.0": + version: 0.5.0 + resolution: "listr-verbose-renderer@npm:0.5.0" + dependencies: + chalk: "npm:^2.4.1" + cli-cursor: "npm:^2.1.0" + date-fns: "npm:^1.27.2" + figures: "npm:^2.0.0" + checksum: 10c0/041cd1e82da7054f27ae0a914e98b40d15faf9f950ef850578fc6241d3fff3c2d7158a4f6226006e566b4c47bf445be2d254dd1ce5c16569a3a5dcd575bec656 + languageName: node + linkType: hard + "listr2@npm:^4.0.5": version: 4.0.5 resolution: "listr2@npm:4.0.5" @@ -41945,6 +42145,23 @@ __metadata: languageName: node linkType: hard +"listr@npm:^0.14.3": + version: 0.14.3 + resolution: "listr@npm:0.14.3" + dependencies: + "@samverschueren/stream-to-observable": "npm:^0.3.0" + is-observable: "npm:^1.1.0" + is-promise: "npm:^2.1.0" + is-stream: "npm:^1.1.0" + listr-silent-renderer: "npm:^1.1.1" + listr-update-renderer: "npm:^0.5.0" + listr-verbose-renderer: "npm:^0.5.0" + p-map: "npm:^2.0.0" + rxjs: "npm:^6.3.3" + checksum: 10c0/753d518218c423f46bee8eeacccecadfd2e414ba9c0f602e7f85fe3f6fa18404dfab0812433aeda4683ee2548358488f597ac1a3d321196baec5d3149b200b10 + languageName: node + linkType: hard + "load-esm@npm:1.0.3": version: 1.0.3 resolution: "load-esm@npm:1.0.3" @@ -42366,6 +42583,26 @@ __metadata: languageName: node linkType: hard +"log-symbols@npm:^1.0.2": + version: 1.0.2 + resolution: "log-symbols@npm:1.0.2" + dependencies: + chalk: "npm:^1.0.0" + checksum: 10c0/c64e1fe41d0d043840f8b592d043b8607a836b846506f525a53d99d578561f02f97b2cba1d2b3c30bae5311d64b308d5a392a9930d252b906a9042fc2877da7a + languageName: node + linkType: hard + +"log-update@npm:^2.3.0": + version: 2.3.0 + resolution: "log-update@npm:2.3.0" + dependencies: + ansi-escapes: "npm:^3.0.0" + cli-cursor: "npm:^2.0.0" + wrap-ansi: "npm:^3.0.1" + checksum: 10c0/9bf21b138801ab4770a2bfa735161cf005b869360eaf5003a84ba64ddc5f5c3ce7217f4f1fa79d9c1f510d792213b2c9800327228e94df05859d19b716215d90 + languageName: node + linkType: hard + "log-update@npm:^4.0.0": version: 4.0.0 resolution: "log-update@npm:4.0.0" @@ -44593,6 +44830,13 @@ __metadata: languageName: node linkType: hard +"mimic-fn@npm:^1.0.0": + version: 1.2.0 + resolution: "mimic-fn@npm:1.2.0" + checksum: 10c0/ad55214aec6094c0af4c0beec1a13787556f8116ed88807cf3f05828500f21f93a9482326bcd5a077ae91e3e8795b4e76b5b4c8bb12237ff0e4043a365516cba + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -44916,7 +45160,7 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^0.5.6": +"mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.6": version: 0.5.6 resolution: "mkdirp@npm:0.5.6" dependencies: @@ -45270,6 +45514,15 @@ __metadata: languageName: node linkType: hard +"native-fetch@npm:^4.0.2": + version: 4.0.2 + resolution: "native-fetch@npm:4.0.2" + peerDependencies: + undici: "*" + checksum: 10c0/e3b824721daaa628086d9dcd02e8eb12f0a6c5e13a1d182682bae238d80c9bbf3dfd6314a94692ebe20316aa354476804b4df148201d066b46fc552a5794cfab + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -46532,6 +46785,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^2.0.0": + version: 2.0.1 + resolution: "onetime@npm:2.0.1" + dependencies: + mimic-fn: "npm:^1.0.0" + checksum: 10c0/b4e44a8c34e70e02251bfb578a6e26d6de6eedbed106cd78211d2fd64d28b6281d54924696554e4e966559644243753ac5df73c87f283b0927533d3315696215 + languageName: node + linkType: hard + "onetime@npm:^5.1.0, onetime@npm:^5.1.2": version: 5.1.2 resolution: "onetime@npm:5.1.2" @@ -46887,6 +47149,13 @@ __metadata: languageName: node linkType: hard +"p-map@npm:^2.0.0": + version: 2.1.0 + resolution: "p-map@npm:2.1.0" + checksum: 10c0/735dae87badd4737a2dd582b6d8f93e49a1b79eabbc9815a4d63a528d5e3523e978e127a21d784cccb637010e32103a40d2aaa3ab23ae60250b1a820ca752043 + languageName: node + linkType: hard + "p-map@npm:^3.0.0": version: 3.0.0 resolution: "p-map@npm:3.0.0" @@ -48249,7 +48518,7 @@ __metadata: languageName: node linkType: hard -"prettier@npm:2.8.8, prettier@npm:^2.0.0": +"prettier@npm:2.8.8, prettier@npm:^2.0.0, prettier@npm:^2.8.0": version: 2.8.8 resolution: "prettier@npm:2.8.8" bin: @@ -50986,6 +51255,16 @@ __metadata: languageName: node linkType: hard +"restore-cursor@npm:^2.0.0": + version: 2.0.0 + resolution: "restore-cursor@npm:2.0.0" + dependencies: + onetime: "npm:^2.0.0" + signal-exit: "npm:^3.0.2" + checksum: 10c0/f5b335bee06f440445e976a7031a3ef53691f9b7c4a9d42a469a0edaf8a5508158a0d561ff2b26a1f4f38783bcca2c0e5c3a44f927326f6694d5b44d7a4993e6 + languageName: node + linkType: hard + "restore-cursor@npm:^3.1.0": version: 3.1.0 resolution: "restore-cursor@npm:3.1.0" @@ -51086,6 +51365,17 @@ __metadata: languageName: node linkType: hard +"rimraf@npm:^2.6.3": + version: 2.7.1 + resolution: "rimraf@npm:2.7.1" + dependencies: + glob: "npm:^7.1.3" + bin: + rimraf: ./bin.js + checksum: 10c0/4eef73d406c6940927479a3a9dee551e14a54faf54b31ef861250ac815172bade86cc6f7d64a4dc5e98b65e4b18a2e1c9ff3b68d296be0c748413f092bb0dd40 + languageName: node + linkType: hard + "rimraf@npm:^3.0.0, rimraf@npm:^3.0.2": version: 3.0.2 resolution: "rimraf@npm:3.0.2" @@ -51374,7 +51664,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^6.6.0": +"rxjs@npm:^6.3.3, rxjs@npm:^6.6.0": version: 6.6.7 resolution: "rxjs@npm:6.6.7" dependencies: @@ -52331,6 +52621,13 @@ __metadata: languageName: node linkType: hard +"slice-ansi@npm:0.0.4": + version: 0.0.4 + resolution: "slice-ansi@npm:0.0.4" + checksum: 10c0/997d4cc73e34aa8c0f60bdb71701b16c062cc4acd7a95e3b10e8c05d790eb5e735d9b470270dc6f443b1ba21492db7ceb849d5c93011d1256061bf7ed7216c7a + languageName: node + linkType: hard + "slice-ansi@npm:^3.0.0": version: 3.0.0 resolution: "slice-ansi@npm:3.0.0" @@ -53061,6 +53358,16 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^2.1.1": + version: 2.1.1 + resolution: "string-width@npm:2.1.1" + dependencies: + is-fullwidth-code-point: "npm:^2.0.0" + strip-ansi: "npm:^4.0.0" + checksum: 10c0/e5f2b169fcf8a4257a399f95d069522f056e92ec97dbdcb9b0cdf14d688b7ca0b1b1439a1c7b9773cd79446cbafd582727279d6bfdd9f8edd306ea5e90e5b610 + languageName: node + linkType: hard + "string-width@npm:^5.0.1, string-width@npm:^5.1.2": version: 5.1.2 resolution: "string-width@npm:5.1.2" @@ -53231,6 +53538,15 @@ __metadata: languageName: node linkType: hard +"strip-ansi@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-ansi@npm:4.0.0" + dependencies: + ansi-regex: "npm:^3.0.0" + checksum: 10c0/d75d9681e0637ea316ddbd7d4d3be010b1895a17e885155e0ed6a39755ae0fd7ef46e14b22162e66a62db122d3a98ab7917794e255532ab461bb0a04feb03e7d + languageName: node + linkType: hard + "strip-ansi@npm:^5.0.0, strip-ansi@npm:^5.2.0": version: 5.2.0 resolution: "strip-ansi@npm:5.2.0" @@ -53591,6 +53907,13 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:^2.0.0": + version: 2.0.0 + resolution: "supports-color@npm:2.0.0" + checksum: 10c0/570e0b63be36cccdd25186350a6cb2eaad332a95ff162fa06d9499982315f2fe4217e69dd98e862fbcd9c81eaff300a825a1fe7bf5cc752e5b84dfed042b0dda + languageName: node + linkType: hard + "supports-color@npm:^5.0.0, supports-color@npm:^5.3.0, supports-color@npm:^5.4.0, supports-color@npm:^5.5.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -53728,7 +54051,7 @@ __metadata: languageName: node linkType: hard -"symbol-observable@npm:^1.0.4": +"symbol-observable@npm:^1.0.4, symbol-observable@npm:^1.1.0": version: 1.2.0 resolution: "symbol-observable@npm:1.2.0" checksum: 10c0/009fee50798ef80ed4b8195048288f108b03de162db07493f2e1fd993b33fafa72d659e832b584da5a2427daa78e5a738fb2a9ab027ee9454252e0bedbcd1fdc @@ -54783,6 +55106,7 @@ __metadata: version: 0.0.0-use.local resolution: "twenty-cli@workspace:packages/twenty-cli" dependencies: + "@genql/cli": "npm:^3.0.3" "@types/fs-extra": "npm:^11.0.0" "@types/inquirer": "npm:^9.0.0" "@types/jest": "npm:^29.5.0" @@ -54798,6 +55122,7 @@ __metadata: commander: "npm:^12.0.0" dotenv: "npm:^16.4.0" fs-extra: "npm:^11.2.0" + graphql: "npm:^16.8.1" inquirer: "npm:^10.0.0" jest: "npm:^29.5.0" jsonc-parser: "npm:^3.2.0" @@ -54805,7 +55130,6 @@ __metadata: lodash.capitalize: "npm:^4.2.1" lodash.kebabcase: "npm:^4.1.1" tsx: "npm:^4.7.0" - twenty-sdk: "workspace:*" typescript: "npm:^5.9.2" uuid: "npm:^13.0.0" wait-on: "npm:^7.2.0" @@ -54972,13 +55296,16 @@ __metadata: languageName: unknown linkType: soft -"twenty-sdk@workspace:*, twenty-sdk@workspace:packages/twenty-sdk": +"twenty-sdk@workspace:packages/twenty-sdk": version: 0.0.0-use.local resolution: "twenty-sdk@workspace:packages/twenty-sdk" dependencies: + "@types/node": "npm:^24.0.0" + twenty-shared: "workspace:*" typescript: "npm:5.9.2" vite: "npm:^7.0.0" vite-plugin-dts: "npm:3.8.1" + vite-tsconfig-paths: "npm:^4.2.1" languageName: unknown linkType: soft @@ -55946,6 +56273,15 @@ __metadata: languageName: node linkType: hard +"undici@npm:^5.18.0": + version: 5.29.0 + resolution: "undici@npm:5.29.0" + dependencies: + "@fastify/busboy": "npm:^2.0.0" + checksum: 10c0/e4e4d631ca54ee0ad82d2e90e7798fa00a106e27e6c880687e445cc2f13b4bc87c5eba2a88c266c3eecffb18f26e227b778412da74a23acc374fca7caccec49b + languageName: node + linkType: hard + "unhead@npm:1.11.20": version: 1.11.20 resolution: "unhead@npm:1.11.20" @@ -57905,6 +58241,16 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^3.0.1": + version: 3.0.1 + resolution: "wrap-ansi@npm:3.0.1" + dependencies: + string-width: "npm:^2.1.1" + strip-ansi: "npm:^4.0.0" + checksum: 10c0/ad6fed8f242c26755badaf452da154122d0d862f8b7aab56e758466857f230efafdc5fbffca026650b947ac3fc0eb563df5c05b9e2190a52a4a68f4eef3d4555 + languageName: node + linkType: hard + "wrap-ansi@npm:^6.0.1, wrap-ansi@npm:^6.2.0": version: 6.2.0 resolution: "wrap-ansi@npm:6.2.0"