twenty 0.2.0 → 2.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +166 -0
- package/LICENSE +21 -0
- package/README.md +64 -57
- package/dist/app-template/.nvmrc +1 -0
- package/dist/app-template/.oxlintrc.json +37 -0
- package/dist/app-template/AGENTS.md +70 -0
- package/dist/app-template/CHANGELOG.md +7 -0
- package/dist/app-template/README.md +34 -0
- package/dist/app-template/SETUP.md +47 -0
- package/dist/app-template/github/workflows/cd.yml +42 -0
- package/dist/app-template/github/workflows/ci.yml +60 -0
- package/dist/app-template/github/workflows/publish.yml +52 -0
- package/dist/app-template/gitignore +43 -0
- package/dist/app-template/locales/en.json +1 -0
- package/dist/app-template/package.json +36 -0
- package/dist/app-template/public/logo.svg +7 -0
- package/dist/app-template/src/__tests__/application-config.test.ts +14 -0
- package/dist/app-template/src/__tests__/global-setup.ts +56 -0
- package/dist/app-template/src/__tests__/run-twenty.ts +66 -0
- package/dist/app-template/src/__tests__/schema.integration-test.ts +46 -0
- package/dist/app-template/src/application-config.ts +13 -0
- package/dist/app-template/src/constants/universal-identifiers.ts +12 -0
- package/dist/app-template/src/default-role.ts +16 -0
- package/dist/app-template/src/front-components/main-page.tsx +287 -0
- package/dist/app-template/src/logic-functions/health-check.ts +11 -0
- package/dist/app-template/src/navigation-menu-items/main-page.navigation-menu-item.ts +19 -0
- package/dist/app-template/src/page-layouts/main-page.page-layout.ts +42 -0
- package/dist/app-template/tsconfig.json +37 -0
- package/dist/app-template/tsconfig.spec.json +9 -0
- package/dist/app-template/vitest.config.ts +31 -0
- package/dist/app-template/vitest.unit.config.ts +14 -0
- package/dist/app-template/yarn.lock +3374 -0
- package/dist/app-template/yarnrc.yml +1 -0
- package/dist/app-worker.cjs +1 -0
- package/dist/chunks/application-DuIJE2qG.cjs +5 -0
- package/dist/chunks/assert-pull-paths-BuQ1ZRvQ.cjs +1 -0
- package/dist/chunks/build-CX6FBrbu.cjs +1 -0
- package/dist/chunks/build-pull-entities-zwArrVMk.cjs +5 -0
- package/dist/chunks/build-source-manifest-CLKH5vzc.cjs +2 -0
- package/dist/chunks/build-source-snapshot-CbGsy1-v.cjs +206 -0
- package/dist/chunks/check-node-requirement-mqwNPktb.cjs +1 -0
- package/dist/chunks/cli-error-CSsayo0u.cjs +1 -0
- package/dist/chunks/cli-version.constant-aSp8WZDh.cjs +1 -0
- package/dist/chunks/collect-watch-inputs-B9jWG4W0.cjs +1 -0
- package/dist/chunks/compile-application-translations-B59jYVWc.cjs +1 -0
- package/dist/chunks/config-file-mode.constant-DLBzKKUl.cjs +1 -0
- package/dist/chunks/confirm-in-terminal-BxEM5lxE.cjs +1 -0
- package/dist/chunks/constants-CL6mrTMT-B748cNNG.cjs +23 -0
- package/dist/chunks/constants-Cf5-CwMu.cjs +1 -0
- package/dist/chunks/convert-to-label-BPBLtc_O.cjs +1 -0
- package/dist/chunks/create-app-not-installed-error-Cc8YZqI7.cjs +1 -0
- package/dist/chunks/create-cancelled-error-DgrNfOv2.cjs +1 -0
- package/dist/chunks/create-http-status-error-D3gy3DzB.cjs +1 -0
- package/dist/chunks/create-metadata-client-ChjDalSi.cjs +6 -0
- package/dist/chunks/create-pull-base-write-rxjcKtD-.cjs +1 -0
- package/dist/chunks/create-rest-client-eAfOFfDA.cjs +58 -0
- package/dist/chunks/create-tooling-failure-DLI5FlQP.cjs +1 -0
- package/dist/chunks/credential-kind-labels.constant-CKdoQF8o.cjs +1 -0
- package/dist/chunks/extract-manifest-from-file-j-Y9wNas.cjs +1 -0
- package/dist/chunks/fetch-metadata-inspection-Dp034TpK.cjs +1 -0
- package/dist/chunks/fetch-signed-in-identity-BhvaL3zp.cjs +1 -0
- package/dist/chunks/fetch-workspace-id-DJStmf_V.cjs +1 -0
- package/dist/chunks/find-remote-D48r20g0.cjs +1 -0
- package/dist/chunks/format-app-duration-gu2oRLB4.cjs +1 -0
- package/dist/chunks/format-body-VcKL4WXt.cjs +1 -0
- package/dist/chunks/format-data-value-B6rIbpJv.cjs +1 -0
- package/dist/chunks/format-details-COEwKz_M.cjs +2 -0
- package/dist/chunks/format-metadata-fields-DJnTg1R9.cjs +2 -0
- package/dist/chunks/format-metadata-owner-yUeocnwY.cjs +1 -0
- package/dist/chunks/format-table-4drtI5sP.cjs +2 -0
- package/dist/chunks/fs-utils-Bl1fQcaJ.cjs +2 -0
- package/dist/chunks/generate-application-client-CCVLjxTj.cjs +1 -0
- package/dist/chunks/get-app-plan-summary-B0uwHXbO.cjs +8 -0
- package/dist/chunks/get-config-path-C-D-QHuX.cjs +1 -0
- package/dist/chunks/get-credential-kind-JI9heHe2.cjs +1 -0
- package/dist/chunks/get-function-input-schema-GNk3NRLJ-Bfph1o9z.cjs +1 -0
- package/dist/chunks/get-tooling-error-context-C69TBSej.cjs +1 -0
- package/dist/chunks/global-flags.constant-8GfW0sNU.cjs +1 -0
- package/dist/chunks/has-yarn-plug-and-play-X3ay5Xl0.cjs +1 -0
- package/dist/chunks/is-access-token-expiring-gICPXsbO.cjs +1 -0
- package/dist/chunks/is-application-not-found-error-mNeGbxmT.cjs +1 -0
- package/dist/chunks/is-exported-manifest-CriNOWvx.cjs +1 -0
- package/dist/chunks/is-inside-directory-t3ixLQDr.cjs +1 -0
- package/dist/chunks/is-interaction-allowed-ycbAFMIj.cjs +1 -0
- package/dist/chunks/is-same-universal-identifier-BCBib3RP.cjs +1 -0
- package/dist/chunks/is-supported-locale-BAjnKnKj.cjs +20 -0
- package/dist/chunks/kebab-case-D0usAzG-.cjs +1 -0
- package/dist/chunks/list-application-source-files-jEau5Rie.cjs +1 -0
- package/dist/chunks/metadata-D9sQOTRK.cjs +1 -0
- package/dist/chunks/metadata-page.constant-BkG4ndrO.cjs +1 -0
- package/dist/chunks/oauth-timing.constant-BClMQcKn.cjs +1 -0
- package/dist/chunks/open-browser-LPuVspT7.cjs +1 -0
- package/dist/chunks/package-CdYv4LEx.cjs +1 -0
- package/dist/chunks/parse-api-url-BllxRdpE.cjs +1 -0
- package/dist/chunks/parse-app-export-BkfflyUY.cjs +1 -0
- package/dist/chunks/parse-graphql-response-CtP9KTS2.cjs +1 -0
- package/dist/chunks/parse-json-input-CvMTKAWw.cjs +1 -0
- package/dist/chunks/print-typescript-value-DhElcFzI.cjs +3 -0
- package/dist/chunks/pull-source-eoiJwFb9.cjs +1 -0
- package/dist/chunks/read-app-identity-SeleQ_D6.cjs +1 -0
- package/dist/chunks/read-application-identity-BFg_p84F.cjs +1 -0
- package/dist/chunks/read-command-values-uSJqBG_E.cjs +1 -0
- package/dist/chunks/read-config-DUJ27809.cjs +1 -0
- package/dist/chunks/read-input-value-Cxgh_r2g.cjs +1 -0
- package/dist/chunks/read-source-identity-C8LG1v6U.cjs +1 -0
- package/dist/chunks/read-watch-input-stamp-BxeOb0vI.cjs +1 -0
- package/dist/chunks/record-pull-base-BbxvlnU8.cjs +1 -0
- package/dist/chunks/record-typecheck-config-inputs-BDqkKCrj.cjs +1 -0
- package/dist/chunks/request-oauth-tokens-C4KKlTQT.cjs +1 -0
- package/dist/chunks/request-timeout-milliseconds.constant-DtyQHrll.cjs +1 -0
- package/dist/chunks/require-approval-fhegB1ap.cjs +1 -0
- package/dist/chunks/resolve-app-project-D5ZXIpsG.cjs +1 -0
- package/dist/chunks/resolve-inside-sdk-p-16gKb4.cjs +1 -0
- package/dist/chunks/resolve-metadata-object-QoDi-Aik.cjs +1 -0
- package/dist/chunks/resolve-project-typescript-CDY8BbmP.cjs +1 -0
- package/dist/chunks/resolve-request-url-jC5kYwkn.cjs +1 -0
- package/dist/chunks/resolve-source-sdk-mK-f4q55.cjs +1 -0
- package/dist/chunks/result-item-limit.constant-CDrOhiWD.cjs +1 -0
- package/dist/chunks/run-api-graphql-command-Biy0WqXa.cjs +1 -0
- package/dist/chunks/run-api-rest-command-D9kdQceV.cjs +1 -0
- package/dist/chunks/run-app-add-command-DcTQg7Si.cjs +163 -0
- package/dist/chunks/run-app-apply-command-DwLPPJ-Y.cjs +2 -0
- package/dist/chunks/run-app-build-command-BRYb0one.cjs +2 -0
- package/dist/chunks/run-app-dev-command-CVeh9Gab.cjs +2 -0
- package/dist/chunks/run-app-exec-command-DKLNLbL5.cjs +3 -0
- package/dist/chunks/run-app-init-command-CJj8_spC.cjs +2 -0
- package/dist/chunks/run-app-logs-command-Dusb8K-W.cjs +9 -0
- package/dist/chunks/run-app-operation-BWrY9YZK.cjs +1 -0
- package/dist/chunks/run-app-plan-command-C0_3e332.cjs +1 -0
- package/dist/chunks/run-app-pull-command-DcZqs8gR.cjs +3 -0
- package/dist/chunks/run-app-typecheck-command-C8LqpOej.cjs +1 -0
- package/dist/chunks/run-app-uninstall-command-ncWS_k-p.cjs +2 -0
- package/dist/chunks/run-auth-login-command-_zDgRcth.cjs +1 -0
- package/dist/chunks/run-auth-logout-command-BO6O7GVi.cjs +2 -0
- package/dist/chunks/run-auth-status-command-Cj-vqoHg.cjs +1 -0
- package/dist/chunks/run-auth-token-command-D4geJ-rx.cjs +1 -0
- package/dist/chunks/run-cli-c9TOR0zj.cjs +23 -0
- package/dist/chunks/run-commands-command-C6gvMpQ_.cjs +2 -0
- package/dist/chunks/run-data-get-command-jDEU4EG1.cjs +1 -0
- package/dist/chunks/run-data-list-command-EnCdbuzc.cjs +1 -0
- package/dist/chunks/run-doctor-command-DnJ6io-m.cjs +2 -0
- package/dist/chunks/run-metadata-field-describe-command-vJngpw2S.cjs +3 -0
- package/dist/chunks/run-metadata-field-list-command-CR4w7fI6.cjs +3 -0
- package/dist/chunks/run-metadata-object-describe-command-CHP8K-W2.cjs +3 -0
- package/dist/chunks/run-metadata-object-list-command-BqXMcAp6.cjs +2 -0
- package/dist/chunks/run-open-command-DKl8acw0.cjs +1 -0
- package/dist/chunks/run-remote-list-command-DeAzr5Mn.cjs +1 -0
- package/dist/chunks/run-remote-remove-command-D0C5sWjP.cjs +2 -0
- package/dist/chunks/run-remote-rename-command-Byq9WRHd.cjs +1 -0
- package/dist/chunks/run-remote-use-command-B6VVDlMG.cjs +1 -0
- package/dist/chunks/run-version-command-BmSk8PUV.cjs +1 -0
- package/dist/chunks/select-target-C1VZjf-A.cjs +1 -0
- package/dist/chunks/sign-in-to-remote-again-CW8HPSTP.cjs +1 -0
- package/dist/chunks/sign-in-with-browser-C3kC6uP2.cjs +1 -0
- package/dist/chunks/style-BR52fK4V.cjs +1 -0
- package/dist/chunks/target-environment-variable.constant-D7z8AihP.cjs +1 -0
- package/dist/chunks/to-cli-error-BBYf_wqo.cjs +1 -0
- package/dist/chunks/to-public-target-CIAwoOKL.cjs +1 -0
- package/dist/chunks/to-worker-output-diagnostics-lprh9b0l.cjs +2 -0
- package/dist/chunks/typecheck-application-DGrovgX9.cjs +2 -0
- package/dist/chunks/undici-D3mKdfwl.cjs +95 -0
- package/dist/chunks/update-config-BkKBEfAp.cjs +1 -0
- package/dist/chunks/utils-B8EXJeaX-DK3gCslL.cjs +33 -0
- package/dist/chunks/validate-app-path-Dau-PC0a.cjs +1 -0
- package/dist/chunks/validate-remote-name-w-qnr43w.cjs +1 -0
- package/dist/chunks/write-config-atomically-DRWTukU-.cjs +1 -0
- package/dist/cli.cjs +2 -0
- package/docs/commands.md +564 -0
- package/package.json +41 -18
- package/src/app/README.md +43 -0
- package/src/app/add/README.md +69 -0
- package/src/app/bundles/README.md +85 -0
- package/src/app/client/README.md +62 -0
- package/src/app/dev/README.md +35 -0
- package/src/app/exec/README.md +48 -0
- package/src/app/function-logs/README.md +44 -0
- package/src/app/manifest/README.md +74 -0
- package/src/app/pull/README.md +28 -0
- package/src/app/source/README.md +33 -0
- package/src/app/typecheck/README.md +67 -0
- package/.npmignore +0 -2
- package/bin/twenty +0 -2
- package/index.js +0 -118
package/package.json
CHANGED
|
@@ -1,28 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "twenty",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
3
|
+
"version": "2.46.0",
|
|
4
|
+
"description": "The Twenty command line.",
|
|
5
|
+
"homepage": "https://twenty.com",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/
|
|
8
|
+
"url": "git+https://github.com/twentyhq/twenty.git",
|
|
9
|
+
"directory": "packages/twenty-cli"
|
|
9
10
|
},
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/twentyhq/twenty/issues"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"bin": "dist/cli.cjs",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"docs/commands.md",
|
|
19
|
+
"CONTRIBUTING.md",
|
|
20
|
+
"src/app/**/README.md"
|
|
15
21
|
],
|
|
16
22
|
"scripts": {
|
|
17
|
-
"
|
|
23
|
+
"build": "npx rimraf dist && npx vite build",
|
|
24
|
+
"prepublish": "node scripts/check-publish.mjs && node scripts/generate-template-lock.mjs"
|
|
18
25
|
},
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": "^24.5.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@sniptt/guards": "^0.2.0",
|
|
31
|
+
"@types/lodash.startcase": "^4",
|
|
32
|
+
"@types/node": "^24.0.0",
|
|
33
|
+
"@types/semver": "^7.5.8",
|
|
34
|
+
"cjs-module-lexer": "^2.2.0",
|
|
35
|
+
"commander": "^14.0.3",
|
|
36
|
+
"lodash.startcase": "^4.4.0",
|
|
37
|
+
"semver": "7.6.3",
|
|
38
|
+
"twenty-client-sdk": "2.46.0",
|
|
39
|
+
"twenty-shared": "0.0.0",
|
|
40
|
+
"undici": "^7.29.0",
|
|
41
|
+
"uuid": "^13.0.2",
|
|
42
|
+
"vite": "^8.0.0",
|
|
43
|
+
"vitest": "^4.1.0"
|
|
24
44
|
},
|
|
25
|
-
"
|
|
26
|
-
"
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"chokidar": "^3.6.0",
|
|
47
|
+
"esbuild": "^0.28.1",
|
|
48
|
+
"tinyglobby": "^0.2.15",
|
|
49
|
+
"typescript": "^5.9.3"
|
|
27
50
|
}
|
|
28
|
-
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Application tooling
|
|
2
|
+
|
|
3
|
+
See [Contributing](../../CONTRIBUTING.md) for package setup and tests, and the
|
|
4
|
+
[command reference](../../docs/commands.md) for user-visible behavior.
|
|
5
|
+
|
|
6
|
+
The `twenty` CLI owns filesystem operations, source loading, build, typecheck,
|
|
7
|
+
watching, pull and deployment. Apps use `twenty-sdk` to define application
|
|
8
|
+
metadata and runtime behavior. They do not import or depend on the CLI, which
|
|
9
|
+
is normally installed globally.
|
|
10
|
+
|
|
11
|
+
| Module | Responsibility |
|
|
12
|
+
| ---------------------------------------- | -------------------------------------------------------------------- |
|
|
13
|
+
| `project` | Locate the app and resolve its installed SDK and Node requirements |
|
|
14
|
+
| `deployment` | Preview metadata changes, approve them, upload snapshots and sync |
|
|
15
|
+
| [source](source/README.md) | Discover and evaluate trusted app definitions in a disposable worker |
|
|
16
|
+
| [manifest](manifest/README.md) | Construct and validate metadata, compile translations |
|
|
17
|
+
| [bundles](bundles/README.md) | Build artifacts and hold immutable snapshots for consumers |
|
|
18
|
+
| [typecheck](typecheck/README.md) | Check source and configuration with the app's TypeScript compiler |
|
|
19
|
+
| [client](client/README.md) | Invoke the app's installed client SDK generator |
|
|
20
|
+
| [pull](pull/README.md) | Reconcile remote metadata with source and a target-bound baseline |
|
|
21
|
+
| [add](add/README.md) | Create starter definitions without replacing existing files |
|
|
22
|
+
| [dev](dev/README.md) | Watch build inputs and schedule builds and remote apply |
|
|
23
|
+
| [exec](exec/README.md) | Execute an installed logic function |
|
|
24
|
+
| [function-logs](function-logs/README.md) | Stream application function logs |
|
|
25
|
+
|
|
26
|
+
The worker isolates process exits, captures bounded output, filters inherited
|
|
27
|
+
CLI credentials and supports cancellation. It runs trusted developer code and
|
|
28
|
+
is not a security sandbox. Configurations containing functions stay inside the
|
|
29
|
+
worker; only serializable requests, results and diagnostics cross IPC.
|
|
30
|
+
|
|
31
|
+
Snapshots retain files until their consumers finish. Uploads verify containment,
|
|
32
|
+
size and checksums. Pull and scaffolding stage writes before exposing them to
|
|
33
|
+
file watchers. Keep these guarantees when simplifying implementation details.
|
|
34
|
+
|
|
35
|
+
## Package boundaries
|
|
36
|
+
|
|
37
|
+
The CLI imports the app's public SDK authoring exports and invokes its installed
|
|
38
|
+
client SDK generator. The [typecheck documentation](typecheck/README.md) describes
|
|
39
|
+
compiler resolution and project requirements.
|
|
40
|
+
|
|
41
|
+
Parity tests compare generated source, manifests and bundles with the repository
|
|
42
|
+
SDK. Command failure and cancellation tests inject tooling fixtures into a bundle
|
|
43
|
+
of the production worker; real-app tests exercise its compiler and bundler.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# App definition scaffolding
|
|
2
|
+
|
|
3
|
+
The templates create object, field, logic-function and front-component
|
|
4
|
+
definitions. Filenames and logic-function/front-component names use kebab-case.
|
|
5
|
+
Object and field metadata names keep the entered spelling; display labels use
|
|
6
|
+
the same helper as app init.
|
|
7
|
+
|
|
8
|
+
## Contract
|
|
9
|
+
|
|
10
|
+
- `twenty app add [entity]` accepts `object`, `field`, `logic-function` and
|
|
11
|
+
`front-component`. A human terminal can supply omitted values interactively.
|
|
12
|
+
JSON, CI, redirected stdin and `--no-input` never prompt; missing required or
|
|
13
|
+
inapplicable options exit 2. Labels default to the name; fields default to
|
|
14
|
+
`TEXT`.
|
|
15
|
+
- `--path` selects an app, like the other CLI app commands. Files go under the
|
|
16
|
+
selected app's `src/objects`, `src/fields`, `src/logic-functions` or
|
|
17
|
+
`src/front-components`. Custom output directories are not supported.
|
|
18
|
+
- Parent and relation endpoint identifiers must be valid universal UUIDs. No
|
|
19
|
+
workspace lookup verifies ownership or existence. Relations require reviewing
|
|
20
|
+
both endpoints; this generator creates only the requested standalone field.
|
|
21
|
+
- Objects accept `--create-view`, `--create-navigation-menu-item` and
|
|
22
|
+
`--create-page-layout`. Each flag opts into that definition; page layouts also
|
|
23
|
+
create the fields view used by their Fields widget. The list view and fields
|
|
24
|
+
view reuse the object's name field. Fields views also include the SDK's
|
|
25
|
+
generated audit fields, resolved using the application's universal identifier.
|
|
26
|
+
With no creation flags, human interactive mode offers all three together,
|
|
27
|
+
defaulting to No. Explicit creation flags bypass that question. Noninteractive
|
|
28
|
+
modes never ask and create only explicitly selected companions. All prompts
|
|
29
|
+
complete before any files are written, including on cancellation.
|
|
30
|
+
- Node's `randomUUID` creates version 4 identifiers. The CLI TypeScript printer
|
|
31
|
+
escapes authored strings, including quotes, backslashes and line breaks.
|
|
32
|
+
- App discovery, SDK compatibility and application identity reuse the CLI's
|
|
33
|
+
worker boundary. Identity loading evaluates trusted app code in a disposable
|
|
34
|
+
worker; it is not a security sandbox. The command makes no workspace requests
|
|
35
|
+
and does not require authentication.
|
|
36
|
+
- Destination paths reuse pull's containment and symlink checks. All destinations
|
|
37
|
+
are checked before writing and all files are staged beside their destinations
|
|
38
|
+
before exclusive linking, including when source directories are mounted on
|
|
39
|
+
different filesystems.
|
|
40
|
+
Existing or concurrently created files are never replaced. Failure or
|
|
41
|
+
cancellation before all links complete rolls back the links already created;
|
|
42
|
+
files replaced by another process are preserved. Incomplete rollback reports
|
|
43
|
+
remaining app-relative paths. Completion wins over late cancellation. Cleanup failure
|
|
44
|
+
after success warns and reports the remaining temporary directory. Empty
|
|
45
|
+
parent directories can remain after failure or cancellation. Exclusive atomic
|
|
46
|
+
creation requires a filesystem supporting hard links; unsupported filesystems
|
|
47
|
+
fail instead of falling back to a potentially partial destination write.
|
|
48
|
+
- Path validation rejects existing symbolic links. It cannot prevent another
|
|
49
|
+
local process from replacing a parent directory between validation and writing.
|
|
50
|
+
- JSON reports app-relative `createdPaths`. There is no overwrite mode and no
|
|
51
|
+
editing or registration of existing definitions. Standalone view, layout and
|
|
52
|
+
navigation item generators are not supported.
|
|
53
|
+
|
|
54
|
+
These are starter definitions, not a deployment validator. Review type-specific
|
|
55
|
+
settings, relation endpoints, handlers and components, then build and plan the
|
|
56
|
+
app. Successful generation does not imply the server will accept the metadata.
|
|
57
|
+
|
|
58
|
+
## Verification
|
|
59
|
+
|
|
60
|
+
`add-template-parity.spec.ts` compares generated source with the repository SDK,
|
|
61
|
+
normalizing random UUIDs while preserving their reuse. It covers every field type
|
|
62
|
+
and both runtime templates. Additional tests cover escaped strings, invalid
|
|
63
|
+
identifiers, destination conflicts and cancellation.
|
|
64
|
+
|
|
65
|
+
View and navigation templates are deliberately limited to object companions:
|
|
66
|
+
fully populated fields and an object-target navigation item, without the SDK
|
|
67
|
+
CLI's placeholder and unrelated view/link/folder variants. Generated identifiers
|
|
68
|
+
and references are checked by the app-add command tests; record page layouts
|
|
69
|
+
also retain SDK source parity.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Bundles and snapshots
|
|
2
|
+
|
|
3
|
+
The CLI owns application bundling and immutable snapshot lifecycle. The internal
|
|
4
|
+
`bundleSnapshot` worker request builds and holds or releases a snapshot under
|
|
5
|
+
`.twenty/cli/snapshots/build-*`. The [typechecker](../typecheck/README.md) uses the
|
|
6
|
+
app's own TypeScript compiler. Build, plan, apply and uninstall use this pipeline.
|
|
7
|
+
Worker requests are internal, not a public API or a way to bypass build checks.
|
|
8
|
+
|
|
9
|
+
See the [app tooling overview](../README.md) for package ownership.
|
|
10
|
+
|
|
11
|
+
## Contract
|
|
12
|
+
|
|
13
|
+
Logic functions use an ESM/CJS banner, external modules and define stubs.
|
|
14
|
+
Front components use JSX wrappers, remote-DOM transformation, optional
|
|
15
|
+
preact aliases, bundled CSS injection, static shared-dependency export discovery
|
|
16
|
+
and live-binding shims. The app's translation catalogs are embedded in the bundles.
|
|
17
|
+
Dependency discovery does not execute package initialization code. CommonJS
|
|
18
|
+
named exports must be statically detectable; packages that compute export names
|
|
19
|
+
at runtime can be consumed through their default export.
|
|
20
|
+
|
|
21
|
+
The bundler collects source and dependency files, public and generated assets,
|
|
22
|
+
and the README, and updates the manifest's artifact checksums. Snapshot copies
|
|
23
|
+
dereference symlinks. Each upload artifact records its role, byte size and
|
|
24
|
+
SHA-256 hash. README and source maps are included in the snapshot but are not
|
|
25
|
+
upload artifacts. The content hash combines the sorted artifact paths, roles and
|
|
26
|
+
hashes with the exact manifest bytes.
|
|
27
|
+
|
|
28
|
+
Each build has its own directory and lease. Release removes only a snapshot held
|
|
29
|
+
by that worker. Failed and cooperatively cancelled builds remove their temporary
|
|
30
|
+
directory. Forced worker termination can leave a snapshot behind. Cleanup only
|
|
31
|
+
removes snapshots owned by the current build.
|
|
32
|
+
|
|
33
|
+
## Implementation
|
|
34
|
+
|
|
35
|
+
- Source loading, manifest generation, translations and filesystem helpers are
|
|
36
|
+
shared with the other CLI app operations. The [dev session](../dev/README.md)
|
|
37
|
+
observes the build's inputs and retains immutable copies for remote apply.
|
|
38
|
+
- The define stub reads `twenty-sdk/define` from the app's installed SDK. Factories
|
|
39
|
+
become no-op validators, plain-data exports retain their values, and other
|
|
40
|
+
exports use proxy stubs. Runtime constants match the SDK the app uses.
|
|
41
|
+
- `sharp` is optional and resolved from the app. It is not a CLI dependency.
|
|
42
|
+
When unavailable, cover generation warns and the build continues. Vite embeds
|
|
43
|
+
the backdrop PNG as a data URI so it works in the standalone package.
|
|
44
|
+
- `compileApplication` runs the CLI typecheck after bundling, using the compiler
|
|
45
|
+
resolved from the app.
|
|
46
|
+
- Workers run from the selected app directory, so invoking the CLI from a
|
|
47
|
+
parent or nested directory produces the same bundle paths, bytes and hashes.
|
|
48
|
+
SDK parity references run from the app root as well.
|
|
49
|
+
- Upload validation accepts only snapshots inside the selected app's
|
|
50
|
+
`.twenty/cli/snapshots`, with path containment and per-file hash checks.
|
|
51
|
+
- The internal worker reuses the source SDK gate, credential filtering, output
|
|
52
|
+
capture, cancellation and held-snapshot release flow, and stops esbuild after
|
|
53
|
+
building. Public app commands share this worker lifecycle and its
|
|
54
|
+
`ToolingResult` contract.
|
|
55
|
+
|
|
56
|
+
## Verification
|
|
57
|
+
|
|
58
|
+
After building shared, SDK and UI dependencies, from `packages/twenty-cli`:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
node ../../node_modules/vitest/vitest.mjs run --config vitest.config.ts --maxWorkers=1 src/app/snapshots src/app/bundles src/app/__tests__/resolve-snapshot-directory.spec.ts src/commands/app/__tests__/app-real-sdk.spec.ts
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The parity suite builds all five repository fixtures and a fresh CLI-created app
|
|
65
|
+
through the SDK reference and the production CLI worker. The app has a copy of
|
|
66
|
+
the real authoring SDK without its build/CLI exports or implementation files.
|
|
67
|
+
Successful builds preserve manifest configuration and artifact paths/roles.
|
|
68
|
+
Non-frontend files are compared byte for byte, except for `manifest.json` and
|
|
69
|
+
source maps. Frontend bundle bytes and checksums are allowed to differ: dedicated tests verify preserved
|
|
70
|
+
string literals, resolved CSS assets and live shared-dependency bindings. Every
|
|
71
|
+
CLI artifact's size/checksum and the snapshot content hash are checked against
|
|
72
|
+
the actual bytes. Tests also verify the manifest references for function, component
|
|
73
|
+
and shared-dependency bundles, and compare the serialized manifest with the
|
|
74
|
+
worker's manifest result. The invalid fixture fails in both
|
|
75
|
+
pipelines. The fresh CLI template's test setup typechecks with an authoring-only SDK.
|
|
76
|
+
|
|
77
|
+
Source-map comparisons for unchanged bundles resolve paths against each map's
|
|
78
|
+
directory and exclude the define stub's generator banner from `sourcesContent`.
|
|
79
|
+
|
|
80
|
+
Additional tests cover optional covers, project SDK constants, baked translations
|
|
81
|
+
and CSS, README selection, immutable symlink copies, concurrent snapshots,
|
|
82
|
+
release ownership, failed/cancelled builds and failed snapshot consumers. The
|
|
83
|
+
build/apply contract runs CLI snapshots against a local HTTP fixture, including
|
|
84
|
+
uploads, sync, pull-base recording, release and client generation. It does not contact
|
|
85
|
+
a live Twenty workspace.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Application client generation
|
|
2
|
+
|
|
3
|
+
The CLI calls `replaceCoreClient` from the app's installed
|
|
4
|
+
`twenty-client-sdk/generate`. See the [app tooling overview](../README.md) for
|
|
5
|
+
package boundaries. The package manifest is read through `readJsonObject`;
|
|
6
|
+
missing or malformed JSON and incorrect package names produce an expected-package
|
|
7
|
+
`CLIENT_GENERATION_FAILED` error.
|
|
8
|
+
|
|
9
|
+
Public `app apply` invokes the generator through the internal
|
|
10
|
+
`generateSourceClient` worker request.
|
|
11
|
+
|
|
12
|
+
## Package and output ownership
|
|
13
|
+
|
|
14
|
+
The app must have `node_modules/twenty-client-sdk/package.json` with the expected
|
|
15
|
+
package name. Resolution does not search workspace ancestors, avoiding writes
|
|
16
|
+
to a hoisted-only installation shared by other apps. Package-manager symlinks
|
|
17
|
+
are followed, so a linked installation can still be shared.
|
|
18
|
+
|
|
19
|
+
Resolve `twenty-client-sdk/generate` from this package and require the entry to
|
|
20
|
+
belong to the same real package directory. It must export a callable
|
|
21
|
+
`replaceCoreClient`. This keeps the generator and the client it writes into on
|
|
22
|
+
the same installed version, with no fallback to the CLI's own client SDK.
|
|
23
|
+
|
|
24
|
+
The output layout is `dist/core/generated`, `dist/core.mjs` and
|
|
25
|
+
`dist/core.cjs` inside that installation. Source files and the metadata client
|
|
26
|
+
remain unchanged. Neither apps nor SDK packages depend on the CLI. The client
|
|
27
|
+
SDK's generator dependencies remain part of the app's installed client package.
|
|
28
|
+
|
|
29
|
+
## Failure and cancellation
|
|
30
|
+
|
|
31
|
+
The worker requires a non-empty schema and an absolute app path. Failures use
|
|
32
|
+
`CLIENT_GENERATION_FAILED` / `CANCELLED` results. Missing or incompatible local
|
|
33
|
+
packages fail before invoking the generator. The generator rejects
|
|
34
|
+
invalid schemas without replacing the installed bundles.
|
|
35
|
+
|
|
36
|
+
Generation is not transactional. A generator failure can leave partial changes;
|
|
37
|
+
the wrapper reports that failure even if cancellation was requested meanwhile.
|
|
38
|
+
The generator has no cancellation parameter, so the wrapper waits for it to
|
|
39
|
+
settle and then checks the signal. Forced worker termination can interrupt file
|
|
40
|
+
writes. The operation does not roll back partial writes.
|
|
41
|
+
|
|
42
|
+
## Verification
|
|
43
|
+
|
|
44
|
+
From `packages/twenty-cli`, after building the shared, SDK and client SDK:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
node ../../node_modules/vitest/vitest.mjs run --config vitest.config.ts src/app/client src/commands/app/__tests__/app-real-sdk.spec.ts --maxWorkers=1
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Parity runs the SDK reference and the production CLI worker against
|
|
51
|
+
the same schema and app path, comparing every generated source file and both
|
|
52
|
+
compiled bundles byte for byte. Reusing the path keeps esbuild's source-path
|
|
53
|
+
comments identical without normalizing file contents. The app has its own client
|
|
54
|
+
SDK and no application SDK. The generated CommonJS client is loaded to verify
|
|
55
|
+
its `CoreApiClient` export.
|
|
56
|
+
|
|
57
|
+
Other cases cover a missing or hoisted-only package, invalid manifests and
|
|
58
|
+
schemas, missing exports, a non-callable API, selection of the app's generator,
|
|
59
|
+
pre-cancellation, in-flight cancellation and preserved failures/partial writes.
|
|
60
|
+
The local HTTP apply contract exercises CLI client
|
|
61
|
+
generation, along with snapshot upload, synchronization, pull-base recording
|
|
62
|
+
and release. No live workspace is contacted.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# App development session
|
|
2
|
+
|
|
3
|
+
The parent owns observation and two bounded scheduling lanes: one build worker
|
|
4
|
+
and one remote apply. `runDevLoop` keeps only the latest pending snapshot. Every
|
|
5
|
+
snapshot is released once, including superseded builds and cancellation; the
|
|
6
|
+
active apply retains its files until its reads settle. The skip key is the last
|
|
7
|
+
acknowledged remote content hash, cleared after a failed apply.
|
|
8
|
+
|
|
9
|
+
`buildDevSnapshot` copies declared, size/SHA-256-verified artifacts into a fresh
|
|
10
|
+
`dev-*` directory while the compiler's snapshot lease is held. The worker then
|
|
11
|
+
releases its own snapshot and exits, so retained builds do not retain compiler
|
|
12
|
+
heaps. At most three snapshot directories exist for an active session: in-flight
|
|
13
|
+
apply, compiler source, and its copy. No hard links or mutable output tree.
|
|
14
|
+
|
|
15
|
+
Input collection is private worker metadata, absent on ordinary build requests.
|
|
16
|
+
It observes esbuild loads/resolution parents, static-file copies and TypeScript
|
|
17
|
+
reads. Missing TypeScript package-scope probes cannot expand watching into
|
|
18
|
+
unrelated ancestor directories. Inputs have stamps captured before reads; after
|
|
19
|
+
attaching the watcher, changed stamps invalidate the result. A successful graph
|
|
20
|
+
replaces the previous graph; a failure watches the last successful graph plus
|
|
21
|
+
only that attempt's inputs. External directory observation is shallow.
|
|
22
|
+
|
|
23
|
+
`applyAppBuild` handles remote operations. Dev supplies a
|
|
24
|
+
revision-specific approval signal and a check immediately before the first
|
|
25
|
+
write. Once writes start, a newer build does not cancel those writes. Client
|
|
26
|
+
generation pauses compilation and invalidates pending builds; its successful
|
|
27
|
+
input key plus output stamps prevent generation loops. A failure after client
|
|
28
|
+
writes start ends the session. Normal compilation failures keep watching;
|
|
29
|
+
abnormal worker exits and watcher failures require restart.
|
|
30
|
+
|
|
31
|
+
There is no incremental compiler cache or package-manager dependency watcher.
|
|
32
|
+
Arbitrary app-code filesystem reads, concurrent independent dev processes,
|
|
33
|
+
and SIGKILL recovery are outside this
|
|
34
|
+
session's guarantees. See the [command reference](../../../docs/commands.md#develop-an-app) for
|
|
35
|
+
user-visible limits.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# CLI app function execution
|
|
2
|
+
|
|
3
|
+
The command selects one deployed function from the
|
|
4
|
+
local app's identifiers, executes it once with a payload, and reports status,
|
|
5
|
+
duration, data, error and logs. Name matching is exact. Hooks select their
|
|
6
|
+
function identifier; executing an uninstall hook does not uninstall the app.
|
|
7
|
+
A non-SUCCESS result exits with failure. It executes deployed code, so local
|
|
8
|
+
edits must be synced with apply or dev first.
|
|
9
|
+
|
|
10
|
+
## Contract
|
|
11
|
+
|
|
12
|
+
- Flags follow the space-separated command surface and kebab-case convention:
|
|
13
|
+
`app exec --name`, `--universal-identifier`, `--post-install`, `--pre-install`
|
|
14
|
+
or `--uninstall-hook`. Exactly one selector is required.
|
|
15
|
+
- The CLI keeps no saved build manifest. It uses the `buildManifest`
|
|
16
|
+
worker to load definitions with the app's authoring SDK, without bundling or
|
|
17
|
+
typechecking. Worker output becomes diagnostics; credentials are not passed to it.
|
|
18
|
+
- Function selection checks both the local manifest identifiers and the remote
|
|
19
|
+
installed application's ID. The `findOneApplication` lookup prevents
|
|
20
|
+
copied or stale identifiers from selecting another installed app's function.
|
|
21
|
+
This needs `APPLICATIONS` in addition to execution's `WORKFLOWS` permission.
|
|
22
|
+
- The current server requires a user-bound token for execution. Browser-login
|
|
23
|
+
OAuth remotes are supported; API-key targets fail before loading app source.
|
|
24
|
+
The server still decides which functions that user can run: workspace custom
|
|
25
|
+
app functions, functions exposed as workflow actions, or functions from an
|
|
26
|
+
app registered by this workspace. The CLI does not bypass those checks.
|
|
27
|
+
- Payloads use the JSON-object input reader: inline JSON, `@file` or
|
|
28
|
+
`-` for stdin, default `{}`. Non-object values are rejected.
|
|
29
|
+
- Human output escapes terminal controls. JSON success includes function/app
|
|
30
|
+
identity, status, `durationMilliseconds`, data, logs, error and diagnostics.
|
|
31
|
+
A completed non-SUCCESS result exits 1 with `EXECUTION_FAILED` and retains
|
|
32
|
+
that information in `error.details`, with `outcome: "completed"`. A missing
|
|
33
|
+
function exits 4; ambiguous selection exits 6; bad input exits 2.
|
|
34
|
+
- The execution request deadline is the deployed function's `timeoutSeconds`
|
|
35
|
+
plus the transport's 60-second allowance. Its HTTP dispatcher header/body
|
|
36
|
+
deadlines use the same value, avoiding Node's default five-minute cutoff.
|
|
37
|
+
Other commands use their own deadlines and lazy transport loading.
|
|
38
|
+
Proxies and the server can still close a request earlier.
|
|
39
|
+
- Execution is never retried. Acknowledged authentication, permission,
|
|
40
|
+
not-found and input/validation rejections have `outcome: "not-started"`.
|
|
41
|
+
Transport failure, cancellation, internal server error or an invalid response
|
|
42
|
+
after submission have `outcome: "unknown"`: execution may still be running.
|
|
43
|
+
Ctrl+C exits 130 and cannot stop the server-side function or undo its effects.
|
|
44
|
+
|
|
45
|
+
Command tests use the public command runner and real HTTP metadata transport,
|
|
46
|
+
with controlled worker results. They cover selection, application binding,
|
|
47
|
+
payloads, returned errors, rejection/unknown outcomes, cancellation and no
|
|
48
|
+
replay. These tests do not establish live workspace authorization or execution.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# CLI app function logs
|
|
2
|
+
|
|
3
|
+
The command subscribes to POST `/metadata`,
|
|
4
|
+
`logicFunctionLogs(input: LogicFunctionLogsInput!)`, application universal
|
|
5
|
+
identifier and optional exact function name or universal identifier. It receives
|
|
6
|
+
future completed-execution output, not history or per-line execution progress.
|
|
7
|
+
Names can repeat within one app; name selection is a filter matching all of them.
|
|
8
|
+
|
|
9
|
+
## Contract
|
|
10
|
+
|
|
11
|
+
- `app logs` uses the CLI app-identity worker. Only the app definition is needed;
|
|
12
|
+
no function bundles, full manifest build or generated client is required. Source
|
|
13
|
+
diagnostics use CLI output, and target credentials stay outside the worker.
|
|
14
|
+
- The CLI target supplies OAuth or API-key credentials, environment proxy
|
|
15
|
+
routing and cancellation. Server authorization requires `WORKFLOWS`,
|
|
16
|
+
workspace scoping and per-payload application access checks. There is no
|
|
17
|
+
execution mutation, app registration or workspace audit-log query.
|
|
18
|
+
- The selection requests nullable `name` and `universalIdentifier` fields. Server
|
|
19
|
+
schemas rejecting only these fields trigger one explicit, warned fallback to
|
|
20
|
+
`{ logs }`, before any record has been emitted. Unrelated validation,
|
|
21
|
+
authorization, network and execution errors are never retried. An explicit
|
|
22
|
+
filter provides that identity if the server cannot; unknown values stay null.
|
|
23
|
+
- A pull-driven reader uses the GraphQL `next`/`complete` SSE protocol,
|
|
24
|
+
awaits output and reads the next
|
|
25
|
+
frame only when the consumer is ready. It handles comments, multiline data,
|
|
26
|
+
split UTF-8 and LF/CRLF/CR framing. Each frame is capped at 16 MiB; the session
|
|
27
|
+
has no total byte cap. Reader locks and HTTP requests are released on exit.
|
|
28
|
+
- Setup has a 60-second deadline. After setup there is no whole-session timer;
|
|
29
|
+
the HTTP body's 60-second idle deadline detects missing heartbeats. Yoga sends
|
|
30
|
+
comments every 12 seconds in production. Undici suspends its body deadline
|
|
31
|
+
while the body is paused for backpressure and refreshes it on resume.
|
|
32
|
+
- There is no automatic reconnect or replay. EOF without `complete`, broken
|
|
33
|
+
JSON or records, redirects, oversized frames and network failures stop the
|
|
34
|
+
command. Errors retain `recordCount`; network failures explain the gap.
|
|
35
|
+
Explicit server completion exits 0, Ctrl+C exits 130. Human text escapes
|
|
36
|
+
terminal control characters; NDJSON emits start, connection progress, records
|
|
37
|
+
and a final result/error. A compatibility fallback may establish two
|
|
38
|
+
connections and therefore emit two connection progress events.
|
|
39
|
+
|
|
40
|
+
Tests cover the public command with a real local HTTP transport, duplicate-name
|
|
41
|
+
filters, compatibility failures, proxy routing, per-frame bounds, output
|
|
42
|
+
backpressure, idle heartbeats, stalled connections, completion and cancellation.
|
|
43
|
+
These tests verify protocol behavior, not live workspace authorization or
|
|
44
|
+
execution.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Manifest generation
|
|
2
|
+
|
|
3
|
+
The manifest builder constructs and validates metadata and compiles translations
|
|
4
|
+
for build, plan and apply. The worker evaluates definitions through
|
|
5
|
+
[app/source](../source/README.md); bundling and final artifact checksums belong to
|
|
6
|
+
[app/bundles](../bundles/README.md).
|
|
7
|
+
|
|
8
|
+
See the [app tooling overview](../README.md) for package ownership.
|
|
9
|
+
|
|
10
|
+
## Contract
|
|
11
|
+
|
|
12
|
+
The builder discovers definitions and assets, derives default object fields and
|
|
13
|
+
permission identifiers, infers logic-function input schemas, resolves lifecycle
|
|
14
|
+
hooks, reads the README and validates entity relationships, identifiers, role
|
|
15
|
+
permissions and conditional availability. Manifests are sorted and carry
|
|
16
|
+
checksum placeholders that the bundler replaces with final artifact checksums.
|
|
17
|
+
|
|
18
|
+
`package.json`'s `engines.twenty` sets
|
|
19
|
+
`application.requiredServerVersionRange`, with surrounding whitespace trimmed;
|
|
20
|
+
an absent or empty value becomes `null`. No CLI version is substituted. The
|
|
21
|
+
builder reports package dependency warnings and validates empty lockfiles.
|
|
22
|
+
|
|
23
|
+
Translation compilation reuses the locale catalog helpers shared with
|
|
24
|
+
pull. It preserves the distinction between no locale directory (`undefined`)
|
|
25
|
+
and an empty one (`{}`), context-dependent message IDs, authored-over-compiled
|
|
26
|
+
precedence, orphan compiled translations, collision handling and skipped-file
|
|
27
|
+
warnings. It reads catalogs without rewriting them.
|
|
28
|
+
|
|
29
|
+
Select options without an `id` receive a deterministic ID from their label and
|
|
30
|
+
field identifier. Keep an explicit `id` when changing an option's label or value,
|
|
31
|
+
so existing view filters continue to reference the same option. Duplicate option
|
|
32
|
+
IDs fail validation. Pulled options already carry their server IDs.
|
|
33
|
+
|
|
34
|
+
## Implementation
|
|
35
|
+
|
|
36
|
+
- Definition discovery, detection and evaluation reuse `app/source`, including
|
|
37
|
+
its source and ignore globs and validation-result shape check.
|
|
38
|
+
- Source and public-asset discovery is sorted so warning, error and entity file
|
|
39
|
+
order is deterministic.
|
|
40
|
+
- The loader result accepts a compile-time config type, with the same runtime
|
|
41
|
+
shape check. Config types describe the authoring values consumed by the builder.
|
|
42
|
+
Handler parameters use `never` and return `unknown`; front components expose
|
|
43
|
+
only the `name` consumed here. The builder never calls those callbacks.
|
|
44
|
+
- The version helper requires an explicit app path; the process working directory
|
|
45
|
+
cannot choose another app's version.
|
|
46
|
+
- Filesystem reads use Node and the CLI `pathExists` helper. The JSON reader
|
|
47
|
+
propagates parse errors.
|
|
48
|
+
- The worker resolves the app's authoring SDK with the source compatibility check,
|
|
49
|
+
returns structured errors and warnings, checks cancellation between phases and
|
|
50
|
+
stops esbuild before exiting. It uses credential filtering,
|
|
51
|
+
bounded output capture and cancellation/termination behavior. It does not hold
|
|
52
|
+
a snapshot or write build state.
|
|
53
|
+
|
|
54
|
+
## Verification
|
|
55
|
+
|
|
56
|
+
From `packages/twenty-cli`, after building the repository SDK, shared and UI
|
|
57
|
+
packages:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node ../../node_modules/vitest/vitest.mjs run --config vitest.config.ts --maxWorkers=1 src/app/manifest/__tests__/manifest-parity.spec.ts
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The suite bundles the SDK implementation as a test-only reference. It
|
|
64
|
+
compares manifests, entity file paths, errors and warnings for every repository
|
|
65
|
+
fixture and a freshly initialized app. It also exercises the production CLI
|
|
66
|
+
worker using a copy of the real SDK's authoring entry points without `./build`,
|
|
67
|
+
`./cli` or their implementation files.
|
|
68
|
+
|
|
69
|
+
The SDK reference's discovery is sorted to match the CLI's deterministic input
|
|
70
|
+
order. Comparisons remove only JSON-omitted `undefined` properties, matching the
|
|
71
|
+
worker IPC boundary; arrays and diagnostics are not reordered. The invalid-app
|
|
72
|
+
fixture throws `Invalid UUID` in both implementations. Additional cases cover
|
|
73
|
+
duplicate identifiers, empty lockfiles, translations, source output and credential filtering.
|
|
74
|
+
Unit tests cover individual validation and translation rules.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# CLI application pull
|
|
2
|
+
|
|
3
|
+
Pull reconciles the server's application export with local definitions and a
|
|
4
|
+
target-bound baseline. It preserves unsupported and local-only entities, reports
|
|
5
|
+
overwritten local edits, and commits source changes and the new baseline as one
|
|
6
|
+
transaction. Source generation and translation behavior are covered by parity
|
|
7
|
+
and reconciliation tests.
|
|
8
|
+
|
|
9
|
+
See the [app tooling overview](../README.md) for package boundaries.
|
|
10
|
+
|
|
11
|
+
## Contract
|
|
12
|
+
|
|
13
|
+
- Source scanning stays inside the disposable CLI worker so configs containing functions are never serialized through IPC. Only the server export and report cross that boundary. Selected credentials are removed from the worker environment. App source is trusted developer code, not sandboxed code.
|
|
14
|
+
- The wire/export and baseline boundary check identity and envelope fields, not every metadata collection. `toPullManifest` is the single explicit assertion into the typed writers, preserving their trust in the server schema. Missing/null object and field collections are consumed with `?? []`, like the other collections. Malformed entity contents can still make reconciliation fail before writing. We do not duplicate the server's schema.
|
|
15
|
+
- The baseline file is an envelope whose `manifest` member is an `ExportedManifest`. Reconciliation walks arrays generically without maintaining a second collection list. Unrecognized collections and non-array metadata survive; protected or unreconciled definitions retain their prior baseline entries.
|
|
16
|
+
- `preparePullEntities` resolves the source layout once for current and baseline entities. Planning, overwrite reporting and nested-definition protection consume these prepared entities. A definition can leave a parent only when another retained or written file contains it, or its remote deletion is confirmed.
|
|
17
|
+
- Fields already declared with `defineField` stay in their existing files, even when the server exports them inside an object. Object generation excludes those fields; baseline comparisons use the same source layout. Pull also removes duplicate inline copies left in object files, while retaining the standalone definitions.
|
|
18
|
+
- Before destination writes, `assertPullSdkExports` checks the actual imports emitted by the writer against the app's SDK. It checks export presence, not every config shape; run typecheck to validate the generated definitions against the installed SDK.
|
|
19
|
+
- Temporary files and the baseline live under `.twenty/cli`. Apply and pull share `createPullBaseWrite` as the serializer. The baseline is bound to the API URL, workspace UUID and application UUID. Pull stages it with mode 0600 and writes it after the source files.
|
|
20
|
+
- The baseline can carry `sourceFingerprints`, a SHA-256 per app-relative source and locale file. Apply captures them before building. Pull reports an overwritten local change when a file it rewrites or deletes no longer matches its fingerprint. Without a fingerprint it compares the file with the writer's rendering of the baseline, which is reliable only for source produced by that writer. Pull carries fingerprints forward, updating the files it writes and dropping the ones it deletes.
|
|
21
|
+
- Ordinary I/O failures restore originals. Cancellation is checked before committing the staged plan, not halfway through it. An acknowledged successful commit wins over cancellation. The parent can forcibly kill an unresponsive worker after its grace period, so an unacknowledged result has outcome `unknown`, never a claim that rollback succeeded. Failed rollback retains backups and reports their directory; failure to clean up after a completed commit has outcome `pulled`.
|
|
22
|
+
- Human reporting uses CLI terminal escaping, machine reporting contains the full coverage and path lists, and gaps warn without stopping successful writes. Unknown coverage status strings remain visible. Pull does not prompt for approval or require a clean Git checkout; remote changes overwrite conflicting local edits and are included in the report.
|
|
23
|
+
|
|
24
|
+
Translation string extraction uses the CLI's TypeScript parser. Traversal, static-string rules, whitespace normalization and deduplication are checked against the SDK reference by parity tests. It does not depend on ts-morph or load parser tooling for help and command discovery.
|
|
25
|
+
|
|
26
|
+
Pull accepts metadata exports with `files: []` and refuses nonempty source/dependency-file exports. It does not restore package files, generate the typed client or write logic-function/front-component source. Export responses have a 16 MiB cap. Pull does not modify the workspace.
|
|
27
|
+
|
|
28
|
+
Tests cover writers, planning and reconciliation. Command tests build the actual Vite worker, exercise an authoring-only SDK, local mock HTTP, credentials/output isolation, SDK export refusal and forced termination after the first destination write. The repository SDK tests cover canonical generated definitions, nested coverage, translation preservation, target mismatch, overwrite reporting and rollback.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# App source loading
|
|
2
|
+
|
|
3
|
+
This CLI-owned source loader powers `twenty app pull` and the [manifest builder](../manifest/README.md) used by public build, plan and apply commands.
|
|
4
|
+
|
|
5
|
+
Package boundaries are documented in the
|
|
6
|
+
[app tooling overview](../README.md).
|
|
7
|
+
|
|
8
|
+
## Boundary
|
|
9
|
+
|
|
10
|
+
`readAppIdentity` checks the installed SDK without importing it, then evaluates the application's definition in a disposable CLI worker. Source is trusted executable developer code. The child contains exits, captures bounded output and can be killed on cancellation; it is not a filesystem or network sandbox. Selected workspace credentials are removed from its environment by the worker launcher.
|
|
11
|
+
|
|
12
|
+
`scanProjectSourceFiles` stays inside the worker. Its configs may contain functions and React components, so reconciliation runs in that same worker rather than send configs over JSON IPC. Only the application UUID, display name and diagnostics cross the identity boundary.
|
|
13
|
+
|
|
14
|
+
## Compatibility
|
|
15
|
+
|
|
16
|
+
- The app supplies its own `twenty-sdk`, version `>=1.23.0`, with resolvable `twenty-sdk/define` and `twenty-sdk/front-component` entry points. The installed SDK's Node requirement is checked.
|
|
17
|
+
- There is no upper SDK version cap. Every evaluated definition must return the public `ValidationResult` shape: boolean `success`, object `config`, string-array `errors`, and optional string-array `warnings`. An incompatible shape fails with `SDK_SOURCE_UNSUPPORTED`. This validates the loader contract, not every future SDK behavior.
|
|
18
|
+
- The CLI owns esbuild, its TypeScript parser and tinyglobby as runtime dependencies. These load only for source operations. The app does not need to install TypeScript for source loading. Build and typecheck use the project's compiler separately.
|
|
19
|
+
- The pull writer separately checks the authoring exports its generated files use; this loader floor does not establish that every SDK since 1.23 supports all current manifest collections.
|
|
20
|
+
|
|
21
|
+
## Source contract
|
|
22
|
+
|
|
23
|
+
Source discovery covers root and nested `.ts`/`.tsx` files, excluding declarations, `node_modules`, `dist` and `.twenty`. Only a direct top-level `export default defineX(...)` is classified as a definition. Aliases, namespace calls and variable re-exports remain unsupported. Helpers reserve their paths without being evaluated on their own; imported helpers run as dependencies of a definition.
|
|
24
|
+
|
|
25
|
+
Definitions are bundled using the app's tsconfig and imports, then evaluated with a require rooted at the app. React resolves from the app. UI and generated-client imports use extraction stubs; these are not working UI or client implementations. CSS imports are ignored and conditional-availability expressions are transformed for extraction.
|
|
26
|
+
|
|
27
|
+
An ordinary extraction failure marks a scanned definition unreadable, retaining its path so reconciliation can avoid overwriting it. SDK validation errors leave the config available for reporting. Reading identity evaluates only application definitions, rejects duplicate or invalid application UUIDs, and returns `null` when no application is declared.
|
|
28
|
+
|
|
29
|
+
The loader checks SDK compatibility, validation-result shape and cancellation between files. Discovery is sorted: the loader, the identity reader and the scanner share `listApplicationSourceFiles`, so results do not depend on filesystem timing. Detection uses the function name rather than its import origin: a local helper named `defineObject`, for example, must be renamed if it returns an incompatible shape. Such a mismatch fails the scan. Source rewriting and reconciliation live in the adjacent `pull` module.
|
|
30
|
+
|
|
31
|
+
## Verification
|
|
32
|
+
|
|
33
|
+
Run the source-loader, SDK-resolution and worker tests with the CLI Vitest configuration. The repository-SDK fixture requires its existing SDK/UI/shared artifacts to be built, as do the other CLI app tests. Worker tests use an authoring-only SDK without a project compiler, exercise output and credential isolation, and cover source exits and cancellation.
|