Update manifest structure (#17547)

Move all sync entities in an `entities` key. Rename functions to
logicFunctions

```json
{
  application: {
    ...
  },
  entities: {
    objects: [],
    logicFunctions: [],
    ...
  }
}
```
This commit is contained in:
martmull
2026-01-30 16:26:45 +01:00
committed by GitHub
parent bc022f82cb
commit f46da3eefd
162 changed files with 2555 additions and 3778 deletions
@@ -32,7 +32,7 @@ describe('copyBaseApplicationProject', () => {
}
});
it('should create the correct folder structure with src/app/', async () => {
it('should create the correct folder structure with src/', async () => {
await copyBaseApplicationProject({
appName: 'my-test-app',
appDisplayName: 'My Test App',
@@ -48,8 +48,8 @@ describe('copyBaseApplicationProject', () => {
const appConfigPath = join(srcAppPath, 'application.config.ts');
expect(await fs.pathExists(appConfigPath)).toBe(true);
// Verify default-function.role.ts exists in src/app/
const roleConfigPath = join(srcAppPath, 'default-function.role.ts');
// Verify default.role.ts exists in src/app/
const roleConfigPath = join(srcAppPath, 'default.role.ts');
expect(await fs.pathExists(roleConfigPath)).toBe(true);
});
@@ -102,7 +102,7 @@ describe('copyBaseApplicationProject', () => {
expect(yarnLockContent).toContain('yarn lockfile v1');
});
it('should create application.config.ts with defineApp and correct values', async () => {
it('should create application.config.ts with defineApplication and correct values', async () => {
await copyBaseApplicationProject({
appName: 'my-test-app',
appDisplayName: 'My Test App',
@@ -117,15 +117,15 @@ describe('copyBaseApplicationProject', () => {
);
const appConfigContent = await fs.readFile(appConfigPath, 'utf8');
// Verify it uses defineApp
// Verify it uses defineApplication
expect(appConfigContent).toContain(
"import { defineApp } from 'twenty-sdk'",
"import { defineApplication } from 'twenty-sdk'",
);
expect(appConfigContent).toContain('export default defineApp({');
expect(appConfigContent).toContain('export default defineApplication({');
// Verify it imports the role identifier
expect(appConfigContent).toContain(
"import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from 'src/default-function.role'",
"import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/default.role'",
);
// Verify display name and description
@@ -139,11 +139,11 @@ describe('copyBaseApplicationProject', () => {
// Verify it references the role
expect(appConfigContent).toContain(
'functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER',
'defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER',
);
});
it('should create default-function.role.ts with defineRole and correct values', async () => {
it('should create default.role.ts with defineRole and correct values', async () => {
await copyBaseApplicationProject({
appName: 'my-test-app',
appDisplayName: 'My Test App',
@@ -151,11 +151,7 @@ describe('copyBaseApplicationProject', () => {
appDirectory: testAppDirectory,
});
const roleConfigPath = join(
testAppDirectory,
'src',
'default-function.role.ts',
);
const roleConfigPath = join(testAppDirectory, 'src', 'default.role.ts');
const roleConfigContent = await fs.readFile(roleConfigPath, 'utf8');
// Verify it uses defineRole
@@ -166,7 +162,7 @@ describe('copyBaseApplicationProject', () => {
// Verify it exports the universal identifier constant
expect(roleConfigContent).toContain(
'export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER',
'export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER',
);
// Verify role label includes app name
@@ -182,7 +178,7 @@ describe('copyBaseApplicationProject', () => {
// Verify it has a universalIdentifier (UUID format)
expect(roleConfigContent).toMatch(
/universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER/,
/universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER/,
);
});
@@ -283,19 +279,19 @@ describe('copyBaseApplicationProject', () => {
appDirectory: secondAppDir,
});
// Read both role configs
const firstRoleConfig = await fs.readFile(
join(firstAppDir, 'src', 'default-function.role.ts'),
join(firstAppDir, 'src', 'default.role.ts'),
'utf8',
);
const secondRoleConfig = await fs.readFile(
join(secondAppDir, 'src', 'default-function.role.ts'),
join(secondAppDir, 'src', 'default.role.ts'),
'utf8',
);
// Extract UUIDs using regex
const uuidRegex =
/DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =\s*'([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})'/;
/DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =\s*'([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})'/;
const firstUuid = firstRoleConfig.match(uuidRegex)?.[1];
const secondUuid = secondRoleConfig.match(uuidRegex)?.[1];
@@ -3,7 +3,7 @@ import { join } from 'path';
import { v4 } from 'uuid';
import { ASSETS_DIR } from 'twenty-shared/application';
const APP_FOLDER = 'src';
const SRC_FOLDER = 'src';
export const copyBaseApplicationProject = async ({
appName,
@@ -26,27 +26,27 @@ export const copyBaseApplicationProject = async ({
await createYarnLock(appDirectory);
const appFolderPath = join(appDirectory, APP_FOLDER);
const sourceFolderPath = join(appDirectory, SRC_FOLDER);
await fs.ensureDir(appFolderPath);
await fs.ensureDir(sourceFolderPath);
await createDefaultServerlessFunctionRoleConfig({
await createDefaultRoleConfig({
displayName: appDisplayName,
appDirectory: appFolderPath,
appDirectory: sourceFolderPath,
});
await createDefaultFrontComponent({
appDirectory: appFolderPath,
appDirectory: sourceFolderPath,
});
await createDefaultFunction({
appDirectory: appFolderPath,
appDirectory: sourceFolderPath,
});
await createApplicationConfig({
displayName: appDisplayName,
description: appDescription,
appDirectory: appFolderPath,
appDirectory: sourceFolderPath,
});
};
@@ -103,7 +103,7 @@ yarn-error.log*
await fs.writeFile(join(appDirectory, '.gitignore'), gitignoreContent);
};
const createDefaultServerlessFunctionRoleConfig = async ({
const createDefaultRoleConfig = async ({
displayName,
appDirectory,
}: {
@@ -114,11 +114,11 @@ const createDefaultServerlessFunctionRoleConfig = async ({
const content = `import { defineRole } from 'twenty-sdk';
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER =
'${universalIdentifier}';
export default defineRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
label: '${displayName} default function role',
description: '${displayName} default function role',
canReadAllObjectRecords: true,
@@ -128,7 +128,7 @@ export default defineRole({
});
`;
await fs.writeFile(join(appDirectory, 'default-function.role.ts'), content);
await fs.writeFile(join(appDirectory, 'default.role.ts'), content);
};
const createDefaultFrontComponent = async ({
@@ -171,23 +171,24 @@ const createDefaultFunction = async ({
const universalIdentifier = v4();
const triggerUniversalIdentifier = v4();
const content = `import { defineFunction } from 'twenty-sdk';
const content = `import { defineLogicFunction } from 'twenty-sdk';
const handler = async (): Promise<{ message: string }> => {
return { message: 'Hello, World!' };
};
export default defineFunction({
// Logic function handler - rename and implement your logic
export default defineLogicFunction({
universalIdentifier: '${universalIdentifier}',
name: 'hello-world-function',
description: 'A sample serverless function',
name: 'hello-world-logic-function',
description: 'A simple logic function',
timeoutSeconds: 5,
handler,
triggers: [
{
universalIdentifier: '${triggerUniversalIdentifier}',
type: 'route',
path: '/hello-world-function',
path: '/hello-world-logic-function',
httpMethod: 'GET',
isAuthRequired: false,
},
@@ -195,7 +196,10 @@ export default defineFunction({
});
`;
await fs.writeFile(join(appDirectory, 'hello-world.function.ts'), content);
await fs.writeFile(
join(appDirectory, 'hello-world.logic-function.ts'),
content,
);
};
const createApplicationConfig = async ({
@@ -207,14 +211,14 @@ const createApplicationConfig = async ({
description?: string;
appDirectory: string;
}) => {
const content = `import { defineApp } from 'twenty-sdk';
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from 'src/default-function.role';
const content = `import { defineApplication } from 'twenty-sdk';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/default.role';
export default defineApp({
export default defineApplication({
universalIdentifier: '${v4()}',
displayName: '${displayName}',
description: '${description ?? ''}',
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
});
`;