mirror of
https://github.com/twentyhq/twenty.git
synced 2026-04-19 22:39:30 -04:00
1750 extensibility twenty sdk v2 use twenty sdk to define an object (#15230)
We maintain jsonc object definition but will deprecate them pretty soon ## Before <img width="1512" height="575" alt="image" src="https://github.com/user-attachments/assets/d2fa6ca4-c456-4aa9-a1e3-845b61839718" /> ## After <img width="1260" height="555" alt="image" src="https://github.com/user-attachments/assets/ba72f4cf-d443-4967-913c-029bc71f3f48" />
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-cli/schemas/object.schema.json",
|
||||
"universalIdentifier": "54b589ca-eeed-4950-a176-358418b85c05",
|
||||
"standardId": "54b589ca-eeed-4950-a176-358418b85c05",
|
||||
"nameSingular": "postCard",
|
||||
"namePlural": "postCards",
|
||||
"labelSingular": "Post card",
|
||||
"labelPlural": "Post cards"
|
||||
}
|
||||
@@ -19,7 +19,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.12.2"
|
||||
"axios": "^1.12.2",
|
||||
"twenty-sdk": "^0.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.7.2"
|
||||
|
||||
10
packages/twenty-apps/hello-world/src/postCard.ts
Normal file
10
packages/twenty-apps/hello-world/src/postCard.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ObjectMetadata } from 'twenty-sdk';
|
||||
|
||||
@ObjectMetadata({
|
||||
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
|
||||
nameSingular: 'postCard',
|
||||
namePlural: 'postCards',
|
||||
labelSingular: 'Post card',
|
||||
labelPlural: 'Post cards',
|
||||
})
|
||||
export class PostCard {}
|
||||
22
packages/twenty-apps/hello-world/tsconfig.json
Normal file
22
packages/twenty-apps/hello-world/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": ".",
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"strict": true,
|
||||
"target": "es2018",
|
||||
"module": "esnext",
|
||||
"lib": ["es2020", "dom"],
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@@ -5,11 +5,22 @@ __metadata:
|
||||
version: 8
|
||||
cacheKey: 10c0
|
||||
|
||||
"@types/node@npm:^24.7.2":
|
||||
version: 24.9.1
|
||||
resolution: "@types/node@npm:24.9.1"
|
||||
dependencies:
|
||||
undici-types: "npm:~7.16.0"
|
||||
checksum: 10c0/c52f8168080ef9a7c3dc23d8ac6061fab5371aad89231a0f6f4c075869bc3de7e89b075b1f3e3171d9e5143d0dda1807c3dab8e32eac6d68f02e7480e7e78576
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"Hello world@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "Hello world@workspace:."
|
||||
dependencies:
|
||||
"@types/node": "npm:^24.7.2"
|
||||
axios: "npm:^1.12.2"
|
||||
twenty-sdk: "npm:^0.0.2"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
@@ -246,3 +257,17 @@ __metadata:
|
||||
checksum: 10c0/fe7dd8b1bdbbbea18d1459107729c3e4a2243ca870d26d34c2c1bcd3e4425b7bcc5112362df2d93cc7fb9746f6142b5e272fd1cc5c86ddf8580175186f6ad42b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"twenty-sdk@npm:^0.0.2":
|
||||
version: 0.0.2
|
||||
resolution: "twenty-sdk@npm:0.0.2"
|
||||
checksum: 10c0/99e6fe86059d847b548c1f03e0f0c59a4d540caf1d28dd4500f1f5f0094196985ded955801274de9e72ff03e3d1f41e9a509b4c2c5a02ffc8a027277b1e35d8e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"undici-types@npm:~7.16.0":
|
||||
version: 7.16.0
|
||||
resolution: "undici-types@npm:7.16.0"
|
||||
checksum: 10c0/3033e2f2b5c9f1504bdc5934646cb54e37ecaca0f9249c983f7b1fc2e87c6d18399ebb05dc7fd5419e02b2e915f734d872a65da2e3eeed1813951c427d33cc9a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -30,7 +30,7 @@ const jestConfig = {
|
||||
],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
statements: 2,
|
||||
statements: 1,
|
||||
lines: 1,
|
||||
functions: 1,
|
||||
},
|
||||
|
||||
@@ -35,17 +35,21 @@
|
||||
"fs-extra": "^11.2.0",
|
||||
"inquirer": "^10.0.0",
|
||||
"jsonc-parser": "^3.2.0",
|
||||
"lodash.kebabcase": "^4.1.1"
|
||||
"lodash.camelcase": "^4.3.0",
|
||||
"lodash.capitalize": "^4.2.1",
|
||||
"lodash.kebabcase": "^4.1.1",
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^11.0.0",
|
||||
"@types/inquirer": "^9.0.0",
|
||||
"@types/jest": "^29.5.0",
|
||||
"@types/lodash.camelcase": "^4.3.7",
|
||||
"@types/lodash.capitalize": "^4",
|
||||
"@types/lodash.kebabcase": "^4.1.7",
|
||||
"@types/node": "^20.0.0",
|
||||
"jest": "^29.5.0",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.3.0",
|
||||
"wait-on": "^7.2.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -3,11 +3,13 @@ import { randomUUID } from 'crypto';
|
||||
import * as fs from 'fs-extra';
|
||||
import inquirer from 'inquirer';
|
||||
import path from 'path';
|
||||
import camelcase from 'lodash.camelcase';
|
||||
import { CURRENT_EXECUTION_DIRECTORY } from '../constants/current-execution-directory';
|
||||
import { HTTPMethod } from '../types/config.types';
|
||||
import { parseJsoncFile, writeJsoncFile } from '../utils/jsonc-parser';
|
||||
import { getSchemaUrls } from '../utils/schema-validator';
|
||||
import { BASE_SCHEMAS_PATH } from '../constants/constants-path';
|
||||
import { getDecoratedClass } from '../utils/get-decorated-class';
|
||||
|
||||
export enum SyncableEntity {
|
||||
AGENT = 'agent',
|
||||
@@ -56,6 +58,22 @@ export class AppAddCommand {
|
||||
|
||||
const entityData = await this.getEntityToCreateData(entity, entityName);
|
||||
|
||||
if (entity === SyncableEntity.OBJECT) {
|
||||
delete entityData['standardId'];
|
||||
delete entityData['$schema'];
|
||||
|
||||
const objectFileName = `${camelcase(entityName)}.ts`;
|
||||
|
||||
const decoratedObject = getDecoratedClass({
|
||||
data: entityData,
|
||||
name: entityName,
|
||||
});
|
||||
|
||||
await fs.writeFile(path.join(appPath, objectFileName), decoratedObject);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const folderName = getFolderName(entity);
|
||||
|
||||
const entitiesDir = path.join(appPath, folderName, entityName);
|
||||
|
||||
5
packages/twenty-cli/src/constants/base-application-project/.gitignore
vendored
Normal file
5
packages/twenty-cli/src/constants/base-application-project/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Duplicated with ./gitignore because npm publish does not include .gitignore
|
||||
# https://github.com/npm/npm/issues/3763
|
||||
|
||||
.yarn/install-state.gz
|
||||
.env
|
||||
@@ -7,6 +7,9 @@
|
||||
"yarn": ">=4.0.2"
|
||||
},
|
||||
"packageManager": "yarn@4.9.2",
|
||||
"dependencies": {
|
||||
"twenty-sdk": "^0.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.7.2"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": ".",
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"strict": true,
|
||||
"target": "es2018",
|
||||
"module": "esnext",
|
||||
"lib": ["es2020", "dom"],
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
# This file is generated by running "yarn install" inside your project.
|
||||
# Manual changes might be lost - proceed with caution!
|
||||
|
||||
__metadata:
|
||||
version: 8
|
||||
cacheKey: 10c0
|
||||
|
||||
"@types/node@npm:^24.7.2":
|
||||
version: 24.9.1
|
||||
resolution: "@types/node@npm:24.9.1"
|
||||
dependencies:
|
||||
undici-types: "npm:~7.16.0"
|
||||
checksum: 10c0/c52f8168080ef9a7c3dc23d8ac6061fab5371aad89231a0f6f4c075869bc3de7e89b075b1f3e3171d9e5143d0dda1807c3dab8e32eac6d68f02e7480e7e78576
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"root-workspace-0b6124@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "root-workspace-0b6124@workspace:."
|
||||
dependencies:
|
||||
"@types/node": "npm:^24.7.2"
|
||||
twenty-sdk: "npm:^0.0.2"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"twenty-sdk@npm:^0.0.2":
|
||||
version: 0.0.2
|
||||
resolution: "twenty-sdk@npm:0.0.2"
|
||||
checksum: 10c0/99e6fe86059d847b548c1f03e0f0c59a4d540caf1d28dd4500f1f5f0094196985ded955801274de9e72ff03e3d1f41e9a509b4c2c5a02ffc8a027277b1e35d8e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"undici-types@npm:~7.16.0":
|
||||
version: 7.16.0
|
||||
resolution: "undici-types@npm:7.16.0"
|
||||
checksum: 10c0/3033e2f2b5c9f1504bdc5934646cb54e37ecaca0f9249c983f7b1fc2e87c6d18399ebb05dc7fd5419e02b2e915f734d872a65da2e3eeed1813951c427d33cc9a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -78,6 +78,7 @@ export type ServerlessFunctionCodeManifest = {
|
||||
export type ObjectManifest = {
|
||||
$schema?: string;
|
||||
standardId: string;
|
||||
universalIdentifier: string;
|
||||
nameSingular: string;
|
||||
namePlural: string;
|
||||
labelSingular: string;
|
||||
@@ -89,6 +90,7 @@ export type ObjectManifest = {
|
||||
export type AgentManifest = {
|
||||
$schema?: string;
|
||||
standardId: string;
|
||||
universalIdentifier: string;
|
||||
name: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { getDecoratedClass } from '../../utils/get-decorated-class';
|
||||
|
||||
describe('getDecoratedClass', () => {
|
||||
it('should return properly formatted class', () => {
|
||||
const result = getDecoratedClass({
|
||||
data: { nameSingular: 'Name', namePlural: 'Names' },
|
||||
name: 'MyNewObject',
|
||||
});
|
||||
|
||||
const expectedResult = `import { ObjectMetadata } from 'twenty-sdk';
|
||||
|
||||
@ObjectMetadata({
|
||||
nameSingular: 'Name',
|
||||
namePlural: 'Names',
|
||||
})
|
||||
export class MyNewObject {}
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
});
|
||||
@@ -5,10 +5,12 @@ import * as path from 'path';
|
||||
import {
|
||||
AppManifest,
|
||||
CoreEntityManifest,
|
||||
ObjectManifest,
|
||||
PackageJson,
|
||||
} from '../types/config.types';
|
||||
import { validateSchema } from '../utils/schema-validator';
|
||||
import { parseJsoncFile } from './jsonc-parser';
|
||||
import { loadManifestFromDecorators } from '../utils/load-manifest-from-decorators';
|
||||
|
||||
type Sources = { [key: string]: string | Sources };
|
||||
|
||||
@@ -156,7 +158,7 @@ export const loadManifest = async (
|
||||
(manifest, path) => validateSchema('agent', manifest, path),
|
||||
);
|
||||
|
||||
const objects = await loadCoreEntity(
|
||||
const objectFromManifests = await loadCoreEntity(
|
||||
path.join(appPath, 'objects'),
|
||||
(manifest, path) => validateSchema('object', manifest, path),
|
||||
);
|
||||
@@ -166,6 +168,15 @@ export const loadManifest = async (
|
||||
(manifest, path) => validateSchema('serverlessFunction', manifest, path),
|
||||
);
|
||||
|
||||
const { objects: objectsFromDecorators } = loadManifestFromDecorators();
|
||||
|
||||
const objects = (
|
||||
[...objectFromManifests, ...objectsFromDecorators] as ObjectManifest[]
|
||||
).map((object) => {
|
||||
object.standardId = object.universalIdentifier;
|
||||
return object;
|
||||
});
|
||||
|
||||
return {
|
||||
packageJson,
|
||||
yarnLock: rawYarnLock,
|
||||
|
||||
25
packages/twenty-cli/src/utils/get-decorated-class.ts
Normal file
25
packages/twenty-cli/src/utils/get-decorated-class.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import camelcase from 'lodash.camelcase';
|
||||
|
||||
export const getDecoratedClass = ({
|
||||
data,
|
||||
name,
|
||||
}: {
|
||||
data: object;
|
||||
name: string;
|
||||
}) => {
|
||||
const decoratorOptions = Object.entries(data)
|
||||
.map(([key, value]) => ` ${key}: '${value}',`)
|
||||
.join('\n');
|
||||
|
||||
const camelCaseName = camelcase(name);
|
||||
|
||||
const className = camelCaseName[0].toUpperCase() + camelCaseName.slice(1);
|
||||
|
||||
return `import { ObjectMetadata } from 'twenty-sdk';
|
||||
|
||||
@ObjectMetadata({
|
||||
${decoratorOptions}
|
||||
})
|
||||
export class ${className} {}
|
||||
`;
|
||||
};
|
||||
175
packages/twenty-cli/src/utils/load-manifest-from-decorators.ts
Normal file
175
packages/twenty-cli/src/utils/load-manifest-from-decorators.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import {
|
||||
sys,
|
||||
getDecorators,
|
||||
readConfigFile,
|
||||
parseJsonConfigFileContent,
|
||||
formatDiagnosticsWithColorAndContext,
|
||||
createProgram,
|
||||
Decorator,
|
||||
isPropertyAccessExpression,
|
||||
isNumericLiteral,
|
||||
SyntaxKind,
|
||||
isArrayLiteralExpression,
|
||||
Expression,
|
||||
isPropertyAssignment,
|
||||
isComputedPropertyName,
|
||||
isStringLiteralLike,
|
||||
isShorthandPropertyAssignment,
|
||||
isIdentifier,
|
||||
Program,
|
||||
Node,
|
||||
isClassDeclaration,
|
||||
isCallExpression,
|
||||
isObjectLiteralExpression,
|
||||
forEachChild,
|
||||
} from 'typescript';
|
||||
import { AppManifest, ObjectManifest } from '../types/config.types';
|
||||
|
||||
type JSONValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| JSONValue[]
|
||||
| { [k: string]: JSONValue };
|
||||
|
||||
const getProgramFromTsconfig = (tsconfigPath = 'tsconfig.json') => {
|
||||
const basePath = process.cwd();
|
||||
const configFile = readConfigFile(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, basePath);
|
||||
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)) {
|
||||
if (isIdentifier(expr.expression)) return expr.expression.text === name;
|
||||
if (isPropertyAccessExpression(expr.expression))
|
||||
return expr.expression.name.text === name;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const exprToValue = (expr: Expression): JSONValue => {
|
||||
if (isStringLiteralLike(expr)) return expr.text;
|
||||
if (isNumericLiteral(expr)) return Number(expr.text);
|
||||
if (expr.kind === SyntaxKind.TrueKeyword) return true;
|
||||
if (expr.kind === SyntaxKind.FalseKeyword) return false;
|
||||
if (expr.kind === SyntaxKind.NullKeyword) return null;
|
||||
|
||||
if (isArrayLiteralExpression(expr)) {
|
||||
return expr.elements.map((e) =>
|
||||
e.kind === SyntaxKind.SpreadElement ? [] : exprToValue(e),
|
||||
);
|
||||
}
|
||||
|
||||
if (isObjectLiteralExpression(expr)) {
|
||||
const obj: Record<string, JSONValue> = {};
|
||||
for (const prop of expr.properties) {
|
||||
if (isPropertyAssignment(prop)) {
|
||||
const key =
|
||||
isIdentifier(prop.name) || isStringLiteralLike(prop.name)
|
||||
? prop.name.text
|
||||
: isComputedPropertyName(prop.name) &&
|
||||
isStringLiteralLike(prop.name.expression)
|
||||
? prop.name.expression.text
|
||||
: undefined;
|
||||
if (key) obj[key] = exprToValue(prop.initializer);
|
||||
} else if (isShorthandPropertyAssignment(prop)) {
|
||||
// Unsupported without a checker; skip to keep it "light".
|
||||
// Could resolve via typechecker if needed.
|
||||
}
|
||||
// getters/setters/methods are ignored intentionally
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Keep it intentionally strict/lightweight: anything non-literal becomes a string fallback.
|
||||
// You can throw instead if you prefer to fail fast.
|
||||
return isIdentifier(expr)
|
||||
? expr.text
|
||||
: String((expr as any).getText?.() ?? '');
|
||||
};
|
||||
|
||||
const collectObjects = (program: Program) => {
|
||||
const manifest: ObjectManifest[] = [];
|
||||
|
||||
for (const sf of program.getSourceFiles()) {
|
||||
if (sf.isDeclarationFile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const visit = (node: Node) => {
|
||||
if (isClassDeclaration(node) && getDecorators(node)?.length) {
|
||||
const decorators = getDecorators(node);
|
||||
const objectDec = decorators?.find((d) =>
|
||||
isDecoratorNamed(d, 'ObjectMetadata'),
|
||||
);
|
||||
if (objectDec && isCallExpression(objectDec.expression)) {
|
||||
const [firstArg] = objectDec.expression.arguments;
|
||||
if (firstArg && isObjectLiteralExpression(firstArg)) {
|
||||
const config = exprToValue(firstArg);
|
||||
if (
|
||||
config &&
|
||||
typeof config === 'object' &&
|
||||
!Array.isArray(config)
|
||||
) {
|
||||
manifest.push({
|
||||
...config,
|
||||
} as ObjectManifest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
forEachChild(node, visit);
|
||||
};
|
||||
|
||||
visit(sf);
|
||||
}
|
||||
|
||||
return manifest;
|
||||
};
|
||||
|
||||
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}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const loadManifestFromDecorators = (): Pick<AppManifest, 'objects'> => {
|
||||
const program = getProgramFromTsconfig('tsconfig.json');
|
||||
|
||||
validateProgram(program);
|
||||
|
||||
const objects = collectObjects(program);
|
||||
|
||||
return { objects };
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "twenty-sdk",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.mjs",
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export { };
|
||||
|
||||
export { ObjectMetadata } from './object-metadata.decorator';
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
type ObjectMetadataOptions = {
|
||||
universalIdentifier: string;
|
||||
nameSingular: string;
|
||||
namePlural: string;
|
||||
labelSingular: string;
|
||||
labelPlural: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
};
|
||||
|
||||
export const ObjectMetadata = (_: ObjectMetadataOptions): ClassDecorator => {
|
||||
return () => {};
|
||||
};
|
||||
@@ -1,2 +1 @@
|
||||
export * from './decorators';
|
||||
export * from './types';
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { };
|
||||
@@ -1,9 +1,21 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
"outDir": "./dist",
|
||||
"rootDir": "src",
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"strict": true,
|
||||
"importHelpers": true,
|
||||
"target": "es2018",
|
||||
"module": "esnext",
|
||||
"lib": ["es2020", "dom"],
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"lib": ["es2020", "dom"],
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"exclude": ["node_modules", "tmp"]
|
||||
}
|
||||
|
||||
22
yarn.lock
22
yarn.lock
@@ -21743,6 +21743,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/lodash.capitalize@npm:^4":
|
||||
version: 4.2.9
|
||||
resolution: "@types/lodash.capitalize@npm:4.2.9"
|
||||
dependencies:
|
||||
"@types/lodash": "npm:*"
|
||||
checksum: 10c0/4a4bc23bc82a8a0952bf75712cea34cd9e6eb15ef77a58352d19f387be50cdea0b6f2b21e7f0d87c1623bfd42b8d4fd2384478901702b146f016f4c7c11e1abb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/lodash.chunk@npm:^4.2.9":
|
||||
version: 4.2.9
|
||||
resolution: "@types/lodash.chunk@npm:4.2.9"
|
||||
@@ -39575,6 +39584,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.capitalize@npm:^4.2.1":
|
||||
version: 4.2.1
|
||||
resolution: "lodash.capitalize@npm:4.2.1"
|
||||
checksum: 10c0/b289326497c2e24d6b8afa2af2ca4e068ef6ef007ade36bfb6f70af77ce10ea3f090eeee947d5fdcf2db4bcfa4703c8c10a5857a2b39e308bddfd1d11ad35970
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.chunk@npm:4.2.0, lodash.chunk@npm:^4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "lodash.chunk@npm:4.2.0"
|
||||
@@ -51694,6 +51710,8 @@ __metadata:
|
||||
"@types/fs-extra": "npm:^11.0.0"
|
||||
"@types/inquirer": "npm:^9.0.0"
|
||||
"@types/jest": "npm:^29.5.0"
|
||||
"@types/lodash.camelcase": "npm:^4.3.7"
|
||||
"@types/lodash.capitalize": "npm:^4"
|
||||
"@types/lodash.kebabcase": "npm:^4.1.7"
|
||||
"@types/node": "npm:^20.0.0"
|
||||
ajv: "npm:^8.12.0"
|
||||
@@ -51707,9 +51725,11 @@ __metadata:
|
||||
inquirer: "npm:^10.0.0"
|
||||
jest: "npm:^29.5.0"
|
||||
jsonc-parser: "npm:^3.2.0"
|
||||
lodash.camelcase: "npm:^4.3.0"
|
||||
lodash.capitalize: "npm:^4.2.1"
|
||||
lodash.kebabcase: "npm:^4.1.1"
|
||||
tsx: "npm:^4.7.0"
|
||||
typescript: "npm:^5.3.0"
|
||||
typescript: "npm:^5.9.2"
|
||||
wait-on: "npm:^7.2.0"
|
||||
bin:
|
||||
twenty: dist/cli.js
|
||||
|
||||
Reference in New Issue
Block a user