1c8b8970fdb7b0bdc8c8552f2a5f43d73da67df1
13 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e0bd4ab732 |
docs(apps): make the workspace functions URL the primary route-serving story (#22693)
Part 5 of the app-docs audit series (after #22688–#22691).
## The problem
`front-components.mdx` warns that the legacy `/s/` route is deprecated
and **deactivates on 2026-07-24** (16 days from now), but the rest of
the docs still teach `/s/` as the only serving path:
`logic-functions.mdx` ("Exposes your function ... under the `/s/`
endpoint"), `logic/overview.mdx` ("A request hits your `/s/<path>`
endpoint"), and the document-generator tutorial fetches
`${TWENTY_API_URL}/s/...` from front-component code. A developer
following those pages today ships an app that breaks on Cloud in two
weeks.
## What this changes
- **logic/logic-functions.mdx** — httpRoute triggers are described as
served at the workspace's functions base URL (what the server injects as
`TWENTY_FUNCTIONS_URL`; a dedicated per-workspace domain on Cloud, per
`WorkspaceDomainsService.buildPublicFunctionBaseUrl`), with a warning
box covering the `/s/` deprecation and the self-host fallback.
- **logic/overview.mdx** — trigger table no longer hardcodes
`/s/<path>`.
- **document-generator tutorial** — the `curl
http://localhost:2020/s/...` examples stay (they're correct against the
local dev image, where no isolated functions domain exists), with a note
explaining the Cloud behavior. The front-component code snippets now use
the `TWENTY_FUNCTIONS_URL || TWENTY_API_URL + '/s'` fallback pattern —
the same one Twenty's own published apps use (e.g.
`packages/twenty-apps/public/call-recorder`).
Only English sources were touched; `l/<locale>` copies come from
Crowdin.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ExboyDAT19khDuKXaYXETT)_
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22693?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
---------
Co-authored-by: Martin <martin@twenty.com>
|
||
|
|
2781a06025 |
docs(apps): fix nonexistent SDK import paths and unsupported config in layout pages (#22689)
Part 2 of the app-docs audit series (after #22688). Every fix below was verified against the `twenty-sdk` source and its `exports` map. ## What this fixes **Broken import paths (copy-paste would not compile)** - `twenty-sdk/command` and `twenty-sdk/clients` are not export subpaths of `twenty-sdk` — 9 code samples across `front-components.mdx` and `command-menu-items.mdx` used them. `Command`, `CommandModal`, `CommandLink`, `CommandOpenSidePanelPage` actually live in `twenty-sdk/front-component`, and `CoreApiClient` in `twenty-client-sdk/core` (matching every example app in `packages/twenty-apps`). **Unsupported config** - The bulk-export example passed an inline `command: {...}` to `defineFrontComponent`, but `FrontComponentConfig` has no such property. Replaced with a separate `defineCommandMenuItem` file, which is the supported pairing. **Deprecated API in examples** - Three examples used `useRecordId()` even though the hooks table on the same page marks it deprecated. Switched them to `useSelectedRecordIds()`. - `defineCommandMenuItem`'s `icon` is deprecated (the build warns "icon will be ignored in favor of application icon") but the docs listed it as a normal field and used it in examples. Marked it deprecated in the table and removed it from examples. **Missing enum value** - `availabilityType` supports `'GLOBAL_OBJECT_CONTEXT'` (`CommandMenuItemManifest` in `twenty-shared`), which the config table omitted. **Deduplication** - The full run-action example (component + command, ~40 lines) appeared verbatim on both layout pages. `command-menu-items.mdx` now keeps only the command snippet and links to the component example on the Front Components page. Only English sources were touched; `l/<locale>` copies come from Crowdin. --- _Generated by [Claude Code](https://claude.ai/code/session_01ExboyDAT19khDuKXaYXETT)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22689?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> Co-authored-by: Martin <martin@twenty.com> |
||
|
|
25fe66565c |
feat(applications): add type and options to application variables (#22157)
## Before <img width="1452" height="709" alt="image" src="https://github.com/user-attachments/assets/cd384ffa-cbe6-49d5-a807-ca8d580f55a9" /> <img width="1074" height="452" alt="image" src="https://github.com/user-attachments/assets/720d38db-3495-4032-8831-17d24ec6a7e7" /> ## After <img width="1421" height="865" alt="image" src="https://github.com/user-attachments/assets/2275c996-c895-4800-8324-2aa2ddfddd43" /> <img width="1348" height="870" alt="image" src="https://github.com/user-attachments/assets/3e1a891d-6db0-4cbd-870a-2a5bbde4929d" /> ## Summary Adds typed application variables with optional select **options**. This is the other half of #22059, split out from the custom-settings-tab removal. ## Changes - **Shared types**: `ApplicationVariable` / `ServerVariables` gain an optional `type` (a `FieldMetadataType` subset — `TEXT`, `BOOLEAN`, `NUMBER`, `DATE`, `SELECT`, `MULTI_SELECT`, `RAW_JSON`, `RICH_TEXT`, `ARRAY`, …) and select `options`. New `serializeApplicationVariableValue` / `deserializeApplicationVariableValue` helpers convert typed values to/from the encrypted string storage. - **Server**: `type`/`options` columns on `applicationVariable` and `applicationRegistrationVariable` (entities + DTOs), a fast `2-17` instance command, manifest processing via the serialization helpers, and a `QueryDeepPartialEntity` cast where the manifest JSON column is persisted. - **Frontend**: a polymorphic `SettingsApplicationVariableInput` that renders the native `Form*` field component for each type (boolean, number, date/date-time, select, multi-select, array, raw JSON, rich text, text); fragment/query updates to fetch `type`/`options`. - **SDK**: `defineApplication` validates that `SELECT`/`MULTI_SELECT` variables declare non-empty `options` at build time (since `options` is kept structurally optional for TypeORM/SDK compatibility). Variables default to `TEXT` when no type is given, so existing manifests are unaffected. ## Notes The generated GraphQL artifacts (`type`/`options` on the variable types) are regenerated by codegen; that change accompanies this PR. https://claude.ai/code/session_013Z7UB35V2mvUozh55QHG23 --- _Generated by [Claude Code](https://claude.ai/code/session_013Z7UB35V2mvUozh55QHG23)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22157?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> |
||
|
|
0dc6272da5 |
Remove twenty-ui reexport from the SDK and use twenty-ui directly (#22326)
## What & why Removes the `twenty-sdk/ui` reexport. Apps now use Twenty UI by installing [`twenty-ui@1.0.0-alpha.1`](https://www.npmjs.com/package/twenty-ui/v/1.0.0-alpha.1) from npm and importing its subpaths directly. The reexport re-exported types that didn't resolve, forcing typecheck workarounds. ## Changes - **twenty-sdk**: delete `src/ui/index.ts`, drop the `./ui` export, remove it from the browser vite build, and rewire the CLI manifest-mock to `twenty-ui` (`.css` falls through to the empty-CSS loader). `twenty-ui` stays a devDependency for the CLI fixture tests. - **Renderer + create-twenty-app template**: import from `twenty-ui` subpaths; the template pins `twenty-ui@1.0.0-alpha.1`. - **Docs**: new "Using Twenty UI components" section (install + subpath imports + `useTheme()` for theme tokens), codex references, and the cross-doc-contract validator. The `twenty-for-twenty` / `twenty-slack` example apps are intentionally left on `twenty-sdk/ui`: they consume the published SDK (which still ships `./ui`), and `twenty-ui@1.0.0-alpha.1` requires react 19 + a `monaco-editor` peer the react-18 apps can't satisfy. They migrate once the SDK is republished. |
||
|
|
5242ddf458 |
feat(apps): let front components open a record in the side panel (#22140)
## Why Front components (apps) could `navigate()` to a record's **full page**, but there was no way to open a specific record in the **side panel**. More generally, `openSidePanelPage` could navigate to a `SidePanelPages` enum page but couldn't pass the context most pages need. ## What `openSidePanelPage`'s params are now a **discriminated union keyed on `page`**, so each page declares its own typed payload (instead of a flat bag of optionals whose validity silently depends on `page`). This is also safer: pages that can't render without context can't be "opened" into a broken panel. Wired the param-bearing pages host-side, each bridging to its existing internal hook: | `page` | Params | Bridges to | |---|---|---| | `ViewRecord` | `recordId`, `objectNameSingular`, `resetNavigationStack?` | `useOpenRecordInSidePanel` (full-page fallback on mobile / unsupported objects) | | `EditRichText` | `recordId`, `objectNameSingular`, `fieldName?` | `useOpenRichTextInSidePanel` | | `ComposeEmail` | `connectedAccountId`, `threadId?`, `defaultTo?`, `defaultSubject?`, `defaultInReplyTo?`, `pageTitle?`, `pageIcon?` | `useOpenComposeEmailInSidePanel` | | `ViewFrontComponent` | `frontComponentId`, optional `recordId`+`objectNameSingular`, `pageTitle`, `pageIcon?`, `resetNavigationStack?` | `useOpenFrontComponentInSidePanel` | | *(any other page)* | `pageTitle`, `pageIcon?`, `shouldResetSearchState?` | `navigateSidePanel` | `CommandOpenSidePanelPage` now takes the union directly, so headless command-menu items can open any of these. Threaded through `twenty-sdk` → `twenty-front-component-renderer` → host (`useFrontComponentExecutionContext`), with unit tests per page and the mobile/unsupported fallbacks. ## Deliberately deferred: `MergeRecords` `useOpenMergeRecordsPageInSidePanel` takes `objectNameSingular` / `objectRecordIds` at **hook-init** (it calls `useObjectMetadataItem` / `useLazyFindManyRecords` at render), so it can't be driven by runtime app params without refactoring that hook + its current caller. Left out of this PR — better as its own change. ## Worth a second look (reviewers) - **`ViewFrontComponent`** lets an app open a front component by id. Within an app that's clean composition; whether an app should be able to target *another* app's component is a scoping/security question. The render still runs under the app's access token, so cross-app fetches would fail auth — but flagging it explicitly. ## Security note Side-panel record/page views render natively under the **user's** session/Apollo client, not the app's scoped token — RLS/field permissions are enforced as if the user opened it themselves. Same trust model as `navigate(AppPath.RecordShowPage, …)`. ## Follow-up A separate PR will centralize the mobile + `canOpenObjectInSidePanel` guard inside `useOpenRecordInSidePanel` (currently duplicated across callers, missing in others). ## Validation > [!NOTE] > Dependencies wouldn't install in this environment (flaky network during `yarn install`), so lint / typecheck / jest weren't run locally — relying on CI. The diff was reviewed manually for type-consistency, including the discriminated-union narrowing in the host switch. https://claude.ai/code/session_01AAJFXzsCeoj6BeP3ofiTKQ |
||
|
|
614bc7b7e6 |
feat: serve HTTP logic functions on isolated *.withtwenty.com domain (#22045)
## Summary Implements [core-team-issues#2473](https://github.com/twentyhq/core-team-issues/issues/2473): serve HTTP-triggered logic functions from a dedicated, **cookieless** public domain (`{workspaceSubdomain}.withtwenty.com`) instead of the same-site `/s/` route, so functions can safely return **arbitrary headers** — custom headers, `Permissions-Policy` (camera/mic/geolocation), `Cross-Origin-Opener-Policy: same-origin`, `Cross-Origin-Embedder-Policy: require-corp`, `Set-Cookie`, etc. The `/s/` route stays the strict, same-site path it is today. **Self-hosting is unchanged** — everything new is gated on `PUBLIC_DOMAIN_URL` being set. ### Why Today user-authored function responses are served same-site with the Twenty app, so the response-header allow-list is restricted to 5 safe headers and request headers are limited to a per-function allow-list. Serving from an origin that shares nothing with `*.twenty.com` removes that constraint safely — the same "user content domain" pattern as GitHub (`*.githubusercontent.com`) and CodeSandbox (`*.csb.app`). ## What's in here **Routing** - The **root-path → `/s` rewrite happens at the nginx ingress**, not in app code. The existing `api-ingress.yaml` already rewrites root paths onto `/s` (host-agnostically) when the edge sets `X-Twenty-Public-Domain: true`, so `*.withtwenty.com` and registered custom public domains are handled by the same mechanism. (An earlier in-app middleware was removed as a redundant, wrong-layer duplicate.) - `WorkspaceDomainsService.resolveWorkspaceAndPublicDomain` recognizes `*.` subdomains, resolves the workspace by subdomain, and returns `isIsolatedOrigin`. Explicitly registered public-domain rows still take precedence and keep their application scoping. The ingress preserves the `Host` header, so this resolution still fires. **Headers (server)** - Isolated origin → all response headers pass through and all request headers are forwarded. Same-site `/s/` keeps the strict allow-lists. (Global CORS already handles preflight/ACAO.) **`/s/` deprecation for new routes (cloud only)** - New `LOGIC_FUNCTION_LEGACY_ROUTE_CUTOFF` config var (ISO date, optional). When `PUBLIC_DOMAIN_URL` is set, functions created on/after the cutoff return **410 Gone** on `/s/` with the new URL. Existing routes and self-hosted instances are untouched. **Frontend education** - `publicFunctionDomain` added to `ClientConfig` (from `PUBLIC_DOMAIN_URL`). - The logic-function **Live URL** now resolves to `https://{workspaceSubdomain}.{publicFunctionDomain}{path}` on cloud, falling back to `/s/` for self-hosting. - Front components call their functions through the SDK (`RestApiClient`), which now targets the isolated domain via the injected `TWENTY_FUNCTIONS_URL`. - New **"Public URL"** section on the application **Settings** tab explaining the isolated domain (shown when the app exposes HTTP-triggered functions). **Docs**: note the `withtwenty.com` domain for external callers in the apps guide. ## Infra prerequisites (not code — needs dashboard work) - Wildcard DNS `*.withtwenty.com` (proxied) + wildcard TLS in the public-domain Cloudflare zone. - Edge (Cloudflare) sets `X-Twenty-Public-Domain: true` for `*.withtwenty.com` requests, so the existing nginx ingress rewrites them onto `/s` (same header the custom-domain flow already relies on). - Set `PUBLIC_DOMAIN_URL=https://withtwenty.com` on cloud. - Submit `withtwenty.com` to the **Public Suffix List** (required for cross-tenant cookie isolation before relying on `Set-Cookie`). ## Test plan - [x] `nx typecheck twenty-server`, `nx typecheck twenty-front` - [x] `lint:diff-with-main` + oxfmt clean (server + front) - [x] `npx jest route-trigger public-function-domain domain-server-config workspace-domains build-logic-function-event client-config` → server unit tests passing (resolution tiers, header passthrough vs allow-list, `/s/` cutoff 410) - [x] `npx jest getLogicFunctionHttpUrl` (front) and `nx test twenty-client-sdk` (RestApiClient routing) passing - [x] CI green (server, front, sdk, renderer, ui, zapier, example apps) - [ ] Manual: hit `{subdomain}.withtwenty.com/` end-to-end once infra is provisioned <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22045?utm_source=github" rel="nofollow noreferrer noopener" target="_blank">``<img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg">``</a> |
||
|
|
5207493cda |
Add useColorScheme hook to twenty-sdk (#21595)
Ability to update front compoonent design according to the dark or white theme of the UI <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21595?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> |
||
|
|
27aea728df |
2474 add a utils to perform route trigger logic function requests in front components (#21330)
- exports `RestApiClient` from 'twenty-client-sdk/rest';` - documents the rest client |
||
|
|
b2539f5b6a |
Prevent conditional availability variables from being used at runtime (#21110)
Fixes https://github.com/twentyhq/twenty/issues/21094 Conditional availability variables (`objectMetadataItem`, `numberOfSelectedRecords`, `objectPermissions`, operators like `everyEquals`/`none`, etc.) are compile-time-only constructs used in `conditionalAvailabilityExpression`. They were previously exported from `twenty-sdk/front-component`, which let developers mistakenly import them into runtime component code where they have no value. - Move conditional availability variables from `twenty-sdk/front-component` to `twenty-sdk/define`. - Add a build-time manifest validation (validate-conditional-availability-usage) that fails the build if these variables are imported/used outside of `conditionalAvailabilityExpression`. - Update the github-connector example app to register commands via dedicated *.command-menu-item.ts files instead of inline command config in front components. - Update docs (all locales) and test mocks to reflect the new import paths. |
||
|
|
57118a868f |
Docs update: Calling a logic function from a front component (#21057)
Documents how a headless front component calls a server-side logic function over HTTP via the /s/ route, so AI agents have a clear reference for implementing this pattern. |
||
|
|
d5ff9eb515 |
Create twenty app improvements (#20688)
create-twenty-app updates: - remove --example option - sync --once when scaffolding an applicaiton - rename --api-url option to --workspace-url - create a standalone page when scaffolding an app <img width="1494" height="765" alt="image" src="https://github.com/user-attachments/assets/0e35ed0c-b0aa-466c-9f56-7939294fd2cf" /> --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> |
||
|
|
dea1f89904 |
Inject none secret env variables into front components (#20511)
## Summary - Inject non-secret application variables (`isSecret: false`) into front component `process.env` via the existing Web Worker `setWorkerEnv` mechanism - Filter secret variables server-side in the resolver so they never reach the browser - Set application variables before system variables (`TWENTY_API_URL`, `TWENTY_APP_ACCESS_TOKEN`) to prevent override - Wire up environment variable keys in the logic function code editor for TypeScript autocomplete ## Test plan - [x] Unit tests for `buildNonSecretEnvVar` (6 passing) - [x] Typecheck passes for `twenty-front` and `twenty-server` - [x] Install an app with both `isSecret: false` and `isSecret: true` variables, open a front component, verify only non-secret vars appear in `process.env` - [x] Open a logic function editor, verify autocomplete suggests declared variable keys |
||
|
|
2eae25dc34 |
Improved create-twenty-app documentation for AI coding agents (#20325)
Added a bit of enhanced context for better agentic coding, based on this [Discord conversation](https://discord.com/channels/1130383047699738754/1130383048173682821/1501538550301331477). --------- Co-authored-by: Félix Malfait <felix@twenty.com> Co-authored-by: Félix Malfait <felix.malfait@gmail.com> |