Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c27815ca34 | ||
|
|
19aeec66d2 | ||
|
|
16e2204d4d | ||
|
|
611c2706c8 | ||
|
|
df5af17760 |
@@ -5,7 +5,7 @@ import {
|
||||
type ObjectRecordCreateEvent,
|
||||
type ObjectRecordUpdateEvent,
|
||||
} from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '../generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const MAILCHIMP_API_URL: string =
|
||||
process.env.MAILCHIMP_SERVER_PREFIX !== '' &&
|
||||
@@ -88,7 +88,7 @@ type DatabaseEvent =
|
||||
| DatabaseEventPayload<ObjectRecordUpdateEvent<Person>>;
|
||||
|
||||
const fetchCompanyData = async (companyId: string): Promise<twentyCompany> => {
|
||||
const { company } = await new Twenty().query({
|
||||
const { company } = await new CoreApiClient().query({
|
||||
company: {
|
||||
__args: { filter: { id: { eq: companyId } } },
|
||||
name: true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineFunction } from 'twenty-sdk';
|
||||
import { createClient } from '../../generated';
|
||||
import { createClient } from 'twenty-sdk/core-api';
|
||||
|
||||
// TODO: import from twenty-sdk when 0.4.0 is deployed
|
||||
type ServerlessFunctionEvent<TBody = object> = {
|
||||
|
||||
@@ -430,10 +430,10 @@ Each function file uses `defineLogicFunction()` to export a configuration with a
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -642,10 +642,10 @@ To mark a logic function as a tool, set `isTool: true` and provide a `toolInputS
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -773,9 +773,9 @@ You can create new skills in two ways:
|
||||
The typed client is auto-generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated` based on your workspace schema. Use it in your functions:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
@@ -795,13 +795,13 @@ Notes:
|
||||
|
||||
#### Uploading files
|
||||
|
||||
The generated `Twenty` client includes an `uploadFile` method for attaching files to file-type fields on your workspace objects. Because standard GraphQL clients do not support multipart file uploads natively, the client provides this dedicated method that implements the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec) under the hood.
|
||||
The generated `MetadataApiClient` includes an `uploadFile` method for attaching files to file-type fields on your workspace objects. Because standard GraphQL clients do not support multipart file uploads natively, the client provides this dedicated method that implements the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec) under the hood.
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/metadata-api';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
|
||||
@@ -433,10 +433,10 @@ export default defineRole({
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -653,10 +653,10 @@ const handler = async (event: RoutePayload) => {
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -786,9 +786,9 @@ export default defineSkill({
|
||||
يُولَّد العميل مضبوط الأنواع تلقائيًا بواسطة `yarn twenty app:dev` ويُخزَّن في `node_modules/twenty-sdk/generated` استنادًا إلى مخطط مساحة العمل لديك. استخدمه في وظائفك:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
@@ -809,13 +809,13 @@ const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
|
||||
#### رفع الملفات
|
||||
|
||||
يتضمن العميل `Twenty` المُولَّد طريقة `uploadFile` لإرفاق الملفات بالحقول من نوع ملف ضمن كائنات مساحة العمل الخاصة بك. نظرًا لأن عملاء GraphQL القياسيون لا يدعمون تحميل الملفات متعددة الأجزاء افتراضيًا، يوفر العميل هذه الطريقة المخصصة التي تطبق [مواصفة طلب GraphQL متعدد الأجزاء](https://github.com/jaydenseric/graphql-multipart-request-spec) في الخلفية.
|
||||
يتضمن العميل `MetadataApiClient` المُولَّد طريقة `uploadFile` لإرفاق الملفات بالحقول من نوع ملف ضمن كائنات مساحة العمل الخاصة بك. نظرًا لأن عملاء GraphQL القياسيون لا يدعمون تحميل الملفات متعددة الأجزاء افتراضيًا، يوفر العميل هذه الطريقة المخصصة التي تطبق [مواصفة طلب GraphQL متعدد الأجزاء](https://github.com/jaydenseric/graphql-multipart-request-spec) في الخلفية.
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/metadata-api';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
|
||||
@@ -433,10 +433,10 @@ Každý soubor funkce používá `defineLogicFunction()` k exportu konfigurace s
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -653,10 +653,10 @@ Chcete-li označit logickou funkci jako nástroj, nastavte `isTool: true` a posk
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -786,9 +786,9 @@ Nové dovednosti můžete vytvářet dvěma způsoby:
|
||||
Typovaný klient je automaticky generován pomocí `yarn twenty app:dev` a ukládá se do `node_modules/twenty-sdk/generated` podle schématu vašeho pracovního prostoru. Použijte jej ve svých funkcích:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
@@ -809,13 +809,13 @@ Poznámky:
|
||||
|
||||
#### Nahrávání souborů
|
||||
|
||||
Vygenerovaný klient `Twenty` obsahuje metodu `uploadFile` pro připojování souborů k polím typu souboru u objektů ve vašem pracovním prostoru. Protože standardní klienti GraphQL nativně nepodporují nahrávání souborů pomocí multipart, klient poskytuje tuto speciální metodu, která interně implementuje [specifikaci multipart požadavků GraphQL](https://github.com/jaydenseric/graphql-multipart-request-spec).
|
||||
Vygenerovaný klient `MetadataApiClient` obsahuje metodu `uploadFile` pro připojování souborů k polím typu souboru u objektů ve vašem pracovním prostoru. Protože standardní klienti GraphQL nativně nepodporují nahrávání souborů pomocí multipart, klient poskytuje tuto speciální metodu, která interně implementuje [specifikaci multipart požadavků GraphQL](https://github.com/jaydenseric/graphql-multipart-request-spec).
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/metadata-api';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
|
||||
@@ -433,10 +433,10 @@ Jede Funktionsdatei verwendet `defineLogicFunction()`, um eine Konfiguration mit
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -653,10 +653,10 @@ Um eine Logikfunktion als Tool zu markieren, setzen Sie `isTool: true` und geben
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -786,9 +786,9 @@ Sie können neue Skills auf zwei Arten erstellen:
|
||||
Der typisierte Client wird von `yarn twenty app:dev` automatisch generiert und basierend auf Ihrem Arbeitsbereichs-Schema in `node_modules/twenty-sdk/generated` gespeichert. Verwenden Sie ihn in Ihren Funktionen:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
@@ -809,13 +809,13 @@ Notizen:
|
||||
|
||||
#### Dateien hochladen
|
||||
|
||||
Der generierte `Twenty`-Client enthält eine Methode `uploadFile`, um Dateien an Felder des Typs Datei in Ihren Arbeitsbereichsobjekten anzuhängen. Da Standard-GraphQL-Clients Multipart-Datei-Uploads nicht nativ unterstützen, stellt der Client diese dedizierte Methode bereit, die unter der Haube die [GraphQL-Multipart-Anfragespezifikation](https://github.com/jaydenseric/graphql-multipart-request-spec) implementiert.
|
||||
Der generierte `MetadataApiClient`-Client enthält eine Methode `uploadFile`, um Dateien an Felder des Typs Datei in Ihren Arbeitsbereichsobjekten anzuhängen. Da Standard-GraphQL-Clients Multipart-Datei-Uploads nicht nativ unterstützen, stellt der Client diese dedizierte Methode bereit, die unter der Haube die [GraphQL-Multipart-Anfragespezifikation](https://github.com/jaydenseric/graphql-multipart-request-spec) implementiert.
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/metadata-api';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
|
||||
@@ -418,10 +418,10 @@ Cada archivo de función usa `defineFunction()` para exportar una configuración
|
||||
// src/app/createPostCard.function.ts
|
||||
import { defineFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -585,9 +585,9 @@ Puedes crear funciones nuevas de dos maneras:
|
||||
`yarn app:dev` genera automáticamente el cliente Twenty tipado en `node_modules/twenty-sdk/generated`. Úsalo en tus funciones:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
|
||||
@@ -418,10 +418,10 @@ Chaque fichier de fonction utilise `defineFunction()` pour exporter une configur
|
||||
// src/app/createPostCard.function.ts
|
||||
import { defineFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -585,9 +585,9 @@ Vous pouvez créer de nouvelles fonctions de deux façons :
|
||||
`yarn app:dev` génère automatiquement le client Twenty typé dans `node_modules/twenty-sdk/generated`. Utilisez-le dans vos fonctions :
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
|
||||
@@ -433,10 +433,10 @@ Ogni file di funzione usa `defineLogicFunction()` per esportare una configurazio
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -653,10 +653,10 @@ Per contrassegnare una funzione logica come strumento, imposta `isTool: true` e
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -786,9 +786,9 @@ You can create new skills in two ways:
|
||||
Il client tipizzato è generato automaticamente da `yarn twenty app:dev` e salvato in `node_modules/twenty-sdk/generated` in base allo schema della tua area di lavoro. Usalo nelle tue funzioni:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
@@ -809,13 +809,13 @@ Note:
|
||||
|
||||
#### Caricamento dei file
|
||||
|
||||
Il client `Twenty` generato include un metodo `uploadFile` per allegare file ai campi di tipo file sugli oggetti del tuo spazio di lavoro. Poiché i client GraphQL standard non supportano nativamente il caricamento di file multipart, il client fornisce questo metodo dedicato che implementa la [specifica della richiesta GraphQL multipart](https://github.com/jaydenseric/graphql-multipart-request-spec) dietro le quinte.
|
||||
Il client `MetadataApiClient` generato include un metodo `uploadFile` per allegare file ai campi di tipo file sugli oggetti del tuo spazio di lavoro. Poiché i client GraphQL standard non supportano nativamente il caricamento di file multipart, il client fornisce questo metodo dedicato che implementa la [specifica della richiesta GraphQL multipart](https://github.com/jaydenseric/graphql-multipart-request-spec) dietro le quinte.
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/metadata-api';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
|
||||
@@ -416,10 +416,10 @@ export default defineRole({
|
||||
// src/app/createPostCard.function.ts
|
||||
import { defineFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -583,9 +583,9 @@ const handler = async (event: RoutePayload) => {
|
||||
`yarn app:dev` は `node_modules/twenty-sdk/generated` に型付き Twenty クライアントを自動生成します。 関数内で使用します:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
|
||||
@@ -418,10 +418,10 @@ The `universalIdentifier` of this role is then referenced in `application.config
|
||||
// src/app/createPostCard.function.ts
|
||||
import { defineFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -585,9 +585,9 @@ const handler = async (event: RoutePayload) => {
|
||||
`yarn app:dev`는 `node_modules/twenty-sdk/generated`에 타입드 Twenty 클라이언트를 자동으로 생성합니다. 함수에서 사용하세요:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
|
||||
@@ -433,10 +433,10 @@ Cada arquivo de função usa `defineLogicFunction()` para exportar uma configura
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -653,10 +653,10 @@ Para marcar uma função lógica como ferramenta, defina `isTool: true` e forne
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -786,9 +786,9 @@ Você pode criar novas habilidades de duas formas:
|
||||
O cliente tipado é gerado automaticamente pelo `yarn twenty app:dev` e armazenado em `node_modules/twenty-sdk/generated` com base no esquema do seu espaço de trabalho. Use-o em suas funções:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
@@ -809,13 +809,13 @@ Notas:
|
||||
|
||||
#### Carregamento de ficheiros
|
||||
|
||||
O cliente `Twenty` gerado inclui um método `uploadFile` para anexar ficheiros a campos do tipo ficheiro nos objetos do seu espaço de trabalho. Como os clientes GraphQL padrão não suportam nativamente o carregamento de ficheiros multipart, o cliente fornece este método dedicado que implementa, nos bastidores, a [especificação de pedidos multipart do GraphQL](https://github.com/jaydenseric/graphql-multipart-request-spec).
|
||||
O cliente `MetadataApiClient` gerado inclui um método `uploadFile` para anexar ficheiros a campos do tipo ficheiro nos objetos do seu espaço de trabalho. Como os clientes GraphQL padrão não suportam nativamente o carregamento de ficheiros multipart, o cliente fornece este método dedicado que implementa, nos bastidores, a [especificação de pedidos multipart do GraphQL](https://github.com/jaydenseric/graphql-multipart-request-spec).
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/metadata-api';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
|
||||
@@ -433,10 +433,10 @@ Fiecare fișier de funcție folosește `defineLogicFunction()` pentru a exporta
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -653,10 +653,10 @@ Pentru a marca o funcție logică drept instrument, setați `isTool: true` și f
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -786,9 +786,9 @@ Puteți crea abilități noi în două moduri:
|
||||
Clientul tipizat este generat automat de `yarn twenty app:dev` și stocat în `node_modules/twenty-sdk/generated`, pe baza schemei spațiului tău de lucru. Folosiți-l în funcțiile dvs.:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
@@ -809,13 +809,13 @@ Notițe:
|
||||
|
||||
#### Încărcarea fișierelor
|
||||
|
||||
Clientul `Twenty` generat include o metodă `uploadFile` pentru atașarea fișierelor la câmpuri de tip fișier ale obiectelor din spațiul de lucru. Deoarece clienții GraphQL standard nu acceptă în mod nativ încărcarea fișierelor multipart, clientul oferă această metodă dedicată care implementează, sub capotă, [specificația cererilor GraphQL multipart](https://github.com/jaydenseric/graphql-multipart-request-spec).
|
||||
Clientul `MetadataApiClient` generat include o metodă `uploadFile` pentru atașarea fișierelor la câmpuri de tip fișier ale obiectelor din spațiul de lucru. Deoarece clienții GraphQL standard nu acceptă în mod nativ încărcarea fișierelor multipart, clientul oferă această metodă dedicată care implementează, sub capotă, [specificația cererilor GraphQL multipart](https://github.com/jaydenseric/graphql-multipart-request-spec).
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/metadata-api';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
|
||||
@@ -433,10 +433,10 @@ export default defineRole({
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -653,10 +653,10 @@ const handler = async (event: RoutePayload) => {
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -786,9 +786,9 @@ export default defineSkill({
|
||||
Типизированный клиент автоматически генерируется с помощью `yarn twenty app:dev` и сохраняется в `node_modules/twenty-sdk/generated` на основе схемы вашего рабочего пространства. Используйте его в своих функциях:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
@@ -809,13 +809,13 @@ const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
|
||||
#### Загрузка файлов
|
||||
|
||||
Сгенерированный клиент `Twenty` включает метод `uploadFile` для прикрепления файлов к полям типа «файл» в объектах вашего рабочего пространства. Поскольку стандартные клиенты GraphQL изначально не поддерживают многочастовую загрузку файлов, клиент предоставляет специальный метод, который под капотом реализует [спецификацию многочастных запросов GraphQL](https://github.com/jaydenseric/graphql-multipart-request-spec).
|
||||
Сгенерированный клиент `MetadataApiClient` включает метод `uploadFile` для прикрепления файлов к полям типа «файл» в объектах вашего рабочего пространства. Поскольку стандартные клиенты GraphQL изначально не поддерживают многочастовую загрузку файлов, клиент предоставляет специальный метод, который под капотом реализует [спецификацию многочастных запросов GraphQL](https://github.com/jaydenseric/graphql-multipart-request-spec).
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/metadata-api';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
|
||||
@@ -433,10 +433,10 @@ Her fonksiyon dosyası, bir işleyici ve isteğe bağlı tetikleyiciler içeren
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -653,10 +653,10 @@ Bir mantık işlevini bir araç olarak işaretlemek için `isTool: true` olarak
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -786,9 +786,9 @@ Yeni yetenekleri iki şekilde oluşturabilirsiniz:
|
||||
Tipli istemci, `yarn twenty app:dev` tarafından otomatik olarak oluşturulur ve çalışma alanı şemanıza göre `node_modules/twenty-sdk/generated` içine kaydedilir. Fonksiyonlarınızda kullanın:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
@@ -809,13 +809,13 @@ Notlar:
|
||||
|
||||
#### Dosya yükleme
|
||||
|
||||
Oluşturulan `Twenty` istemcisi, çalışma alanı nesnelerinizdeki dosya türündeki alanlara dosya eklemek için bir `uploadFile` yöntemi içerir. Standart GraphQL istemcileri çok parçalı dosya yüklemelerini yerel olarak desteklemediğinden, istemci arka planda [GraphQL çok parçalı istek belirtimi](https://github.com/jaydenseric/graphql-multipart-request-spec) uygulayan bu özel yöntemi sağlar.
|
||||
Oluşturulan `MetadataApiClient` istemcisi, çalışma alanı nesnelerinizdeki dosya türündeki alanlara dosya eklemek için bir `uploadFile` yöntemi içerir. Standart GraphQL istemcileri çok parçalı dosya yüklemelerini yerel olarak desteklemediğinden, istemci arka planda [GraphQL çok parçalı istek belirtimi](https://github.com/jaydenseric/graphql-multipart-request-spec) uygulayan bu özel yöntemi sağlar.
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/metadata-api';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
|
||||
@@ -433,10 +433,10 @@ export default defineRole({
|
||||
// src/app/createPostCard.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
|
||||
import Twenty, { type Person } from '~/generated';
|
||||
import { CoreApiClient, type Person } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: RoutePayload) => {
|
||||
const client = new Twenty(); // generated typed client
|
||||
const client = new CoreApiClient(); // generated typed client
|
||||
const name = 'name' in params.queryStringParameters
|
||||
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
|
||||
: 'Hello world';
|
||||
@@ -653,10 +653,10 @@ const handler = async (event: RoutePayload) => {
|
||||
```typescript
|
||||
// src/logic-functions/enrich-company.logic-function.ts
|
||||
import { defineLogicFunction } from 'twenty-sdk';
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const handler = async (params: { companyName: string; domain?: string }) => {
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
|
||||
const result = await client.mutation({
|
||||
createTask: {
|
||||
@@ -786,9 +786,9 @@ You can create new skills in two ways:
|
||||
类型化客户端由 `yarn twenty app:dev` 自动生成,并基于你的工作区架构存放在 `node_modules/twenty-sdk/generated`。 在你的函数中使用它:
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { CoreApiClient } from 'twenty-sdk/core-api';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new CoreApiClient();
|
||||
const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
```
|
||||
|
||||
@@ -809,13 +809,13 @@ const { me } = await client.query({ me: { id: true, displayName: true } });
|
||||
|
||||
#### 上传文件
|
||||
|
||||
生成的 `Twenty` 客户端包含一个 `uploadFile` 方法,用于将文件附加到工作区对象的文件类型字段。 由于标准 GraphQL 客户端不原生支持多部分文件上传,该客户端提供了一个专用方法,在底层实现了 [GraphQL 多部分请求规范](https://github.com/jaydenseric/graphql-multipart-request-spec)。
|
||||
生成的 `MetadataApiClient` 包含一个 `uploadFile` 方法,用于将文件附加到工作区对象的文件类型字段。 由于标准 GraphQL 客户端不原生支持多部分文件上传,该客户端提供了一个专用方法,在底层实现了 [GraphQL 多部分请求规范](https://github.com/jaydenseric/graphql-multipart-request-spec)。
|
||||
|
||||
```typescript
|
||||
import Twenty from '~/generated';
|
||||
import { MetadataApiClient } from 'twenty-sdk/metadata-api';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const client = new Twenty();
|
||||
const client = new MetadataApiClient();
|
||||
|
||||
const fileBuffer = fs.readFileSync('./invoice.pdf');
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
node_modules
|
||||
.twenty
|
||||
generated
|
||||
generated/*
|
||||
!generated/core
|
||||
generated/core/*
|
||||
!generated/core/index.ts
|
||||
!generated/metadata
|
||||
generated/metadata/*
|
||||
!generated/metadata/index.ts
|
||||
storybook-static
|
||||
src/front-component-renderer/__stories__/example-sources-built
|
||||
src/front-component-renderer/__stories__/example-sources-built-preact
|
||||
|
||||
@@ -40,10 +40,15 @@
|
||||
"import": "./dist/front-component-renderer/index.mjs",
|
||||
"require": "./dist/front-component-renderer/index.cjs"
|
||||
},
|
||||
"./generated": {
|
||||
"types": "./generated/index.ts",
|
||||
"import": "./generated/index.ts",
|
||||
"require": "./generated/index.ts"
|
||||
"./core-api": {
|
||||
"types": "./generated/core/index.ts",
|
||||
"import": "./generated/core/index.ts",
|
||||
"require": "./generated/core/index.ts"
|
||||
},
|
||||
"./metadata-api": {
|
||||
"types": "./generated/metadata/index.ts",
|
||||
"import": "./generated/metadata/index.ts",
|
||||
"require": "./generated/metadata/index.ts"
|
||||
}
|
||||
},
|
||||
"license": "AGPL-3.0",
|
||||
@@ -116,8 +121,11 @@
|
||||
"front-component-renderer": [
|
||||
"dist/front-component-renderer/index.d.ts"
|
||||
],
|
||||
"generated": [
|
||||
"generated/index.ts"
|
||||
"core-api": [
|
||||
"generated/core/index.ts"
|
||||
],
|
||||
"metadata-api": [
|
||||
"generated/metadata/index.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,8 +433,10 @@ export class ApiService {
|
||||
|
||||
async getSchema(options?: {
|
||||
authToken?: string;
|
||||
endpoint?: '/graphql' | '/metadata';
|
||||
}): Promise<ApiResponse<string>> {
|
||||
try {
|
||||
const endpoint = options?.endpoint ?? '/graphql';
|
||||
const introspectionQuery = getIntrospectionQuery();
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
@@ -447,7 +449,7 @@ export class ApiService {
|
||||
}
|
||||
|
||||
const response = await this.client.post(
|
||||
'/graphql',
|
||||
endpoint,
|
||||
{
|
||||
query: introspectionQuery,
|
||||
},
|
||||
|
||||
@@ -10,11 +10,12 @@ import {
|
||||
} from '@/cli/utilities/build/common/restartable-watcher-interface';
|
||||
import { createTypecheckPlugin } from '@/cli/utilities/build/common/typecheck-plugin';
|
||||
import * as esbuild from 'esbuild';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import {
|
||||
OUTPUT_DIR,
|
||||
NODE_ESM_CJS_BANNER,
|
||||
GENERATED_DIR,
|
||||
NODE_ESM_CJS_BANNER,
|
||||
OUTPUT_DIR,
|
||||
} from 'twenty-shared/application';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
|
||||
@@ -37,8 +38,6 @@ export const LOGIC_FUNCTION_EXTERNAL_MODULES: string[] = [
|
||||
'tls',
|
||||
'child_process',
|
||||
'worker_threads',
|
||||
'twenty-sdk',
|
||||
'twenty-sdk/*',
|
||||
'twenty-shared',
|
||||
'twenty-shared/*',
|
||||
];
|
||||
@@ -193,20 +192,48 @@ export class EsbuildWatcher implements RestartableWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolves twenty-sdk/generated to the actual file path so esbuild
|
||||
// bundles it instead of treating it as external (via twenty-sdk/*)
|
||||
// Resolves all twenty-sdk imports to bypass esbuild's package.json exports check.
|
||||
// - twenty-sdk/core-api → generated/core/index.ts (bundled)
|
||||
// - twenty-sdk/metadata-api → generated/metadata/index.ts (bundled)
|
||||
// - twenty-sdk → package entry point (bundled)
|
||||
// - twenty-sdk/* (anything else) → external
|
||||
const SUBPATH_TO_GENERATED_DIR: Record<string, string> = {
|
||||
'twenty-sdk/core-api': 'core',
|
||||
'twenty-sdk/metadata-api': 'metadata',
|
||||
};
|
||||
|
||||
const createSdkGeneratedResolverPlugin = (appPath: string): esbuild.Plugin => ({
|
||||
name: 'sdk-generated-resolver',
|
||||
setup: (build) => {
|
||||
build.onResolve({ filter: /^twenty-sdk\/generated/ }, () => ({
|
||||
path: path.join(
|
||||
appPath,
|
||||
'node_modules',
|
||||
'twenty-sdk',
|
||||
GENERATED_DIR,
|
||||
'index.ts',
|
||||
),
|
||||
}));
|
||||
build.onResolve({ filter: /^twenty-sdk(\/|$)/ }, (args) => {
|
||||
const sdkDir = path.join(appPath, 'node_modules', 'twenty-sdk');
|
||||
|
||||
// twenty-sdk/core-api or twenty-sdk/metadata-api → generated subdirectory
|
||||
const generatedSubDir = SUBPATH_TO_GENERATED_DIR[args.path];
|
||||
|
||||
if (generatedSubDir) {
|
||||
const resolvedPath = path.join(
|
||||
sdkDir,
|
||||
GENERATED_DIR,
|
||||
generatedSubDir,
|
||||
'index.ts',
|
||||
);
|
||||
|
||||
if (!fs.existsSync(resolvedPath)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return { path: resolvedPath };
|
||||
}
|
||||
|
||||
// twenty-sdk (main package) → resolve to dist entry point for bundling
|
||||
if (args.path === 'twenty-sdk') {
|
||||
return { path: path.join(sdkDir, 'dist', 'index.mjs') };
|
||||
}
|
||||
|
||||
// any other twenty-sdk/* subpath → keep as external
|
||||
return { path: args.path, external: true };
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+5
-1
@@ -6,7 +6,11 @@ import os from 'os';
|
||||
import path from 'path';
|
||||
import { isDefined, isPlainObject } from 'twenty-shared/utils';
|
||||
|
||||
const MANIFEST_MOCK_MODULES = ['twenty-sdk/ui'];
|
||||
const MANIFEST_MOCK_MODULES = [
|
||||
'twenty-sdk/ui',
|
||||
'twenty-sdk/core-api',
|
||||
'twenty-sdk/metadata-api',
|
||||
];
|
||||
|
||||
const manifestMockPlugin: esbuild.Plugin = {
|
||||
name: 'manifest-mock',
|
||||
|
||||
@@ -25,22 +25,66 @@ export class ClientService {
|
||||
const outputPath = this.resolveGeneratedPath(appPath);
|
||||
const tempPath = `${outputPath}.tmp`;
|
||||
|
||||
const getSchemaResponse = await this.apiService.getSchema({ authToken });
|
||||
await fs.ensureDir(tempPath);
|
||||
await fs.emptyDir(tempPath);
|
||||
|
||||
await this.generateSubClient({
|
||||
authToken,
|
||||
endpoint: '/graphql',
|
||||
subDir: 'core',
|
||||
className: 'CoreApiClient',
|
||||
urlSuffix: '/graphql',
|
||||
tempPath,
|
||||
});
|
||||
|
||||
await this.generateSubClient({
|
||||
authToken,
|
||||
endpoint: '/metadata',
|
||||
subDir: 'metadata',
|
||||
className: 'MetadataApiClient',
|
||||
urlSuffix: '/metadata',
|
||||
tempPath,
|
||||
});
|
||||
|
||||
await fs.remove(outputPath);
|
||||
await fs.move(tempPath, outputPath);
|
||||
}
|
||||
|
||||
private async generateSubClient({
|
||||
authToken,
|
||||
endpoint,
|
||||
subDir,
|
||||
className,
|
||||
urlSuffix,
|
||||
tempPath,
|
||||
}: {
|
||||
authToken?: string;
|
||||
endpoint: '/graphql' | '/metadata';
|
||||
subDir: string;
|
||||
className: string;
|
||||
urlSuffix: string;
|
||||
tempPath: string;
|
||||
}): Promise<void> {
|
||||
const getSchemaResponse = await this.apiService.getSchema({
|
||||
authToken,
|
||||
endpoint,
|
||||
});
|
||||
|
||||
if (!getSchemaResponse.success) {
|
||||
throw new Error(
|
||||
`Failed to introspect schema: ${JSON.stringify(getSchemaResponse.error)}`,
|
||||
`Failed to introspect schema (${endpoint}): ${JSON.stringify(getSchemaResponse.error)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const { data: schema } = getSchemaResponse;
|
||||
|
||||
await fs.ensureDir(tempPath);
|
||||
await fs.emptyDir(tempPath);
|
||||
const subPath = join(tempPath, subDir);
|
||||
|
||||
await fs.ensureDir(subPath);
|
||||
|
||||
await generate({
|
||||
schema,
|
||||
output: tempPath,
|
||||
output: subPath,
|
||||
scalarTypes: {
|
||||
DateTime: 'string',
|
||||
JSON: 'Record<string, unknown>',
|
||||
@@ -48,40 +92,61 @@ export class ClientService {
|
||||
},
|
||||
});
|
||||
|
||||
await this.injectTwentyClient(tempPath);
|
||||
|
||||
await fs.remove(outputPath);
|
||||
await fs.move(tempPath, outputPath);
|
||||
await this.injectApiClient(subPath, className, urlSuffix);
|
||||
}
|
||||
|
||||
private resolveGeneratedPath(appPath: string): string {
|
||||
return join(appPath, 'node_modules', 'twenty-sdk', GENERATED_DIR);
|
||||
}
|
||||
|
||||
private async injectTwentyClient(output: string) {
|
||||
const twentyClientContent = `
|
||||
private async injectApiClient(
|
||||
output: string,
|
||||
className: string,
|
||||
urlSuffix: string,
|
||||
) {
|
||||
const clientContent = this.buildApiClientCode({
|
||||
className,
|
||||
urlSuffix,
|
||||
includeUploadFile: urlSuffix === '/metadata',
|
||||
});
|
||||
|
||||
await fs.appendFile(join(output, 'index.ts'), clientContent);
|
||||
}
|
||||
|
||||
private buildApiClientCode({
|
||||
className,
|
||||
urlSuffix,
|
||||
includeUploadFile,
|
||||
}: {
|
||||
className: string;
|
||||
urlSuffix: string;
|
||||
includeUploadFile: boolean;
|
||||
}): string {
|
||||
const uploadFileMethod = includeUploadFile
|
||||
? this.buildUploadFileMethod()
|
||||
: '';
|
||||
|
||||
return `
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Custom Twenty client (auto-injected)
|
||||
// ${className} (auto-injected)
|
||||
// ----------------------------------------------------
|
||||
|
||||
const defaultOptions: ClientOptions = {
|
||||
url: \`\${process.env.${DEFAULT_API_URL_NAME}}/graphql\`,
|
||||
metadataUrl: \`\${process.env.${DEFAULT_API_URL_NAME}}/metadata\`,
|
||||
url: \`\${process.env.${DEFAULT_API_URL_NAME}}${urlSuffix}\`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: \`Bearer \${process.env.${DEFAULT_API_KEY_NAME}}\`,
|
||||
},
|
||||
}
|
||||
|
||||
export default class Twenty {
|
||||
export class ${className} {
|
||||
private client: Client;
|
||||
private url: string;
|
||||
private metadataUrl: string;
|
||||
private authorizationToken: string;
|
||||
|
||||
constructor(options?: ClientOptions) {
|
||||
const merged: ClientOptions = {
|
||||
const merged: ClientOptions = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
headers: {
|
||||
@@ -91,7 +156,6 @@ export default class Twenty {
|
||||
};
|
||||
this.client = createClient(merged);
|
||||
this.url = merged.url;
|
||||
this.metadataUrl = merged.metadataUrl;
|
||||
this.authorizationToken = merged.headers.Authorization;
|
||||
}
|
||||
|
||||
@@ -102,7 +166,14 @@ export default class Twenty {
|
||||
mutation<R extends MutationGenqlSelection>(request: R & { __name?: string }) {
|
||||
return this.client.mutation(request);
|
||||
}
|
||||
${uploadFileMethod}
|
||||
}
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
private buildUploadFileMethod(): string {
|
||||
return `
|
||||
async uploadFile(
|
||||
fileBuffer: Buffer,
|
||||
filename: string,
|
||||
@@ -129,7 +200,7 @@ export default class Twenty {
|
||||
form.append('map', JSON.stringify({ '0': ['variables.file'] }));
|
||||
form.append('0', new Blob([fileBuffer], { type: contentType }), filename);
|
||||
|
||||
const response = await fetch(this.metadataUrl, {
|
||||
const response = await fetch(this.url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: this.authorizationToken,
|
||||
@@ -144,11 +215,6 @@ export default class Twenty {
|
||||
}
|
||||
|
||||
return result.data.uploadFilesFieldFileByUniversalIdentifier;
|
||||
}
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
await fs.appendFile(join(output, 'index.ts'), twentyClientContent);
|
||||
}`;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -13,7 +13,12 @@ import {
|
||||
} from 'twenty-shared/application';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
|
||||
const API_CLIENT_FILES = ['types.ts', 'schema.ts'];
|
||||
const API_CLIENT_FILES = [
|
||||
'core/types.ts',
|
||||
'core/schema.ts',
|
||||
'metadata/types.ts',
|
||||
'metadata/schema.ts',
|
||||
];
|
||||
|
||||
export type UploadFilesOrchestratorStepOutput = {
|
||||
fileUploader: FileUploader | null;
|
||||
@@ -128,6 +133,8 @@ export class UploadFilesOrchestratorStep {
|
||||
const outputDir = join(appPath, OUTPUT_DIR, API_CLIENT_DIR);
|
||||
|
||||
await fs.ensureDir(outputDir);
|
||||
await fs.ensureDir(join(outputDir, 'core'));
|
||||
await fs.ensureDir(join(outputDir, 'metadata'));
|
||||
|
||||
for (const fileName of API_CLIENT_FILES) {
|
||||
const absoluteSourcePath = join(generatedDir, fileName);
|
||||
|
||||
Reference in New Issue
Block a user