b768441c13aed1c978522f59d2cc326f3bd5a708
191 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
adf6eb572b |
feat(billing): embed Stripe Payment Element in onboarding (#21759)
## What & why Replaces the hosted Stripe Checkout redirect on the onboarding "Choose your plan" step (credit-card trial) with an inline Stripe **Payment Element**, so users never leave the app to enter card details. ## How it works - **Frontend:** a deferred `<Elements mode="setup">` renders the Payment Element, themed via the Appearance API. On Continue: `elements.submit()` → `checkoutSession` mutation creates the trialing subscription server-side and returns its pending SetupIntent `clientSecret` → `stripe.confirmSetup()` confirms the card (handling 3DS) → redirect to the existing `/plan-required/payment-success`. - **Backend:** new `BILLING_STRIPE_PUBLISHABLE_KEY` config var exposed via `/client-config`; the card path creates the subscription with `payment_behavior: default_incomplete` + a free trial (so Stripe attaches a `pending_setup_intent`) and returns its client secret. The hosted-Checkout code path is removed. - The **no-credit-card** trial path is unchanged. - Billing address collection is **disabled** in the Payment Element to reduce friction; `automatic_tax` is correspondingly disabled (tax needs an address — collect it later, e.g. at conversion / via the billing portal). ## Required before this works 1. Set `BILLING_STRIPE_PUBLISHABLE_KEY` (`pk_…`) on the server (infra change pending). 2. Run `nx run twenty-front:graphql:generate --configuration=metadata` against a server exposing the updated schema (see inline note on the hand-authored document). 3. Verify in Stripe test mode: happy path, 3DS (`4000 0025 0000 3155`), a decline. ## Verified typecheck (front + server), oxlint + oxfmt clean, `client-config.service.spec` passing. Not run here: the app end-to-end / Stripe test mode and `graphql:generate` (no server/DB in the dev container). I've left self-review comments inline flagging cleanup opportunities plus a couple of architectural/tech-debt items. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01TxCfinXq7abSrbF7aTw2cA --- _Generated by [Claude Code](https://claude.ai/code/session_01TxCfinXq7abSrbF7aTw2cA)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21759?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: Claude <noreply@anthropic.com> |
||
|
|
9c9c34fccf |
Remove twenty-ui-deprecated and migrate frontend to twenty-ui (#21596)
Migrates `twenty-front`, `twenty-sdk`, and `twenty-front-component-renderer` from `twenty-ui-deprecated` to `twenty-ui` (mechanical import swap — the packages have API parity) and deletes the deprecated package along with its workspace/CI/config wiring. Also adds `@linaria/react`/`@linaria/core` as direct deps of `twenty-front` (it used them transitively via the deprecated package). Note: move the required status check from `ci-ui-status-check` to `ci-new-ui-status-check`. Argos: the Storybook box-model/button-reset baseline shift (the bulk of the visual diffs) is isolated in #21665 — Storybook now loads twenty-ui's global `reset.scss`, which the production app already ships. Once #21665 merges and this branch is rebased, the remaining Argos diffs are component-level visual-parity items only. |
||
|
|
9dd097e11e |
fix(front): set up Monaco workers for GraphQL playground (#21620)
## Problem The GraphQL API playground (`/settings/playground/graphql/core`) crashes with: ``` Uncaught Error: Cannot read properties of undefined (reading 'toUrl') at FileAccessImpl.toUri (monaco-editor) at WorkerManager.getLanguageServiceWorker (graphqlMode) at DiagnosticsAdapter._doValidate (graphqlMode) ``` ## Root cause GraphiQL 5 (adopted in the React 19 migration, #21531) renders its editors with **Monaco** instead of CodeMirror. Monaco spawns web workers for GraphQL validation/autocomplete and needs a `globalThis.MonacoEnvironment.getWorker` factory. None was ever configured, so Monaco fell back to a main-thread worker whose URL resolves to `undefined` → the `toUrl` crash. ## Why not the official helper GraphiQL ships `@graphiql/react/setup-workers/vite`, but its bundled `?worker` imports are incompatible with our rolldown-based Vite setup: - **pre-bundled** (in `optimizeDeps`): esbuild's optimizer can't process `?worker` → the dep 504s and the page fails to load the chunk. - **excluded** from `optimizeDeps`: rolldown tries to load `editor.worker.js?worker` as a literal path → `UNLOADABLE_DEPENDENCY`, crashing the dev server. ## Fix - Register `MonacoEnvironment.getWorker` in **app source** (`setupGraphiqlMonacoWorkers.ts`), where Vite's worker plugin handles `?worker` reliably, and side-effect import it from `GraphQLPlayground.tsx` before GraphiQL mounts. - Align `monaco-editor` to `0.52.2` and add `monaco-graphql@1.8.0` as direct deps so the workers run on the **same deduped Monaco instance** GraphiQL uses on the main thread (a version mismatch would break the worker protocol). ## Verification Ran the playground locally against the dev server: - Editor renders, syntax highlighting works, operation name parses (GraphQL language service alive). - All three worker files (`editor`, `json`, `graphql`) load `200` and instantiate as module workers. - Console is free of `toUrl` / `Cannot read` errors and the "must define MonacoEnvironment.getWorker" warning. - `oxlint`, `oxfmt`, and `nx typecheck twenty-front` pass. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21620?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. --> |
||
|
|
88b9294afd |
feat(front): persist metadata store cache in IndexedDB instead of localStorage (#21586)
## Problem The metadata store cache (object/field metadata, views, page layouts, command menu items, …) is persisted client-side to power **cache-first boot**: the app renders instantly from the cache, then `MinimalMetadataLoadEffect` revalidates per-collection hashes and only refetches what's stale. It was persisted to **localStorage**, which Safari/WebKit caps at **~5 MB per origin, counted in UTF-16 (2 bytes/char)** → an effective ceiling of ~2.5 M characters. Measured on the seeded demo workspace (33 objects, 612 fields): | Bucket | Safari quota (UTF-16) | |---|---| | `metadataStoreState__*` (26 keys) | **1.9 MB — 37%** | | Whole origin | **2.47 MB — 48%** | A workspace ~2.5× the demo's schema blows past 5 MB, and there is **no `QuotaExceededError` handling** — `setItem` throws and breaks the app. This is what large-workspace users on Safari have been hitting. ## Fix Move **only the metadata store** to **IndexedDB** (multi-GB, disk-based quota), keeping a **fully synchronous read path** so the ~24 consumers that read these atoms with `useAtomValue` never suspend. The auth/UI atoms (incl. the synchronously-read `tokenPair`) stay on localStorage — intentionally scoped. - **`createIndexedDbBackedJotaiStorage.ts`** — a synchronous Jotai storage facade backed by an in-memory map, hydrated once from IndexedDB at boot and written through on every set. IndexedDB access uses the **`idb-keyval`** library (by the IndexedDB spec co-author, ~0.6 KB) rather than a hand-rolled wrapper. Each cache gets its own database + BroadcastChannel (`twenty-front-<cacheName>`), so it's safely reusable. Swallowed errors are surfaced via `logError`. When IndexedDB is unavailable the cache stays in memory only (re-fetched each boot). - **`createAtomFamilyState`** — gains an optional `storage` param; `metadataStoreState` uses the IndexedDB-backed storage. - **`index.tsx`** — awaits hydration before mounting so atoms (`getOnInit: true`) read the persisted snapshot synchronously → cache-first boot preserved. - **No migration**: the facade does not touch localStorage at all. Pre-existing localStorage snapshots are ignored — on first boot of the new code the IndexedDB cache is empty and atoms re-fetch from the network (a one-time reconnect). Old `metadataStoreState__*` localStorage keys are left in place (cleared by the existing logout/reset cleanup); new writes only ever go to IndexedDB. - **Cross-tab sync**: the old localStorage atoms synced across tabs for free via `storage` events; the IndexedDB facade had no equivalent, so a schema change in one tab left others stale until reload. Restored by implementing the Jotai storage `subscribe` contract over a **`BroadcastChannel`** — writes broadcast to other tabs, which update their in-memory map and notify `atomWithStorage` subscribers so mounted atoms re-render live. (BroadcastChannel doesn't echo to the sender, so no feedback loop; guarded for environments without it.) ## Why a synchronous facade (not async `atomWithStorage`) Consumers use `useAtomValue` directly; an async storage would make the atoms resolve to Promises and **suspend** every reader. The in-memory facade keeps reads synchronous (zero ripple on consumers) and confines the async part to a single bulk read at boot, which the existing `MinimalMetadataGater` loader already covers. ## Tests ### Automated - Unit test (10 cases) for the storage facade: synchronous read/write, IndexedDB write-through, hydration from IndexedDB, `removeItem`/`clear`, per-cache DB namespacing, persist-failure logging, in-memory-only behaviour when IndexedDB is unavailable, distinguishing a stored `undefined` from a missing key, and cross-tab subscriber registration. - Existing metadata-store tests (`useIsLayoutCustomizationDirty`, `useDefaultHomePagePath`) still pass. - `nx typecheck twenty-front` and `nx lint:diff-with-main twenty-front` clean. ### Manual (local seeded workspace, two tabs, Playwright) Storage: - After login the metadata cache lives in **IndexedDB (24 keys, ~945 KB)** and **localStorage drops 48% → 11%** of the Safari quota (the remainder is `currentUserState` + auth, out of scope). - Reload boots from the cache (no heavy refetch). Scenarios: | Scenario | Result | |---|---| | **Sign out** | auth cleared, redirect to sign-in, no leftover localStorage, no errors | | **Sign back in** | metadata `up-to-date`, company table renders, token restored | | **Add object** (`Gadget`) | write-through to IndexedDB; survives reload via cache-first hydration | | **Add view** (`QA Cross Tab View`, TABLE) | persisted to the `views` collection (`up-to-date`) | | **Two tabs open** | second tab boots cleanly from the shared IndexedDB — no lock/crash under concurrent access | | **Cross-tab live sync** | creating an object in tab A makes it appear in tab B's open settings object list **without a reload** | Verified by design (no regression): - Runtime sign-out (`clearSession`) clears session keys and does a full `window.location.assign` reload; the metadata-clearing path (`resetJotaiStore`) is test-only, so there's no async-`clear()`-vs-sign-in race. Metadata persisting across sign-out is unchanged from the old localStorage behavior (it's schema, revalidated by hash on next login). ## Notes / follow-ups (not in this PR) - **IndexedDB query capabilities** are not used yet: the cache stores one blob per collection (as it did in localStorage), so this is still a pure key-value use (`idb-keyval`). If we later want to query individual metadata records — e.g. fields by `objectMetadataId` via an index/cursor, or partial hydration — that means record-level storage and a richer wrapper (`idb` for a thin near-native layer, or **Dexie** for a full query API + reactive `liveQuery` that could also replace the BroadcastChannel sync). - IndexedDB still has a (large) quota and Safari ITP eviction applies to both stores — the cache-first design already tolerates eviction by revalidating. - Complementary "load less" wins remain: the denormalized per-field `relation` block (~700 chars/field of pure duplication) and persisting `currentUser.workspaceMembers` (the ~0.5 MB still in localStorage). <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21586?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. --> |
||
|
|
fb4608e437 |
chore(deps): upgrade Tier-1 deps (googleapis 173, gaxios 7, express 5, jsdom 29, date-fns 4, stripe 20) (#21570)
## What Security-driven upgrade of the biggest-drift Tier-1 dependencies (staying on latest = staying patched). Bundled because they share the lockfile and the googleapis/gaxios pair must move together. | Package | From | To | Gap | |---|---|---|---| | googleapis | 105.0.0 | **173.0.0** | 68 majors | | gaxios | 5.1.3 | **7.1.5** | 2 majors | | express | 4.22.2 | **5.2.1** | 1 major | | jsdom | 26.1.0 | **29.1.1** | 3 majors | | date-fns | 2.30.0 | **4.4.0** | 2 majors | | date-fns-tz | 2.0.0 | **3.2.0** | 1 major | | stripe | 19.3.1 | **20.4.1** | 1 major | `yarn npm audit` reports **0 high/critical** advisories before and after. ## Code changes - **gaxios v7** — `GaxiosError.code` is now `string | number` (guard the calendar network-error check by `typeof`); `GaxiosError` config/response use `URL` + `Headers`; and crucially the v7 constructor drops `response.data` unless `bodyUsed` is set — updated the synthetic gmail error mocks accordingly (production gaxios sets it, so real error parsing is unaffected). - **google-auth-library / gaxios dedup** — `googleapis-common@8.0.2` exact-pins `google-auth-library@10.5.0` + `gaxios@7.1.3` while `googleapis` pulls `^10.2.0`; the two copies made `OAuth2Client`/`GaxiosError` type-identities diverge across every gmail/calendar service. Added two singleton `resolutions` (documented inline in root `package.json`). - **express 5** — no source changes. `@nestjs/platform-express@11.1.24` already resolves `express@5.2.1` internally; the old `4.22.2` pin was the override. - **jsdom 29** — no source changes, but it now pulls ESM-only transitive deps (`@csstools/*` `.mjs`, `parse5`, `entities`, `tough-cookie`, `@exodus/bytes`). Extended the server jest `transformIgnorePatterns` allowlist and added `.mjs` to the transform/extensions so jest can load jsdom. - **stripe 20** — `Subscription` gained a required `customer_account` field; added to mocks. No runtime changes. - **date-fns v4** — `Locale` is no longer ambient (import explicitly in 5 files); per-locale entrypoints dropped the typed `default` export (the locale loader now reads the single named export); fixed the default locale import in `formatTimeZoneLabel`. ## Tests - Full suites green locally: **twenty-server 5709 passed**, **twenty-front 4937 passed**, twenty-ui / twenty-ui-deprecated green; typecheck + builds (swc + vite) + lint all pass. - Added regression tests for the two runtime behaviors these upgrades touch and that had no coverage: - `getDateFnsLocale` — named-export locale resolution (date-fns v4). - `sanitizeFile` — jsdom 29 + DOMPurify still strips `<script>`/event handlers from uploaded SVGs (security guard). ## Deliberately deferred (not in this PR) - **stripe → 21/22**: stripe **21** bundles a runtime `Decimal` type for money fields **and** jumps the pinned API version to `2026-03-25.dahlia` (changes webhook/billing payload behavior) — too risky to fold into a deps bump on billing code. stripe **22** additionally drops the node10-resolvable `types` entry, which would force a repo-wide `moduleResolution` change. Capped at the latest clean **20.x**. - **openid-client → 6**: v6 is a full functional rewrite and its passport strategy manages the OAuth `state` internally, but our SSO flow uses `state` to carry `identityProviderId` across the shared `/auth/oidc/callback`. That needs an auth-flow redesign (session-carried provider id) on Enterprise SSO code with no integration harness — it deserves its own focused PR rather than riding along here. ## Tier-1 source Originated from a dependency-drift audit; remaining Tier-1 items (date-fns done here) plus Tier-2/3 follow-ups tracked separately. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21570?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. --> |
||
|
|
7c0136b97b |
feat(deps): migrate frontend to React 19 (#21531)
## What Migrates the frontend stack from **React 18.3 → 19.2**. The website, sdk, companion and emails packages were already on React 19; this brings the remaining holdouts (`twenty-front`, `twenty-ui`, `twenty-ui-deprecated`, `twenty-front-component-renderer`) and `twenty-server`'s email rendering onto 19, and pins a single React version repo-wide. ## Why React 18.x is now the legacy line. Staying current keeps us on the patched/maintained branch and unblocks downstream library majors (react-router 7, mantine 9, etc.) that require React 19 peers. ## Dependency bumps (required by React 19 peers / removed APIs) | Package | From | To | Reason | |---|---|---|---| | react / react-dom | 18.3.1 | 19.2.3 | core | | @hello-pangea/dnd | 16 | 18 | peer `^18 \|\| ^19` | | react-datepicker | 6 | 9 | v<7 used removed `findDOMNode`; drops `@types/react-datepicker` | | react-data-grid | beta.13 | beta.59 | peer `^19.2`; new render API | | graphiql (+ @graphiql/react, plugin-explorer) | 3 / 0.23 / 1 | 5 / 0.37 / 5.1 | peer `^18 \|\| ^19` | | react-helmet-async | 1.3 | **@dr.pogodin/react-helmet** 3.2 | upstream caps peer at `^18`; drop-in React 19 fork | A `resolutions` pin enforces a single React (19.2.3) + `@types/react` (19.2.14) across the monorepo to avoid duplicate copies / type-identity splits. Versions are the aged lockfile patches (clears the `npmMinimalAgeGate`). ## Code changes - **Global `JSX` shim** (`react-jsx-global.d.ts` per package): React 19 moved the `JSX` namespace under `React.JSX`; several deps' published types (notably `@linaria/react`'s `styled.d.ts`, which types every `styled.x` via `keyof JSX.IntrinsicElements`) still reference the global namespace. Without the shim, every styled component degrades to `any` props. - **Ref nullability**: `useRef<T>(null)` now returns `RefObject<T | null>`; widened consumer prop/hook ref types accordingly (incl. the shared `useListenClickOutside`). - **react-datepicker v9**: `onChange`/`onSelect` accept `Date | null`, `calendarStartDay` typing, `ReactDatePickerProps`→`DatePickerProps`, relaxed the dynamic `selectsMultiple` discriminated union. - **react-data-grid beta.59**: `formatter`→`renderCell`, `editor`→`renderEditCell`, `headerRenderer`→`renderHeaderCell`, `components`→`renderers`, `onRowClick`→`onCellClick`, object-shaped `useRowSelection`, Set-based selection. - **dnd style cast**: `@radix-ui/react-popper` augments `CSSProperties` with a `--radix-*` index signature that dnd's closed `DraggingStyle` doesn't satisfy → cast at the spread. ## Status / testing - ✅ `typecheck` green: twenty-front, twenty-ui, twenty-ui-deprecated, twenty-front-component-renderer, twenty-server - ⏳ build / lint / unit tests / storybook+argos / runtime smoke-test in progress Draft until local + CI verification completes. Notable behavior to QA manually: spreadsheet import (data-grid), date pickers, drag-and-drop boards/lists, GraphQL playground, page titles/favicon. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21531?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. --> |
||
|
|
869680a5a1 |
fix(deps): esbuild ^0.28.1 floors + vite 7→8 (rolldown) upgrade (#21517)
## What this does Resolves the remaining esbuild security alerts on packages we own, and upgrades the repo to **Vite 8** (which drops esbuild entirely in favour of rolldown/oxc). ### 1. esbuild → `^0.28.1` (security) - Raised the declared `esbuild` floor in `twenty-sdk` and the logic-function common-layer (both were `^0.25.0`, which can only resolve to a vulnerable version). These are our packages, so this is just declaring the patched version — clears Dependabot **#1467** and **#1468**. ### 2. Vite 7 → 8 - Bumped `vite` to `^8` in the 5 packages that declare it, and `@vitejs/plugin-react-swc` to `^4.3.1` (the only plugin that needed a bump for Vite 8; everything else already supports it). - `twenty-front` keeps esbuild minification, so esbuild is now an explicit (patched) devDependency there — Vite 8 no longer ships it. ### Two Vite-8 fallout fixes (bundler internals changed) - **Storybook tests:** added React to `optimizeDeps.include` so Vite's dep optimizer doesn't re-bundle React mid-run and break in-flight imports in browser-mode tests. - **`hex-rgb`:** it's ESM-only and broke rolldown's CJS interop (a default import resolved to the wrong thing under jest). Replaced its one use with a tiny inline hex→rgb parse and dropped the dependency. ## Verified Vite resolves to a single `8.0.16` with no esbuild in its tree. Builds pass on Vite 8/rolldown: `twenty-front` production build, the SDKs, and Storybook; the previously-failing front and storybook test jobs now pass; `yarn install --immutable` is clean. ## Note This doesn't close root alert **#1469** — esbuild is still pulled by other third-party tools (storybook, tsx, lingui, zapier, etc.) that haven't shipped a patched release. The vulnerable code path (esbuild's dev server) isn't used here, so that one is best dismissed as not-affected. |
||
|
|
c4453923f0 |
Update CI: Argos visual regression for twenty-front storybook (#21454)
## What Adds Argos visual regression for `twenty-front`, reusing the storybook CI already builds and the existing sharded test matrix. Stories in the `modules` and `pages` scopes are captured as PNGs during `front-sb-test`, merged into one artifact, and pixel-diffed against `main` on the self-hosted Argos with results posted as a PR comment — same pipeline as `twenty-ui` (#21210 / #21262). ## How - **Capture**: `@argos-ci/storybook` vitest plugin, same setup as `twenty-ui`. Skipped for `performance` stories (nondeterministic profiling reports). Freezes framer-motion to avoid flaky diffs (#21412). - **Sharding**: each modules/pages shard uploads a partial artifact; a new `front-sb-screenshots` job merges them into `argos-screenshots-twenty-front` (`overwrite: true` so re-runs work). - **Baselines**: `CI Front` now runs on `push: main` — Argos resolves base builds by exact merge-base commit, so every main commit needs a build (#21217/#21222 pattern). Main pushes get a per-SHA concurrency group so back-to-back merges can't cancel queued runs and leave baseline gaps; the `performance` scope is dropped on push. - **Dispatch**: `visual-regression-dispatch.yaml` watches `CI Front` → `project=twenty-front`. ## Rollout - ✅ Prod Argos project `twenty-front` created (id 68) + `ARGOS_TOKEN_FRONT` secret set - ⬜ Merge the twentyhq/ci-privileged companion PR **before** this one - First PR builds show as *orphan* until the first main push creates a baseline (expected, same as the twenty-ui rollout) |
||
|
|
184c4948d6 |
security: strip Node dev headers from images + lingui 5.9.5 (drops vulnerable esbuild) (#21448)
## Context
AWS Inspector flags the `prod-twenty` image (built from current main)
with 16 findings, and Dependabot alert 174 flags esbuild. This PR fixes
the OpenSSL scanner findings and the esbuild CVE. The typeorm bump
(CVE-2025-60542) was **pulled out of this PR** — see "typeorm status"
below.
## Changes
### Strip `/usr/local/include/node` from runtime stages
(`twenty-server`, `twenty-app-dev`)
15 OpenSSL CVEs (June 9 advisory, incl. CRITICAL CVE-2026-34182) are all
detected via **Node's bundled OpenSSL dev headers**: 3 GENERIC
`openssl/openssl` 3.5.6 detections per CVE at
`/usr/local/include/node/openssl/archs/linux-x86_64/{asm,asm_avx2,no-asm}/include/openssl/opensslv.h`.
The headers are only needed by node-gyp and native addons are compiled
in the build stages — nothing compiles at runtime. Dropping them clears
all 45 detection instances and permanently ends this class of finding
(third occurrence: 3.5.5 → 3.5.6 → 3.5.7). None of these CVEs are
reachable through Node (no CMS/PKCS#7 API, `pfx` is operator-supplied,
Node's QUIC uses ngtcp2, ASN.1 issues need ~2GB inputs).
**Follow-up (~June 17, 2026):** the `node` binary itself still
statically links OpenSSL 3.5.6 — invisible to the scanner after this PR
and unreachable in practice, but the real fix is bumping the pinned
`node:24-alpine` digest once the [announced June 17 Node.js security
releases](https://nodejs.org/en/blog/vulnerability/june-2026-security-releases)
ship a 24.x linking OpenSSL ≥ 3.5.7 (verify via
`deps/openssl/openssl/VERSION.dat` on the release tag — 24.16.0 is still
on 3.5.6). A dated TODO sits next to the cleanup in the Dockerfile.
### esbuild dev-server CORS CVE (Dependabot alert 174,
GHSA-67mh-4wv8-2f99)
`@lingui/cli@5.1.2` (pins `esbuild ^0.21.5`) was the last parent
resolving a vulnerable esbuild (≤ 0.24.2 lets any website send requests
to the dev server and read responses). Instead of a resolution override,
this bumps the lockstepped **lingui suite 5.1.2 → 5.9.5** (within-major;
lingui adopted `esbuild ^0.25.1` in 5.4.1), which:
- removes `esbuild@0.21.5` and all its platform packages from the
lockfile with no forced ranges;
- drops the `@lingui/core` lockstep resolution (its comment marked it
droppable on the next coordinated lingui bump — the tree now resolves a
single `@lingui/core@5.9.5`);
- `@lingui/swc-plugin` stays at `^5.11.0` (peers on `@lingui/core: 5`;
its 6.x line targets lingui 6).
**lingui 5.9.5 behavioral fallout handled here:**
- Translation functions now **throw without an active locale** (5.1.2
fell back silently). The global `i18n` singleton that backs server-side
`` t`…` `` calls only had a messages compiler set, never an activated
locale → activate the source locale in `I18nService.loadTranslations()`,
mirrored in the server jest setup (unit tests bypass Nest bootstrap).
- `msg`/`t` placeholders are now strictly typed (reject
`null`/`undefined`/`unknown`) → one server call site and 16 twenty-front
files adapted with minimal nullish-coalescing fixes that preserve
rendering.
- `.po`/compiled-catalog churn from the new extractor/compiler
(reference reordering, sorted keys — verified content-identical on
unchanged `.po` inputs) is intentionally not committed: the scheduled
i18n workflows regenerate those.
## typeorm status (pulled out)
typeorm 0.3.20 → 0.3.26 was originally in this PR but **made workspace
metadata sync intermittently lossy**: `example-app-postcard` failed
twice with a *different* field missing from the synced PostCard object
each run, and one integration shard's `DataSeedWorkspaceCommand` died
with "Could not find flat entity with universal identifier …" — versus
zero such failures on recent main. Local runs (db reset + seed, group-by
integration suite 19/19) pass, so it is a nondeterministic
CI-load-sensitive regression that needs dedicated debugging (typeorm
changed LIMIT/OFFSET 0 semantics, lazy count for `getManyAndCount`,
upsert WHERE construction, and topological-sort internals in that
range). The resolutions comment documents this as the blocker;
CVE-2025-60542 is MySQL-driver-only (`sqlstring`), so Postgres-only
Twenty is not exposed in the meantime.
## Verification
- `npx nx typecheck twenty-server` / `twenty-front` — clean (no cache)
- `npx nx test twenty-server` — full suite green
- `lingui:extract` + `lingui:compile` — clean for twenty-server /
twenty-emails / twenty-front
- `oxfmt --check` — clean for both packages
- Lockfile diff: lingui 5.9.5 entries, `esbuild@0.21.5` +
`@esbuild/*@0.21.5` platform packages removed, no typeorm changes
|
||
|
|
462dd3b0e9 |
security: uuid CVE — bump bullmq/msal/blocknote + scoped resolutions for the rest (Dependabot alert 1289) (#21441)
Closes the uuid Dependabot alert — [1289](https://github.com/twentyhq/twenty/security/dependabot/1289) — by **upgrading the parents that bump cleanly** and **scope-resolving only the ones that genuinely can't**. `uuid < 11.1.1` (buffer-bounds check in v3/v5/v6) is pulled by ~9 transitives. ### Bumped (parent upgrade — drops uuid<11, no behavior change; typecheck verified) - **bullmq** 5.40.0 → 5.78.0 — also aligned **ioredis** 5.6.0 → 5.10.1 (bullmq pins it) and fixed the renamed `Job.returnValue→returnvalue` / `stackTrace→stacktrace` (now `string[]|null`) in `admin-panel-queue.service.ts`. - **@azure/msal-node** ^3.8.4 → ^5.2.3 (5.2.4 was age-gate-quarantined). - **@blocknote/** ×5 ^0.47.3 → ^0.51.4. ### Scope-resolved to uuid 11.1.1 (no clean bump exists) - **sockjs** (latest; pinned by webpack-dev-server) and **@ptc-org/nestjs-query-typeorm** (9.4.0 *is* latest, pins `^10`) — no version drops uuid. - **typeorm** — a `patch:` dep / ORM core, too risky to bump. - **node-ical** 0.26 (type-model overhaul → caldav-parser rewrite) and **googleapis** 173 (Gmail/OAuth, 105→173) — large breaking migrations; **deferred to dedicated PRs**. - **@cypress/request** — transitive (cypress isn't a direct dep). Resolutions are **per-package** and preserve the intentional **uuid 13.x** (twenty-sdk / create-twenty-app). ### Verification - `twenty-server` typecheck ✓ (0 errors), `twenty-front` typecheck ✓ (0 errors). - `yarn install --immutable` ✓; every uuid resolves to **11.1.1** or **13.0.2**. - bullmq/msal/typeorm runtime exercised by the **server integration tests**; @blocknote by the **storybook tests** in CI. |
||
|
|
868cb02e45 |
security: close lodash CVEs (#824/#823/#385) via parent upgrades, no resolution (#21414)
Closes the remaining lodash Dependabot alerts **without any `resolutions` override** — by upgrading the parent packages that pinned the vulnerable lodash. Every `lodash` in the tree now resolves to **4.18.1**. ### Closes - **#824 — `_.template` code injection (HIGH)** - #823 / #385 — prototype pollution in `_.unset` / `_.omit` ### What changed (4 parents pinned vulnerable lodash 4.17.x; all upgraded, no override) - **`@stoplight/spectral-functions`** → 1.10.2 (in-range; now uses `lodash ^4.18.1`) - **`zapier-platform-core`** 15.5.1 → 19.0.0 — aligns with the already-present `zapier-platform-cli ^19` (they were mismatched). v19 tightened the `Bundle` types, so 3 call sites now type their bundle as `Bundle<InputData>` and the test bundle includes the new `meta` fields. - **`@graphql-codegen`** → `cli 6.3.1`, `typescript 5.0.10`, `typescript-operations 5.1.0`, `typed-document-node 6.1.8`. These depend on `@graphql-codegen/plugin-helpers ^6.3.0`, the release that dropped lodash. (Stayed on the 6.x/5.x line on purpose — 7.x changes generated output far more.) ### About the generated-file changes — they are cosmetic, not real changes The codegen bump touches one generated file. **Verified there is zero semantic change:** - Only `src/generated-metadata/graphql.ts` changes. `src/generated/graphql.ts` (data) and `src/generated-admin/graphql.ts` (admin) are **byte-identical**. - Same 1,638 type declarations before and after — none added, none removed. - After stripping whitespace and union pipes, the file is **byte-for-byte identical** — no type, field, or union member changed. The entire diff is one formatting change from `typescript-operations@5.x`: multi-member union types are now printed multi-line with a leading `|` instead of on one line — which TypeScript treats identically: ```ts // before payload?: { …ObjectMetadata… } | { …Path… } | null // after payload?: | { …ObjectMetadata… } | { …Path… } | null ``` Only metadata is affected because only its operations select GraphQL union types. To keep generated types otherwise behavior-identical, `defaultScalarType: 'any'` was added to the three codegen configs (codegen 6 would otherwise default unmapped scalars to `unknown`). ### Verification - `twenty-front` typecheck ✓, `twenty-zapier` typecheck ✓ - `yarn install --immutable` ✓ (passes the hardened 3-day age gate) - CI green — including the `graphql:generate` freshness check, which regenerates against the canonical schema and confirms the committed output is exactly what codegen produces - No `lodash@4.17.x` remains anywhere in `yarn.lock` Supersedes #21411 (which closed these via a one-line resolution). |
||
|
|
232ca8eec2 |
security: clear happy-dom High alerts by upgrading wyw-in-js 0.7 → 1.1 (#21394)
## What Clears the 2 High `happy-dom` alerts (GHSA-w4gp-fjgq-3q4g, GHSA-6q6h-j7hj-3r64) via a parent bump — **no resolution**. `happy-dom@15.11.7` came from **`@wyw-in-js/transform@0.7.0`** (Linaria's CSS transform), pinned by a root resolution + a local `.yarn` patch and requested by `@wyw-in-js/vite@^0.7.0` in twenty-front + twenty-ui-deprecated. - `@wyw-in-js/vite` `^0.7.0` → `^1.1.0` (twenty-front, twenty-ui-deprecated) - `@wyw-in-js/babel-preset` `^0.6.0` → `^1.1.0` (twenty-ui-deprecated) - **drop the `@wyw-in-js/transform` 0.7.0 resolutions + the `.yarn` patch** — the patch added a `visited` cycle-guard to `TransformCacheCollection.invalidateIfChanged`, which is **already upstream** in transform 1.1.0, so it's obsolete. `@wyw-in-js/transform` now resolves to **1.1.0** (→ happy-dom 20.10.2) and 0.8.1 (website, unchanged, → happy-dom 20.8.9). The vulnerable 0.7.0/15.11.7 are gone. ## Required config change wyw-in-js 1.x resolves modules in its CSS pre-build via vite's `resolve.alias` instead of `vite-tsconfig-paths`. So twenty-front's `@/` and `~/` tsconfig path aliases are mirrored into `vite.config` `resolve.alias` — otherwise the CSS evaluator throws `Cannot find module '@/...'` for aliased imports used inside `styled` definitions. ## Verification - happy-dom now **20.8.9 + 20.10.2** (both patched); no 15.x left - `nx build twenty-front` — CSS extraction works (**1018 files transformed**) + `typecheck` - `nx build twenty-ui`, `twenty-ui-deprecated` (Linaria CSS extraction) - website's Linaria transform runs fine (local build only stops on a missing `TWENTY_PARTNERS_API_URL` env var, unrelated) - `yarn install --immutable` clean |
||
|
|
217e1f5ab3 |
security: clear immutable High alert via @graphql-codegen typescript plugins v4 (#21380)
## What Clears the High `immutable` alert (GHSA-wf6x-7x77-mvgw) via a parent bump — **no resolution**. `immutable@3.7.6` was pulled by `@ardatan/relay-compiler@12.0.0` (→ `immutable ~3.7.6`), reached through `@graphql-tools/relay-operation-optimizer` inside the `@graphql-codegen` visitor plugins. The fix lives in `relay-operation-optimizer@7.1.4` → `relay-compiler@13.0.1` → `immutable@^5.1.5` — but the old codegen typescript plugins (v3) pinned a 6.x optimizer stuck on relay-compiler 12. **Fix chain:** - `@graphql-codegen/typescript` `^3.0.4` → `^4.1.6` - `@graphql-codegen/typescript-operations` `^3.0.4` → `^4.6.1` - refresh `@graphql-tools/relay-operation-optimizer` (within its existing `^7.0.0` range) → 7.1.4 → `relay-compiler@13.0.1` → `immutable@5.1.6` ## Heads-up: this is effectively a codegen v4 plugin upgrade The codegen typescript plugins v4 change the generated **scalar shape** (`Scalars['X']` → `Scalars['X']['input'|'output']`), so the committed `generated*/graphql.ts` are regenerated (~7.8k lines). The diff is **purely type-level** — no runtime/enum/document changes — and was regenerated against the current schema (verified: **no schema-content drift**). ## Verification - `immutable@3.7.6` gone (now 5.1.6); `relay-compiler@13.0.1` - `nx typecheck twenty-front` passes against the regenerated types (0 errors) - `yarn install --immutable` clean - Generated files regenerated against a clean origin/main schema (no drift markers) |
||
|
|
ca63904ac5 |
fix(security): bump @scalar/api-reference-react to clear unhead XSS (#21382)
Resolves [Dependabot Alert 630](https://github.com/twentyhq/twenty/security/dependabot/630). unhead@1.11.20 was pulled in transitively via @scalar/api-reference-react@0.4.42 (@unhead/vue@^1.11.11). The useHeadSafe XSS bypass (GHSA, alert https://github.com/twentyhq/twenty/issues/630) is only patched on the unhead 2.x line; the 1.x branch was never fixed and 1.11.20 is the latest 1.x release, so the existing semver range could not reach a patched version. Rather than a resolutions override, bump the direct dependency to a Scalar release that depends on @unhead/vue@^2.x, which resolves unhead to 2.1.15. - Upgrade @scalar/api-reference-react ^0.4.36 -> ^0.9.42 (0.9.43+ blocked by the 3-day npmMinimalAgeGate; the caret adopts them once aged). - Migrate RestPlayground configuration to the new Scalar API: - spec.content -> top-level content - authentication.http.bearer -> authentication.securitySchemes.bearerAuth (with preferredSecurityScheme), matching the server's OpenAPI scheme name. - Drop the ?inline query on the style.css import. It was added in https://github.com/twentyhq/twenty/pull/12099 to stop the old Scalar's global CSS reset from leaking; the new CSS scopes every reset to :where(.scalar-app), so importing it normally restores styling without re-introducing that leak. Proof: <img width="215" height="48" alt="image" src="https://github.com/user-attachments/assets/3a738fae-63bd-4e88-82c3-5dbe72d993ec" /> Co-authored-by: Félix Malfait <felix.malfait@gmail.com> |
||
|
|
0d8d463a44 |
security: clear all High minimatch Dependabot alerts via parent bumps (#21373)
## What Clears **all 14 High `minimatch` ReDoS alerts** (GHSA-7r86-cg39-jmmj, GHSA-23c5-xmqv-rm74, GHSA-3ppc-4f35-3m26) in the root tree — **by bumping the actual parent dev tools, with no `resolutions`/overrides**. Each parent that pinned a vulnerable minimatch is upgraded so the patched version resolves naturally. | Vulnerable minimatch | Pinned by | Fix | |---|---|---| | 10.0.3 | `@microsoft/api-extractor` 7.55.1 | → 7.58.7 (in-range refresh) → minimatch 10.2.3 | | 3.1.2 | `@stoplight/spectral-core` 1.20.0 | → 1.23.0 (in-range refresh) → minimatch ^3.1.4 | | 3.0.8 | `vite-plugin-dts` 3.8.1 → api-extractor 7.43.0 | bump to `^4.5.4` (already used elsewhere here) → minimatch 10.2.3 | | 4.2.3 | `graphql-config` 4.5.0 via `@graphql-codegen/cli` ^3.3.1 | bump cli to `^5.0.7` → graphql-config 5.1.6 → minimatch ^10 | | 9.0.3 | `zapier-platform-cli` ^15.4.1 | bump to `^19.0.0` | | 7.4.6 | `verdaccio` 6.5.2 → `@verdaccio/core` 8.0.0-next | refresh to 6.7.2 → core 8.1.1 → minimatch 7.4.9 | All six are **build/test tooling** — the ReDoS exposure is build-time, never shipped to users. ## Verification - ✅ Every resolved `minimatch` in `yarn.lock` is now ≥ its patched floor (3.1.5 / 7.4.9 / 9.0.9 / 10.2.3+). No `resolutions` added. - ✅ `nx build`: twenty-shared, twenty-ui, twenty-ui-deprecated, twenty-emails (validates vite-plugin-dts v4) - ✅ twenty-zapier: typecheck + build + `zapier validate` (35/35 checks pass; cli 19 + core 15.5.1) - ✅ twenty-front: typecheck; `graphql:generate` with codegen cli 5 produces **byte-identical** output (no generated-file changes in this PR) - ✅ `yarn install --immutable` clean ## Notes - The large `yarn.lock` diff is expected: major bumps to codegen (3→5), zapier-cli (15→19), and vite-plugin-dts (3→4) cascade through dev-tree transitives (net −1244 lines after dedup). - `zapier-platform-core` (runtime) intentionally left at 15.5.1 — only the CLI (dev tool) carried the vulnerable minimatch; `zapier validate` flags only a non-blocking "consider upgrading core" suggestion. - codegen plugins (`typescript`/`typescript-operations`) left at v3: they run fine under cli 5 and produce identical output, so the minimal change is just the cli bump. |
||
|
|
c596a5e342 |
Rename twenty-ui to twenty-ui-deprecated and twenty-new-ui to twenty-ui to prepare package release (#21315)
## Description Promotes the next-gen UI library (formerly `twenty-new-ui`) to the name **`twenty-ui`** (v0.1.0, publishable) and renames the old package to **`twenty-ui-deprecated`**. Rewrites ~1,730 `twenty-ui` imports → `twenty-ui-deprecated`, updates all configs/CI/Docker/deps, and migrates twenty-front's `Toggle` to the new package (first consumer) as a drop-in. ## Next steps - Wire the `ui/v*` publish dispatch (`cd-deploy-tag.yaml` + `.yarnrc.yml`), then tag `ui/v0.1.0` to publish. - Continue migrating components from `twenty-ui-deprecated` → `twenty-ui`. |
||
|
|
13e8e26d1c |
security: bump uuid 9 → 11 (server, shared, front) (#21326)
Clears the `uuid` "missing buffer bounds check in v3/v5/v6" advisory — patched in **11.1.1**. Bumps `twenty-server`, `twenty-shared`, `twenty-front` from 9 → `^11.1.1`. ### Why 11 and not 13 uuid **11.1.x still ships a CommonJS build**, so jest loads it with **no config changes**. uuid went **ESM-only at v12+**, which would otherwise force `transformIgnorePatterns` workarounds across the jest projects (and broke server/integration/storybook CI on the earlier 13 attempt). 11.1.1 is the actual patched version, so this is the minimal fix. ### Changes - `uuid` → `^11.1.1` in the three workspaces (lockfile regenerated under hardened mode) - one test (`useCreateManyRecords.test.tsx`): pin the mocked `v4` to its string-returning overload — uuid's types declare a `Uint8Array` overload that `jest.mocked` resolves to (present in v11 too, unrelated to ESM). All usages are named imports, so no source migration. typecheck passes (server/shared/front); affected specs pass. **No jest config changes.** |
||
|
|
d2e7dc0e74 |
security: bump vulnerable direct dependencies (axios, next, vitest, qs, dompurify, …) (#21309)
## What Within-major version bumps of **direct** dependencies to clear a large batch of Dependabot alerts that are breaching (or near) their SLA. No major-version changes — all stay within the current major, so risk is low. | Package | From → To | Clears | |---|---|---| | `axios` | ^1.13.5 → ^1.16.0 | ReDoS, Proxy-Auth leak, proto-pollution gadgets, NO_PROXY bypass, resource DoS (56 alerts) | | `next` | 16.1.7 → ^16.2.6 | DoS, middleware/proxy bypass, SSRF, cache poisoning, XSS (32 alerts) | | `vitest` | 4.0.18 → ^4.1.0 | **CRITICAL** — UI server arbitrary file read/exec (#1421) | | `qs` | ^6.11.2 → ^6.15.2 | `qs.stringify` DoS | | `dompurify` | 3.3.3 → ^3.4.0 | proto-pollution XSS + FORBID_TAGS / SAFE_FOR_TEMPLATES bypasses | | `@nestjs/core` | 11.1.16 → ^11.1.18 | improper output neutralization / injection | | `nodemailer` | 8.0.4 → 8.0.10 | SMTP command injection via CRLF (bumped via root `resolutions`) | | `path-to-regexp` | ^8.2.0 → ^8.4.0 | ReDoS via multiple wildcards | | `file-type` | ^21.3.1 → ^21.3.2 | ZIP decompression-bomb DoS | | `@opentelemetry/exporter-prometheus` | ^0.211.0 → ^0.217.0 | exporter process crash via malformed HTTP request (#1183/#1184) | ## Notes - Added a `next` root **resolution** so the dev-only `@react-email/preview-server` copy (hard-pinned at `16.0.10`) is also pulled up to the patched `16.2.x` line — otherwise that copy keeps the Next.js alerts open. - `@opentelemetry/exporter-prometheus` 0.217 pulled `@opentelemetry/sdk-metrics` to 2.7.1 (compatible); `@opentelemetry/api` stays pinned at 1.9.1. - **Transitive-only** vulnerable packages (undici, tmp, ws, brace-expansion, …) are handled in a **separate PR** per the split-by-group plan. - Breaking major bumps (electron, uuid, serialize-javascript) and migrations (Apollo Server 3→4, simplemde) are intentionally **out of scope** here. |
||
|
|
f4da7767f8 |
chore: remove Chromatic dependencies and configuration (#21221)
## Summary
- Remove `chromatic` and `@chromatic-com/storybook` devDependencies from
twenty-front
- Remove global `chromatic` Nx target from nx.json and twenty-front
project.json override
- Remove commented Chromatic Storybook addon from twenty-front
- Remove `CHROMATIC_PROJECT_TOKEN` from .env.example
- Update README to remove Chromatic sponsor reference (image was already
missing)
- Update stale Chromatic comment in toSpliced.ts
## Context
Visual regression testing has moved from Chromatic SaaS to self-hosted
Argos at `argos.twenty-internal.com`. These are dead references that are
no longer used by any CI workflow.
**Note:** Story `parameters.chromatic: { disableSnapshot: true }`
entries are intentionally kept — the Argos plugin reads them as a
fallback.
## Test plan
- Verify `yarn install` succeeds after dependency removal
- Verify no workflow references `chromatic` or `nx chromatic`
|
||
|
|
6ad6fcce0f |
Bump playwright (#21113)
Playwright installation is infinite looping in the ci seems like to be a global outage |
||
|
|
41ad63a8ab |
[DockerFile] Optimize twenty-server deps and build (#20132)
# Introduction Aiming for faster cd process ## Splitting front end server deps Reduce dependencies bloating when target is server only, installing only root repo dev deps and server dev and prod deps Still pruning before copying to prod node_modules ## Server only remove twenty-ui Also removing twenty-ui from server build as it was not consumed at all Depends on https://github.com/twentyhq/twenty/pull/20140 |
||
|
|
3c7c62c79f |
fix(server): deduplicate @opentelemetry/api to fix NoopMeterProvider (#20231)
## Summary **All OTel metrics in twenty-server have been silently dropped since April 30.** ### Root cause PR #20149 (`bump @sentry/profiling-node 10.27→10.51`) pulled in `@sentry/node@10.51.0`, which declares `@opentelemetry/api: ^1.9.1` as a **dependency** (not peer). Yarn installed it as a **nested** copy at `1.9.1`, while the hoisted copy stayed at `1.9.0`. At startup in `instrument.ts`: 1. `Sentry.init()` uses the **nested `1.9.1`** to register `trace`, `propagation`, `context` on the OTel global → global version becomes **`1.9.1`** 2. `setGlobalMeterProvider()` uses the **hoisted `1.9.0`** → `registerGlobal` sees version mismatch (`1.9.1` ≠ `1.9.0`) → **silently returns `false`** 3. Global stays `NoopMeterProvider` → every counter, gauge, and histogram in the server is a no-op ### What this PR does 1. **Reverts three troubleshooting PRs** that are no longer needed now that the root cause is identified: - #20230 — heartbeat gauge - #20228 — OTLP export lifecycle logs - #20221 — Sentry revert to 10.27 (which never actually downgraded in `yarn.lock` since `^10.27.0` resolved to `10.51.0`) 2. **Fixes the root cause**: - Root Yarn resolution pinning `@opentelemetry/api` to `1.9.1` → single copy in the entire tree, Sentry and Twenty share the same instance - Named import in `instrument.ts` (`import { metrics as otelMetrics }` instead of default import) as defense-in-depth against CJS interop issues ### Verified on dev cluster Exec'd into the running pod and confirmed: - `@sentry/node` nests `@opentelemetry/api@1.9.1`, hoisted is `1.9.0` - `Sentry.init()` → global version `1.9.1` → `setGlobalMeterProvider` with VERSION `1.9.0` → returns `false` → `NoopMeterProvider` - Same-version registration returns `true` → `MeterProvider` ✓ ## Test plan - [ ] CI passes (lint, typecheck, build) - [ ] Deploy to dev cluster and verify metrics flow to collector - [ ] Confirm `node_modules/@opentelemetry/api/package.json` shows `1.9.1` with no nested copy under `@sentry/` --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
ff22988caf |
revert: Sentry #20064 + @sentry 10.27 (prod bisect) (#20221)
## Summary Reverts **#20064** (`feat(sentry): propagate workspace context to all spans`) and downgrades **@sentry** packages from **10.51** back to **10.27** (reversing **#20149**), to validate in production whether recent Sentry/instrumentation changes correlate with OTLP/metrics issues. ## Changes 1. **Revert #20064** — removes `beforeSendSpan` from `instrument.ts`, restores `WorkspaceAuthContextMiddleware` / `BullMQDriver` behavior, and deletes the three `apply-workspace-sentry-*` utils added in that PR. 2. **Sentry versions** — `packages/twenty-server` (`@sentry/nestjs`, `@sentry/node`, `@sentry/profiling-node`) and `packages/twenty-front` (`@sentry/react`) set to `^10.27.0`; `yarn.lock` regenerated via `yarn install`. --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
8a0225e974 |
Dispatch root package.json hoisted deps and devDeps (#20140)
# Introduction Dispatching root package.json devDeps, prod deps Taking care of keeping non imported module used at build/ci level in the root package.json ## Motivation Avoid redundant deps declaration, better scoping allow better workspace deps granularity installation. <img width="385" height="247" alt="image" src="https://github.com/user-attachments/assets/9d7162ec-ba01-4f58-8563-38333733fdf0" /> --------- Co-authored-by: Charles Bochet <charles@twenty.com> |
||
|
|
83db37d33f |
chore(deps): bump @sentry/profiling-node from 10.27.0 to 10.51.0 (#20149)
Bumps [@sentry/profiling-node](https://github.com/getsentry/sentry-javascript) from 10.27.0 to 10.51.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/getsentry/sentry-javascript/releases"><code>@sentry/profiling-node</code>'s releases</a>.</em></p> <blockquote> <h2>10.51.0</h2> <h3>Important Changes</h3> <ul> <li> <p><strong>feat(cloudflare): Add trace propagation for RPC method calls (<a href="https://redirect.github.com/getsentry/sentry-javascript/pull/20343">#20343</a>)</strong></p> <p>Trace context is now propagated across Cloudflare Workers RPC calls, connecting traces between Workers and Durable Objects. This feature is opt-in and requires setting <code>enableRpcTracePropagation: true</code> in your SDK configuration:</p> <pre lang="ts"><code>// Worker export default Sentry.withSentry( env => ({ dsn: env.SENTRY_DSN, enableRpcTracePropagation: true, }), handler, ); <p>// Durable Object<br /> export const MyDurableObject = Sentry.instrumentDurableObjectWithSentry(<br /> env => ({<br /> dsn: env.SENTRY_DSN,<br /> enableRpcTracePropagation: true,<br /> }),<br /> MyDurableObjectBase,<br /> );<br /> </code></pre></p> </li> <li> <p><strong>feat(hono)!: Change setup for <code>@sentry/hono/node</code> (<code>init</code> in external file) (<a href="https://redirect.github.com/getsentry/sentry-javascript/pull/20497">#20497</a>)</strong></p> <p>To improve Node.js instrumentation, the <code>sentry()</code> middleware exported from <code>@sentry/hono/node</code> no longer accepts configuration options. Instead, you must configure the SDK by calling <code>Sentry.init()</code> in a dedicated instrumentation file that runs before your application code (read more in the <a href="https://github.com/getsentry/sentry-javascript/blob/develop/packages/hono/README.md">Hono SDK readme</a>:</p> <pre lang="ts"><code>// instrument.mjs (or instrument.ts) import * as Sentry from '@sentry/hono/node'; <p>Sentry.init({<br /> dsn: '<strong>DSN</strong>',<br /> tracesSampleRate: 1.0,<br /> });<br /> </code></pre></p> </li> <li> <p><strong>feat(nitro): Add <code>@sentry/nitro</code> SDK (<a href="https://redirect.github.com/getsentry/sentry-javascript/pull/19224">#19224</a>)</strong></p> <p>A new <code>@sentry/nitro</code> package provides first-class Sentry support for <a href="https://nitro.build/">Nitro</a> applications, with HTTP handler and error instrumentation, middleware tracing, request isolation, and build-time source map uploading via <code>withSentryConfig</code>. Read more in the <a href="https://docs.sentry.io/platforms/javascript/guides/nitro/">Nitro SDK docs</a> and the <a href="https://github.com/getsentry/sentry-javascript/blob/develop/packages/nitro/README.md">Nitro SDK readme</a>.</p> </li> </ul> <h3>Other Changes</h3> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md"><code>@sentry/profiling-node</code>'s changelog</a>.</em></p> <blockquote> <h2>10.51.0</h2> <h3>Important Changes</h3> <ul> <li> <p><strong>feat(cloudflare): Add trace propagation for RPC method calls (<a href="https://redirect.github.com/getsentry/sentry-javascript/pull/20343">#20343</a>)</strong></p> <p>Trace context is now propagated across Cloudflare Workers RPC calls, connecting traces between Workers and Durable Objects. This feature is opt-in and requires setting <code>enableRpcTracePropagation: true</code> in your SDK configuration:</p> <pre lang="ts"><code>// Worker export default Sentry.withSentry( env => ({ dsn: env.SENTRY_DSN, enableRpcTracePropagation: true, }), handler, ); <p>// Durable Object<br /> export const MyDurableObject = Sentry.instrumentDurableObjectWithSentry(<br /> env => ({<br /> dsn: env.SENTRY_DSN,<br /> enableRpcTracePropagation: true,<br /> }),<br /> MyDurableObjectBase,<br /> );<br /> </code></pre></p> </li> <li> <p><strong>feat(hono)!: Change setup for <code>@sentry/hono/node</code> (<code>init</code> in external file) (<a href="https://redirect.github.com/getsentry/sentry-javascript/pull/20497">#20497</a>)</strong></p> <p>To improve Node.js instrumentation, the <code>sentry()</code> middleware exported from <code>@sentry/hono/node</code> no longer accepts configuration options. Instead, you must configure the SDK by calling <code>Sentry.init()</code> in a dedicated instrumentation file that runs before your application code (read more in the <a href="https://github.com/getsentry/sentry-javascript/blob/develop/packages/hono/README.md">Hono SDK readme</a>:</p> <pre lang="ts"><code>// instrument.mjs (or instrument.ts) import * as Sentry from '@sentry/hono/node'; <p>Sentry.init({<br /> dsn: '<strong>DSN</strong>',<br /> tracesSampleRate: 1.0,<br /> });<br /> </code></pre></p> </li> <li> <p><strong>feat(nitro): Add <code>@sentry/nitro</code> SDK (<a href="https://redirect.github.com/getsentry/sentry-javascript/pull/19224">#19224</a>)</strong></p> <p>A new <code>@sentry/nitro</code> package provides first-class Sentry support for <a href="https://nitro.build/">Nitro</a> applications, with HTTP handler and error instrumentation, middleware tracing, request isolation, and build-time source map uploading via <code>withSentryConfig</code>. Read more in the <a href="https://docs.sentry.io/platforms/javascript/guides/nitro/">Nitro SDK docs</a> and the <a href="https://github.com/getsentry/sentry-javascript/blob/develop/packages/nitro/README.md">Nitro SDK readme</a>.</p> </li> </ul> <h3>Other Changes</h3> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/getsentry/sentry-javascript/commit/dc0b839ff4896cf90a02f5c1a6de54a31302dcf3"><code>dc0b839</code></a> release: 10.51.0</li> <li><a href="https://github.com/getsentry/sentry-javascript/commit/b3cabee9a9348b9e67332262d44d3d1900424199"><code>b3cabee</code></a> Merge pull request <a href="https://redirect.github.com/getsentry/sentry-javascript/issues/20599">#20599</a> from getsentry/prepare-release/10.51.0</li> <li><a href="https://github.com/getsentry/sentry-javascript/commit/3be99a9afa77e49578e6839e4b32f97fb04fb0f8"><code>3be99a9</code></a> meta(changelog): Update changelog for 10.51.0</li> <li><a href="https://github.com/getsentry/sentry-javascript/commit/bea1aad42277db894d5a299bfec3cdd633d6baf0"><code>bea1aad</code></a> test(browser): Unflake some more tests (<a href="https://redirect.github.com/getsentry/sentry-javascript/issues/20591">#20591</a>)</li> <li><a href="https://github.com/getsentry/sentry-javascript/commit/50aa0859b3a188d34d0317dab3ad57f2140f02fe"><code>50aa085</code></a> test(node): Unflake postgres tests (<a href="https://redirect.github.com/getsentry/sentry-javascript/issues/20593">#20593</a>)</li> <li><a href="https://github.com/getsentry/sentry-javascript/commit/1166839112c4766f210124dc0486ebbfd6db104b"><code>1166839</code></a> fix(hono): Distinguish <code>.use()</code> middleware in sub-apps from <code>.all()</code> handlers...</li> <li><a href="https://github.com/getsentry/sentry-javascript/commit/217ad4a69554281806eccbfeac1b27c4f43f6ffa"><code>217ad4a</code></a> test(node): Fix flaky ANR test (<a href="https://redirect.github.com/getsentry/sentry-javascript/issues/20592">#20592</a>)</li> <li><a href="https://github.com/getsentry/sentry-javascript/commit/91ffb3fac90835ab160f8152527a54a5d64f3250"><code>91ffb3f</code></a> test(node): Fix flaky worker thread integration test (<a href="https://redirect.github.com/getsentry/sentry-javascript/issues/20588">#20588</a>)</li> <li><a href="https://github.com/getsentry/sentry-javascript/commit/c4e3902c9297147158e730f017aba96e83ef619e"><code>c4e3902</code></a> chore(ci): Do not report flaky test issues if we cannot find a test name (<a href="https://redirect.github.com/getsentry/sentry-javascript/issues/20">#20</a>...</li> <li><a href="https://github.com/getsentry/sentry-javascript/commit/c0005cd387f3a7ea6fbb2e85041562c7f32e0484"><code>c0005cd</code></a> test(node): Update timeout for cron integration tests (<a href="https://redirect.github.com/getsentry/sentry-javascript/issues/20586">#20586</a>)</li> <li>Additional commits viewable in <a href="https://github.com/getsentry/sentry-javascript/compare/10.27.0...10.51.0">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Charles Bochet <charles@twenty.com> |
||
|
|
89ad87aa64 |
Make twenty-front build env agnostic (#20055)
## Introduction
In aim to reduce and optimize the number of twenty-front build we do
during our cd process and allow twenty-front build promotion
### Build time
**Nothing is baked.** The `build/` directory is a clean, env-agnostic
artifact. `index.html` contains the empty placeholder:
```html
<script id="twenty-env-config">
window._env_ = {
// This will be overwritten
};
</script>
```
The JS bundles contain no hardcoded server URL.
---
### Deploy mode 1: Frontend served by the backend (Docker / NestJS)
1. Container starts, NestJS boots in `main.ts`
2. `generateFrontConfig()` runs, reads `process.env.SERVER_URL`
3. Rewrites `dist/front/index.html`, replacing the placeholder with:
```html
<script id="twenty-env-config">
window._env_ = {
REACT_APP_SERVER_BASE_URL: "https://api.example.com"
};
</script>
```
4. NestJS serves the static `dist/front/` directory
5. Browser loads `index.html`, `window._env_` is set before the app JS
executes
6. `src/config/index.ts` reads `window._env_.REACT_APP_SERVER_BASE_URL`
and uses it
---
### Deploy mode 2: Frontend served standalone (CDN / nginx / static
server)
1. Take the `build/` artifact as-is
2. Before serving, run at deploy time:
```bash
REACT_APP_SERVER_BASE_URL=https://api.example.com sh
./scripts/inject-runtime-env.sh
```
3. This does the same `sed` replacement on `build/index.html`
4. Serve the `build/` directory with your static server of choice
5. Same resolution in the browser:
`window._env_.REACT_APP_SERVER_BASE_URL` is picked up by
`src/config/index.ts`
---
### Fallback: no injection at all
If neither mechanism runs (e.g. local dev with `vite dev`),
`window._env_.REACT_APP_SERVER_BASE_URL` is `undefined`, and
`getDefaultUrl()` kicks in:
- **Localhost**: returns `http://localhost:3000`
- **Non-localhost**: returns same-origin (`window.location.origin`)
|
||
|
|
80e8f6d516 |
chore(deps): bump @blocknote/server-util from 0.47.1 to 0.47.3 (#19997)
Bumps [@blocknote/server-util](https://github.com/TypeCellOS/BlockNote/tree/HEAD/packages/server-util) from 0.47.1 to 0.47.3. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/TypeCellOS/BlockNote/releases"><code>@blocknote/server-util</code>'s releases</a>.</em></p> <blockquote> <h2>v0.47.3</h2> <h2>0.47.3 (2026-03-25)</h2> <h3>🩹 Fixes</h3> <ul> <li><strong>core:</strong> preserve whitespace edge cases but collapse html formatting newlines (BLO-1065) (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2551">#2551</a>, <a href="https://redirect.github.com/TypeCellOS/BlockNote/issues/2230">#2230</a>)</li> </ul> <h3>❤️ Thank You</h3> <ul> <li>Yousef</li> </ul> <h2>v0.47.2</h2> <h2>0.47.2 (2026-03-20)</h2> <h3>🩹 Fixes</h3> <ul> <li>use <code><details></code> & <code><summary></code> for toggle block HTML export (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2524">#2524</a>)</li> <li>remove <code>@hocuspocus/provider</code> peer dependency by inlining tiptap comment types BLO-1064 (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2564">#2564</a>)</li> <li><strong>core:</strong> slash menu fails in custom blocks after space BLO-1036 (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2553">#2553</a>)</li> <li><strong>i18n:</strong> fix typo in russian translation (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2560">#2560</a>)</li> </ul> <h3>❤️ Thank You</h3> <ul> <li>Drone</li> <li>Yousef</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/TypeCellOS/BlockNote/blob/main/CHANGELOG.md"><code>@blocknote/server-util</code>'s changelog</a>.</em></p> <blockquote> <h2>0.47.3 (2026-03-25)</h2> <h3>🩹 Fixes</h3> <ul> <li><strong>core:</strong> preserve whitespace edge cases but collapse html formatting newlines (BLO-1065) (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2551">#2551</a>, <a href="https://redirect.github.com/TypeCellOS/BlockNote/issues/2230">#2230</a>)</li> </ul> <h3>❤️ Thank You</h3> <ul> <li>Yousef</li> </ul> <h2>0.47.2 (2026-03-20)</h2> <h3>🩹 Fixes</h3> <ul> <li>use <!-- raw HTML omitted -->/<!-- raw HTML omitted --> for toggle block HTML export (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2524">#2524</a>)</li> <li>remove <code>@hocuspocus/provider</code> peer dependency by inlining tiptap comment types BLO-1064 (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2564">#2564</a>)</li> <li><strong>core:</strong> slash menu fails in custom blocks after space BLO-1036 (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2553">#2553</a>)</li> <li><strong>i18n:</strong> fix typo in russian translation (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2560">#2560</a>)</li> </ul> <h3>❤️ Thank You</h3> <ul> <li>Claude Opus 4.6</li> <li>Drone</li> <li>Yousef</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/TypeCellOS/BlockNote/commit/cd92dc21be49397b658fef4e308e01ce8f5c04ad"><code>cd92dc2</code></a> chore(release): publish 0.47.3</li> <li><a href="https://github.com/TypeCellOS/BlockNote/commit/b63b4096daa575f821980ef897fd90f4c76d9e42"><code>b63b409</code></a> chore(release): publish 0.47.2</li> <li><a href="https://github.com/TypeCellOS/BlockNote/commit/d76fd68e016da698f3896f9d349a935a26a52d5f"><code>d76fd68</code></a> test: get snapshots working again (<a href="https://github.com/TypeCellOS/BlockNote/tree/HEAD/packages/server-util/issues/2554">#2554</a>)</li> <li>See full diff in <a href="https://github.com/TypeCellOS/BlockNote/commits/v0.47.3/packages/server-util">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Abdullah <125115953+mabdullahabaid@users.noreply.github.com> |
||
|
|
fd7387928c |
feat: queue messages + replace AI SDK with GraphQL SSE subscription (#19203)
## Summary - **Queue messages while streaming**: Messages sent during active AI streaming are queued server-side and auto-flushed when the current stream completes. Frontend renders queued messages optimistically in a dedicated queue UI. - **Drop `@ai-sdk/react` + `resumable-stream`**: Replace the dual HTTP SSE + AI SDK client architecture with a single GraphQL SSE subscription per thread. All events (token chunks, message persistence, queue updates, errors) flow through Redis PubSub → GraphQL subscription. - **Server-driven architecture**: The server decides whether to queue or stream (via `POST /:threadId/message`). The frontend mirrors this decision for optimistic rendering but defers to the server response. - **Reuse AI SDK accumulation logic**: `readUIMessageStream` from the `ai` package handles chunk-to-message accumulation on the frontend, avoiding a custom 780-line accumulator. ## Key files **Backend:** - `agent-chat-event-publisher.service.ts` — publishes events to Redis PubSub - `agent-chat-subscription.resolver.ts` — GraphQL subscription resolver - `stream-agent-chat.job.ts` — publishes chunks via PubSub instead of resumable-stream - `agent-chat.controller.ts` — unified `POST /:threadId/message` endpoint **Frontend:** - `useAgentChatSubscription.ts` — subscribes to `onAgentChatEvent`, bridges to `readUIMessageStream` - `useAgentChat.ts` — send/stop/optimistic rendering (no more AI SDK) - `AgentChatStreamSubscriptionEffect.tsx` — replaces `AgentChatAiSdkStreamEffect.tsx` ## Test plan - [ ] Send message on new thread → optimistic render, streaming response appears - [ ] Send message while streaming → queued instantly (no flash in main thread) - [ ] Queued message auto-flushes after current stream completes - [ ] Remove queued message via queue UI - [ ] Stop streaming mid-response - [ ] Leave chat idle for several minutes → streaming still works after (SSE client recycling) - [ ] Token refresh during session → requests succeed (authenticated fetch) - [ ] Switch threads while streaming → clean subscription handoff Made with [Cursor](https://cursor.com) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
37908114fc |
[SDK] Extract twenty-front-component-renderer outside of twenty-sdk ( 2.8MB ) (#19021)
Followup https://github.com/twentyhq/twenty/pull/19010 ## Dependency diagram ``` ┌─────────────────────┐ │ twenty-front │ │ (React frontend) │ └─────────┬───────────┘ │ imports runtime: │ FrontComponentRenderer │ FrontComponentRendererWithSdkClient │ useFrontComponentExecutionContext ▼ ┌──────────────────────────────────┐ ┌─────────────────────────┐ │ twenty-front-component-renderer │────────▶│ twenty-sdk │ │ (remote-dom host + worker) │ │ (app developer SDK) │ │ │ │ │ │ imports from twenty-sdk: │ │ Public API: │ │ • types only: │ │ defineFrontComponent │ │ FrontComponentExecutionContext│ │ navigate, closeSide… │ │ NavigateFunction │ │ useFrontComponent… │ │ CloseSidePanelFunction │ │ Command components │ │ CommandConfirmation… │ │ conditional avail. │ │ OpenCommandConfirmation… │ │ │ │ EnqueueSnackbarFunction │ │ Internal only: │ │ etc. │ │ frontComponentHost… │ │ │ │ front-component-build │ │ owns locally: │ │ esbuild plugins │ │ • ALLOWED_HTML_ELEMENTS │ │ │ │ • EVENT_TO_REACT │ └────────────┬────────────┘ │ • HTML_TAG_TO_CUSTOM_ELEMENT… │ │ │ • SerializedEventData │ │ types │ • PropertySchema │ ▼ │ • frontComponentHostComm… │ ┌─────────────────────────┐ │ (local ref to globalThis) │ │ twenty-shared │ │ • setFrontComponentExecution… │ │ (common types/utils) │ │ (local impl, same keys) │ │ AppPath, SidePanelP… │ │ │ │ EnqueueSnackbarParams │ └──────────────────────────────────┘ │ isDefined, … │ │ └─────────────────────────┘ │ also depends on ▼ twenty-shared (types) @remote-dom/* (runtime) @quilted/threads (runtime) react (runtime) ``` **Key points:** - **`twenty-front`** depends on the renderer, **not** on `twenty-sdk` directly (for rendering) - **`twenty-front-component-renderer`** depends on `twenty-sdk` for **types only** (function signatures, `FrontComponentExecutionContext`). The runtime bridge (`frontComponentHostCommunicationApi`) is shared via `globalThis` keys, not module imports - **`twenty-sdk`** has no dependency on the renderer — clean one-way dependency - The renderer owns all remote-dom infrastructure (element schemas, event mappings, custom element tags) that was previously leaking through the SDK's public API - The SDK's `./build` entry point was removed entirely (unused) |
||
|
|
b470cb21a1 |
Upgrade Apollo Client to v4 and refactor error handling (#18584)
## Summary This PR upgrades Apollo Client from v3.10.0 to v4 and refactors error handling patterns across the codebase to use a new centralized `useSnackBarOnQueryError` hook. ## Key Changes - **Dependency Update**: Upgraded `@apollo/client` from `^3.10.0` to `^3.11.0` in root package.json - **New Hook**: Added `useSnackBarOnQueryError` hook for centralized Apollo query error handling with snack bar notifications - **Error Handling Refactor**: Updated 100+ files to use the new error handling pattern: - Removed direct `ApolloError` imports where no longer needed - Replaced manual error handling logic with `useSnackBarOnQueryError` hook - Simplified error handling in hooks and components across multiple modules - **GraphQL Codegen**: Updated codegen configuration files to work with Apollo Client v3.11.0 - **Type Definitions**: Added TypeScript declaration file for `apollo-upload-client` module - **Test Updates**: Updated test files to reflect new error handling patterns ## Notable Implementation Details - The new `useSnackBarOnQueryError` hook provides a consistent way to handle Apollo query errors with automatic snack bar notifications - Changes span across multiple feature areas: auth, object records, settings, workflows, billing, and more - All changes maintain backward compatibility while improving code maintainability and reducing duplication - Jest configuration updated to work with the new Apollo Client version https://claude.ai/code/session_019WGZ6Rd7sEHuBg9sTrXRqJ --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
5b28e59ca7 | Navbar drag drop using dnd kit (#18288) | ||
|
|
9d57bc39e5 |
Migrate from ESLint to OxLint (#18443)
## Summary Fully replaces ESLint with OxLint across the entire monorepo: - **Replaced all ESLint configs** (`eslint.config.mjs`) with OxLint configs (`.oxlintrc.json`) for every package: `twenty-front`, `twenty-server`, `twenty-emails`, `twenty-ui`, `twenty-shared`, `twenty-sdk`, `twenty-zapier`, `twenty-docs`, `twenty-website`, `twenty-apps/*`, `create-twenty-app` - **Migrated custom lint rules** from ESLint plugin format to OxLint JS plugin system (`@oxlint/plugins`), including `styled-components-prefixed-with-styled`, `no-hardcoded-colors`, `sort-css-properties-alphabetically`, `graphql-resolvers-should-be-guarded`, `rest-api-methods-should-be-guarded`, `max-consts-per-file`, and Jotai-related rules - **Migrated custom rule tests** from ESLint `RuleTester` + Jest to `oxlint/plugins-dev` `RuleTester` + Vitest - **Removed all ESLint dependencies** from `package.json` files and regenerated lockfiles - **Updated Nx targets** (`lint`, `lint:diff-with-main`, `fmt`) in `nx.json` and per-project `project.json` to use `oxlint` commands with proper `dependsOn` for plugin builds - **Updated CI workflows** (`.github/workflows/ci-*.yaml`) — no more ESLint executor - **Updated IDE setup**: replaced `dbaeumer.vscode-eslint` with `oxc.oxc-vscode` extension, configured `source.fixAll.oxc` and format-on-save with Prettier - **Replaced all `eslint-disable` comments** with `oxlint-disable` equivalents across the codebase - **Updated docs** (`twenty-docs`) to reference OxLint instead of ESLint - **Renamed** `twenty-eslint-rules` package to `twenty-oxlint-rules` ### Temporarily disabled rules (tracked in `OXLINT_MIGRATION_TODO.md`) | Rule | Package | Violations | Auto-fixable | |------|---------|-----------|-------------| | `twenty/sort-css-properties-alphabetically` | twenty-front | 578 | Yes | | `typescript/consistent-type-imports` | twenty-server | 3814 | Yes | | `twenty/max-consts-per-file` | twenty-server | 94 | No | ### Dropped plugins (no OxLint equivalent) `eslint-plugin-project-structure`, `lingui/*`, `@stylistic/*`, `import/order`, `prefer-arrow/prefer-arrow-functions`, `eslint-plugin-mdx`, `@next/eslint-plugin-next`, `eslint-plugin-storybook`, `eslint-plugin-react-refresh`. Partial coverage for `jsx-a11y` and `unused-imports`. ### Additional fixes (pre-existing issues exposed by merge) - Fixed `EmailThreadPreview.tsx` broken import from main rename (`useOpenEmailThreadInSidePanel`) - Restored truthiness guard in `getActivityTargetObjectRecords.ts` - Fixed `AgentTurnResolver` return types to match entity (virtual `fileMediaType`/`fileUrl` are resolved via `@ResolveField()`) ## Test plan - [x] `npx nx lint twenty-front` passes - [x] `npx nx lint twenty-server` passes - [x] `npx nx lint twenty-docs` passes - [x] Custom oxlint rules validated with Vitest: `npx nx test twenty-oxlint-rules` - [x] `npx nx typecheck twenty-front` passes - [x] `npx nx typecheck twenty-server` passes - [x] CI workflows trigger correctly with `dependsOn: ["twenty-oxlint-rules:build"]` - [x] IDE linting works with `oxc.oxc-vscode` extension |
||
|
|
1affa1e004 |
chore(front): remove vite-plugin-checker background TS/ESLint checks (#18437)
## Summary Removes `vite-plugin-checker` and all references to `VITE_DISABLE_TYPESCRIPT_CHECKER` / `VITE_DISABLE_ESLINT_CHECKER`. These background checks are no longer needed because our dev experience now relies on **independent** linters and type-checkers: - `npx nx lint:diff-with-main twenty-front` for ESLint - `npx nx typecheck twenty-front` for TypeScript Running these as separate processes (rather than inside Vite) is faster, gives cleaner output, and avoids the significant memory overhead that `vite-plugin-checker` introduces during `vite dev` and `vite build`. The old env vars to disable them are removed from `vite.config.ts`, `package.json` scripts, `nx.json`, `.env.example`, and all translated docs. |
||
|
|
6a2e0182ab |
Bump @blocknote/server-util from 0.47.0 to 0.47.1 (#18408)
Bumps [@blocknote/server-util](https://github.com/TypeCellOS/BlockNote/tree/HEAD/packages/server-util) from 0.47.0 to 0.47.1. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/TypeCellOS/BlockNote/releases"><code>@blocknote/server-util</code>'s releases</a>.</em></p> <blockquote> <h2>v0.47.1</h2> <h2>0.47.1 (2026-03-02)</h2> <h3>🩹 Fixes</h3> <ul> <li>typeerror cannot read properties of undefined (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2522">#2522</a>)</li> <li>handle more delete key cases (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2126">#2126</a>)</li> <li>add delay for <code>data-active</code> in collab cursors (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2383">#2383</a>)</li> <li>disable slash menu in table content <a href="https://github.com/TypeCellOS/BlockNote/tree/HEAD/packages/server-util/issues/2408">#2408</a> (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2504">#2504</a>, <a href="https://redirect.github.com/TypeCellOS/BlockNote/issues/2408">#2408</a>)</li> <li><strong>ai:</strong> selections broken due to floating-ui focus manager (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2527">#2527</a>)</li> </ul> <h3>❤️ Thank You</h3> <ul> <li>Matthew Lipski <a href="https://github.com/matthewlipski"><code>@matthewlipski</code></a></li> <li>Nick Perez</li> <li>Yousef</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/TypeCellOS/BlockNote/blob/main/CHANGELOG.md"><code>@blocknote/server-util</code>'s changelog</a>.</em></p> <blockquote> <h2>0.47.1 (2026-03-02)</h2> <h3>🩹 Fixes</h3> <ul> <li>typeerror cannot read properties of undefined (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2522">#2522</a>)</li> <li>handle more delete key cases (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2126">#2126</a>)</li> <li>add delay for <code>data-active</code> in collab cursors (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2383">#2383</a>)</li> <li>disable slash menu in table content <a href="https://github.com/TypeCellOS/BlockNote/tree/HEAD/packages/server-util/issues/2408">#2408</a> (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2504">#2504</a>, <a href="https://redirect.github.com/TypeCellOS/BlockNote/issues/2408">#2408</a>)</li> <li><strong>ai:</strong> selections broken due to floating-ui focus manager (<a href="https://redirect.github.com/TypeCellOS/BlockNote/pull/2527">#2527</a>)</li> </ul> <h3>❤️ Thank You</h3> <ul> <li>Matthew Lipski <a href="https://github.com/matthewlipski"><code>@matthewlipski</code></a></li> <li>Nick Perez</li> <li>Yousef</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/TypeCellOS/BlockNote/commit/d5d056fe3d5362e73fb72e3e3bf1f839aee3e875"><code>d5d056f</code></a> chore(release): publish 0.47.1</li> <li>See full diff in <a href="https://github.com/TypeCellOS/BlockNote/commits/v0.47.1/packages/server-util">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Abdullah <125115953+mabdullahabaid@users.noreply.github.com> |
||
|
|
72086fe111 |
Bump @dagrejs/dagre from 1.1.3 to 1.1.8 (#18409)
Bumps [@dagrejs/dagre](https://github.com/dagrejs/dagre) from 1.1.3 to 1.1.8. <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/dagrejs/dagre/commit/7e4d15f191678f7f05f3c86d9071a193230e7e00"><code>7e4d15f</code></a> Building for release</li> <li><a href="https://github.com/dagrejs/dagre/commit/d3908e2c13148c9143db585accc10ae0b6634657"><code>d3908e2</code></a> Bumping version</li> <li><a href="https://github.com/dagrejs/dagre/commit/ce295f8e073c4fe96c9e36ecf08ae2940e5e6a10"><code>ce295f8</code></a> Build for release</li> <li><a href="https://github.com/dagrejs/dagre/commit/b64b9057726eee17f24f73579ba0668527276448"><code>b64b905</code></a> Bumping the version</li> <li><a href="https://github.com/dagrejs/dagre/commit/de169d24c13d06c1e9c560f4f4f8f98650109b94"><code>de169d2</code></a> Merge pull request <a href="https://redirect.github.com/dagrejs/dagre/issues/481">#481</a> from Nathan-Fenner/nf/improve-network-simplex-perform...</li> <li><a href="https://github.com/dagrejs/dagre/commit/065e0d8374f4c1c35a7cb4b84df37aaa31598d86"><code>065e0d8</code></a> improve performance of graph node ranking</li> <li><a href="https://github.com/dagrejs/dagre/commit/00d3178d671e49de9c032e3abd281dc9f2739e73"><code>00d3178</code></a> Typo</li> <li><a href="https://github.com/dagrejs/dagre/commit/3982a69d2b323b06aa969a4ec09829d37fe6e7bd"><code>3982a69</code></a> Bump version and set as pre-release</li> <li><a href="https://github.com/dagrejs/dagre/commit/1339f5516508dba0cbcc4ef1c0587e7384bec23d"><code>1339f55</code></a> Building for release</li> <li><a href="https://github.com/dagrejs/dagre/commit/9459f01bc815f16b87db727821d8401acbad2cd3"><code>9459f01</code></a> Bumping the version</li> <li>Additional commits viewable in <a href="https://github.com/dagrejs/dagre/compare/v1.1.3...v1.1.8">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Abdullah <125115953+mabdullahabaid@users.noreply.github.com> |
||
|
|
76c7639eb3 |
fix: upgrade storybook to latest to resolve dependabot alert (#18285)
Resolves [Dependabot Alert 509](https://github.com/twentyhq/twenty/security/dependabot/509). Upgraded storybook and related packages to latest, also fixed a failing test to match what the DOM really contains. |
||
|
|
4ed09a3feb |
Upgrade blocknote dependencies from 0.31.1 to 0.47.0. (#18207)
This PR pgrades all BlockNote packages (@blocknote/core, @blocknote/react, @blocknote/mantine, @blocknote/server-util, @blocknote/xl-docx-exporter, @blocknote/xl-pdf-exporter) to 0.47.0 and adapts the codebase to the new API. ### Changes - Dependency upgrades: Bumped all BlockNote packages to 0.47.0, added required Mantine v8 peer dependencies, removed unnecessary prosemirror resolutions - Formatting toolbar: Replaced the manual reimplementation of FormattingToolbarController (which handled visibility, positioning, portal rendering, text-alignment-based placement, and a dangerouslySetInnerHTML transition trick) with BlockNote's built-in FormattingToolbarController. The toolbar buttons themselves are unchanged. - Side menu: Replaced manual drag handle menu positioning and rendering (DashboardBlockDragHandleMenu, DashboardBlockColorPicker, and their floating configs) with BlockNote's built-in SideMenuController, DragHandleButton, and DragHandleMenu components. Deleted 4 files that became dead code. - Extension API migration: Replaced deprecated editor.suggestionMenus and editor.formattingToolbar APIs with the new extension system (SuggestionMenu, useExtensionState, editor.getExtension()) - Slash menu fixes: Filtered out BlockNote's new default "File" item (added in 0.47) to avoid duplicates with our custom one; added icon mappings for new block types (Toggle List, Divider, Toggle Headings, Headings 4-6) - Server-side: Switched @blocknote/server-util to dynamic import() to handle ESM-only transitive dependencies in CJS context |
||
|
|
9107f5bbc7 |
feat: upgrade ai package to version six and the corresponding @ai-sdk/* packages to compatible versions (#18172)
Used the migration guide to carry out this upgrade: https://ai-sdk.dev/docs/migration-guides/migration-guide-6-0 I have not been able to test locally due to credits. <img width="220" height="450" alt="image" src="https://github.com/user-attachments/assets/050b34b9-3239-4010-8c47-b43d44571994" /> --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com> |
||
|
|
121788c42f |
Fully deprecate old recoil (#18210)
## Summary Removes the `recoil` dependency entirely from `package.json` and `twenty-front/package.json`, completing the migration to Jotai as the sole state management library. Removes all Recoil infrastructure: `RecoilRoot` wrapper from `App.tsx` and test decorators, `RecoilDebugObserver`, Recoil-specific ESLint rules (`use-getLoadable-and-getValue-to-get-atoms`, `useRecoilCallback-has-dependency-array`), and legacy Recoil utility hooks/types (`useRecoilComponentState`, `useRecoilComponentValue`, `createComponentState`, `createFamilyState`, `getSnapshotValue`, `cookieStorageEffect`, `localStorageEffect`, etc.). Renames all `V2`-suffixed Jotai state files and types to their canonical names (e.g., `ComponentStateV2` -> `ComponentState`, `agentChatInputStateV2` -> `agentChatInputState`, `SelectorCallbacksV2` -> `SelectorCallbacks`), and removes the now-redundant V1 counterparts. Updates ~433 files across the codebase to use the renamed Jotai imports, remove Recoil imports, and clean up test wrappers (`RecoilRootDecorator` -> `JotaiRootDecorator`). |
||
|
|
0e25aeb5be |
chore: upgrade @swc/core to 1.15.11 and align SWC ecosystem (#18088)
## Summary - Upgrades `@swc/core` from 1.13.3 to **1.15.11** (swc_core v56), which introduces CBOR-based plugin serialization replacing rkyv, eliminating strict version-matching between SWC core and Wasm plugins - Upgrades `@lingui/swc-plugin` from ^5.6.0 to **^5.11.0** (swc_core 50.2.3, built with `--cfg=swc_ast_unknown` for cross-version compatibility) - Upgrades `@swc/plugin-emotion` from 10.0.4 to **14.6.0** (swc_core 53, also with backward-compat feature) - Upgrades companion packages: `@swc-node/register` 1.8.0 → 1.11.1, `@swc/helpers` ~0.5.2 → ~0.5.18, `@vitejs/plugin-react-swc` 3.11.0 → 4.2.3 ### Why this is safe now Starting from `@swc/core v1.15.0`, SWC replaced the rkyv serialization scheme with CBOR (a self-describing format) and added `Unknown` AST enum variants. Plugins built with `swc_core >= 47` and `--cfg=swc_ast_unknown` are now forward-compatible across `@swc/core` versions. Both `@lingui/swc-plugin@5.10.1+` and `@swc/plugin-emotion@14.0.0+` have this support, meaning the old version-matching nightmare between Lingui and SWC is largely solved. Reference: https://github.com/lingui/swc-plugin/issues/179 ## Test plan - [x] `yarn install` resolves without errors - [x] `npx nx build twenty-shared` succeeds - [x] `npx nx build twenty-ui` succeeds (validates @swc/plugin-emotion@14.6.0) - [x] `npx nx typecheck twenty-front` succeeds - [x] `npx nx build twenty-front` succeeds (validates vite + swc + lingui pipeline) - [x] `npx nx build twenty-emails` succeeds (validates lingui plugin) - [x] Frontend jest tests pass (validates @swc/jest + @lingui/swc-plugin) - [x] Server jest tests pass (validates server-side SWC + lingui) Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
d2f8352cb8 |
Start Jotai Migration (#17893)
## Recoil → Jotai progressive migration: infrastructure + ChipFieldDisplay ### Benchmark In the beginning, there was no hope: <img width="1180" height="948" alt="image" src="https://github.com/user-attachments/assets/f8635991-52e6-4958-8240-6ba7214132b2" /> Then the hope was reborn <img width="2070" height="948" alt="image" src="https://github.com/user-attachments/assets/be1182b9-1c8d-4fdc-ab4c-1484ad74449d" /> ### Approach We introduce a **V2 state management layer** backed by Jotai that mirrors the existing Recoil API, enabling component-by-component migration without a big-bang rewrite. #### V2 API (Jotai-backed, Recoil-ergonomic) - `createStateV2` / `createFamilyStateV2` — drop-in replacements for `createState` / `createFamilyState`, returning wrapper types over Jotai atoms - `useRecoilValueV2`, `useRecoilStateV2`, `useFamilyRecoilValueV2`, etc. — thin wrappers around Jotai's `useAtomValue` / `useAtom` / `useSetAtom` - A shared `jotaiStore` (via `createStore()`) passed to a `<JotaiProvider>` wrapping `<RecoilRoot>`, also accessible imperatively for dual-writes #### Dual-write bridge for progressive migration For state shared between migrated and non-migrated components, we use **dual-write**: writers update both the Recoil atom and the Jotai V2 atom (via `jotaiStore.set()`). This avoids sync components or extra subscriptions. Write sites updated: `useUpsertRecordsInStore`, `useSetRecordTableData`, `ListenRecordUpdatesEffect`, `RecordShowEffect`, `useLoadRecordIndexStates`, `useUpdateObjectViewOptions`. #### First migration: ChipFieldDisplay render path - `useChipFieldDisplay` → reads `recordStoreFamilyStateV2` via `useFamilyRecoilValueV2` (was `useRecoilValue(recordStoreFamilyState)`) - `RecordChip` → reads `recordIndexOpenRecordInStateV2` via `useRecoilValueV2` (was `useRecoilValue(recordIndexOpenRecordInState)`) - `Avatar` (twenty-ui) and event handlers (`useOpenRecordInCommandMenu`) left on Recoil — not on the render path / in a different package #### Pattern for migrating additional state 1. Create V2 atom: `createStateV2` or `createFamilyStateV2` 2. Add `jotaiStore.set(v2Atom, value)` at each write site 3. Switch readers to `useRecoilValueV2(v2Atom)` 4. Once all readers are migrated, remove the Recoil atom and dual-writes #### Why not jotai-recoil-adapter? Evaluated [jotai-recoil-adapter](https://github.com/clockelliptic/jotai-recoil-adapter) — not production-ready (21 open issues, no React 19, forces providerless mode, missing types). We built a purpose-built thin layer instead. |
||
|
|
f0bc9fcb43 |
[FRONT COMPONENTS] Move to twenty-sdk (#17587)
Move front components from twenty-shared to twenty-sdk |
||
|
|
fb97c40cad |
[Dashboards] Replace nivo bar chart with custom canvas bar chart (#17441)
before - https://github.com/user-attachments/assets/01d1ce73-1732-4516-bde0-d43c1bbcb734 after - I got rid of line chart in the after clip -- because now its the line chart thats more laggy :) -- but now could be easily migrated away from nivo https://github.com/user-attachments/assets/430f4697-68cd-47be-b63e-f8df34a2ee0e stress test - before - https://github.com/user-attachments/assets/c3d3e05c-943e-48dc-9429-a2ecf41cd4fe after - https://github.com/user-attachments/assets/98b35a43-f918-4a46-9b66-3bc8deabfdb8 --------- Co-authored-by: bosiraphael <raphael.bosi@gmail.com> |
||
|
|
2b60e41374 |
build(deps-dev): bump @typescript-eslint/parser from 8.39.0 to 8.51.0 (#16926)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.39.0 to 8.51.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@typescript-eslint/parser</code>'s releases</a>.</em></p> <blockquote> <h2>v8.51.0</h2> <h2>8.51.0 (2025-12-29)</h2> <h3>🚀 Features</h3> <ul> <li><strong>eslint-plugin:</strong> expose rule name via RuleModule interface (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11719">#11719</a>)</li> <li><strong>eslint-plugin:</strong> [no-useless-default-assignment] fix some cases to optional syntax (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11871">#11871</a>)</li> <li><strong>eslint-plugin:</strong> add namespace to plugin meta (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11885">#11885</a>)</li> <li><strong>tsconfig-utils:</strong> more informative error on parsing failures (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11888">#11888</a>)</li> </ul> <h3>🩹 Fixes</h3> <ul> <li><strong>eslint-plugin:</strong> fix crash and false positives in <code>no-useless-default-assignment</code> (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11845">#11845</a>)</li> <li><strong>eslint-plugin:</strong> remove fixable from no-dynamic-delete rule (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11876">#11876</a>)</li> <li><strong>eslint-plugin:</strong> bump ts-api-utils to 2.2.0 (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11881">#11881</a>)</li> <li><strong>eslint-plugin:</strong> [prefer-optional-chain] handle MemberExpression in final chain position (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11835">#11835</a>)</li> </ul> <h3>❤️ Thank You</h3> <ul> <li>Josh Goldberg ✨</li> <li>Kirk Waiblinger <a href="https://github.com/kirkwaiblinger"><code>@kirkwaiblinger</code></a></li> <li>mdm317</li> <li>Ulrich Stark</li> <li>Yannick Decat <a href="https://github.com/mho22"><code>@mho22</code></a></li> <li>Yukihiro Hasegawa <a href="https://github.com/y-hsgw"><code>@y-hsgw</code></a></li> </ul> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> <h2>v8.50.1</h2> <h2>8.50.1 (2025-12-22)</h2> <h3>🩹 Fixes</h3> <ul> <li><strong>eslint-plugin:</strong> [method-signature-style] ignore methods that return <code>this</code> (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11813">#11813</a>)</li> <li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] correct handling of undefined vs. void (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11826">#11826</a>)</li> </ul> <h3>❤️ Thank You</h3> <ul> <li>Josh Goldberg ✨</li> <li>Tamashoo <a href="https://github.com/Tamashoo"><code>@Tamashoo</code></a></li> </ul> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> <h2>v8.50.0</h2> <h2>8.50.0 (2025-12-15)</h2> <h3>🚀 Features</h3> <ul> <li><strong>eslint-plugin:</strong> [no-useless-default-assignment] add rule (<a href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/11720">#11720</a>)</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md"><code>@typescript-eslint/parser</code>'s changelog</a>.</em></p> <blockquote> <h2>8.51.0 (2025-12-29)</h2> <p>This was a version bump only for parser to align it with other projects, there were no code changes.</p> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> <h2>8.50.1 (2025-12-22)</h2> <p>This was a version bump only for parser to align it with other projects, there were no code changes.</p> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> <h2>8.50.0 (2025-12-15)</h2> <p>This was a version bump only for parser to align it with other projects, there were no code changes.</p> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> <h2>8.49.0 (2025-12-08)</h2> <p>This was a version bump only for parser to align it with other projects, there were no code changes.</p> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> <h2>8.48.1 (2025-12-02)</h2> <p>This was a version bump only for parser to align it with other projects, there were no code changes.</p> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> <h2>8.48.0 (2025-11-24)</h2> <p>This was a version bump only for parser to align it with other projects, there were no code changes.</p> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> <h2>8.47.0 (2025-11-17)</h2> <p>This was a version bump only for parser to align it with other projects, there were no code changes.</p> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> <h2>8.46.4 (2025-11-10)</h2> <p>This was a version bump only for parser to align it with other projects, there were no code changes.</p> <p>You can read about our <a href="https://typescript-eslint.io/users/versioning">versioning strategy</a> and <a href="https://typescript-eslint.io/users/releases">releases</a> on our website.</p> <h2>8.46.3 (2025-11-03)</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/e4c57f5996a9a3aed8a8c2b02712a9ce37db4928"><code>e4c57f5</code></a> chore(release): publish 8.51.0</li> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/d520b88990e1b20674dcfa3db3b0461c1d6d9aa2"><code>d520b88</code></a> chore(release): publish 8.50.1</li> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/c62e85874f0e482156a54b6744fe90a6f270012a"><code>c62e858</code></a> chore(release): publish 8.50.0</li> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/864595a44b56beb9870bf0f41d59cf7f8f48276a"><code>864595a</code></a> chore(release): publish 8.49.0</li> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/32b7e891bd60ae993e85018ceefa2a0c07590688"><code>32b7e89</code></a> chore(deps): update dependency <code>@vitest/eslint-plugin</code> to v1.5.1 (<a href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser/issues/11816">#11816</a>)</li> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/8fe34456f75c1d1e8a4dc518306d5ab93422efec"><code>8fe3445</code></a> chore(release): publish 8.48.1</li> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/6fb1551634b2ff11718e579098f69e041a2ff92c"><code>6fb1551</code></a> chore(release): publish 8.48.0</li> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/a4dc42ac541139f0da344550bce7accd8f3d366a"><code>a4dc42a</code></a> chore: migrate to nx 22 (<a href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser/issues/11780">#11780</a>)</li> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/28cf8032c2492bb3c55dd7dd145249f2246034ad"><code>28cf803</code></a> chore(release): publish 8.47.0</li> <li><a href="https://github.com/typescript-eslint/typescript-eslint/commit/843f144797c0a94272cdb002c00c5639cf0797c6"><code>843f144</code></a> chore(release): publish 8.46.4</li> <li>Additional commits viewable in <a href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.51.0/packages/parser">compare view</a></li> </ul> </details> <details> <summary>Maintainer changes</summary> <p>This version was pushed to npm by [GitHub Actions](<a href="https://www.npmjs.com/~GitHub">https://www.npmjs.com/~GitHub</a> Actions), a new releaser for <code>@typescript-eslint/parser</code> since your current version.</p> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Abdullah <125115953+mabdullahabaid@users.noreply.github.com> |
||
|
|
21ff42074d |
feat: implement skills system for AI agents (#16865)
## Summary This PR introduces a Skills system for AI agents, inspired by the [Agent Skills specification](https://agentskills.io/specification). ## Changes ### Backend - **SkillEntity**: New database entity with migration for storing skills - **V2 Sync Mechanism**: Implemented FlatSkill, builders, validators, and action handlers following the v2 flat entity pattern - **Standard Skills**: Pre-defined skills (workflow-building, data-manipulation, dashboard-building, metadata-building, research, code-interpreter, xlsx, pdf, docx, pptx) - **GraphQL API**: CRUD operations for skills with proper guards and permissions - **Workspace Cache**: Integrated skills into the workspace cache system ### Frontend - **Skills Table**: Searchable table in AI settings showing all skills - **Skill Form**: Create/edit page with Label (primary), Description, and Content (markdown editor) - **API Name**: Following existing patterns, name is derived from label with advanced settings toggle for custom API names - **Standard vs Custom**: Standard skills are read-only, custom skills can be edited/deleted ## Key Design Decisions - Skills are stored in the database (Salesforce-like approach) rather than files - Name is derived from Label by default (isLabelSyncedWithName pattern) - Skills reference functions/files via @ mentions in markdown content rather than explicit relations - Standard skills are synced from code, custom skills are created via UI ## Screenshots Skills table and form UI follow existing settings patterns. ## Testing - [x] Lint passes - [x] Typecheck passes - [ ] CI tests |
||
|
|
61addf8b62 |
fix(email threads): linkify Email body so that URL links are properly formatted (#16415)
Closes #16396 Added [linkify-react](https://www.npmjs.com/package/linkify-react) and [linkifyjs](https://www.npmjs.com/package/linkifyjs?activeTab=versions) to dependancies. Both are widely used libraries with millions of weekly downloads. Both of them have 0 dependancies on other packages, so should be safe to use. ### Before URLs were shown as plain text <img width="395" height="699" alt="Screenshot 2025-12-09 at 12 25 03 PM" src="https://github.com/user-attachments/assets/772e6d03-8c48-45a1-985c-f775bbd3465c" /> ### After URLs are shown as link and are clickable now. <img width="367" height="641" alt="Screenshot 2025-12-09 at 2 50 29 PM" src="https://github.com/user-attachments/assets/d15bf23a-7cae-4cd4-b9da-e99706c7db38" /> <img width="376" height="656" alt="Screenshot 2025-12-09 at 2 50 46 PM" src="https://github.com/user-attachments/assets/ba112a65-7550-47e3-b42f-83b94c08bfa6" /> --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> |
||
|
|
3096769616 |
build(deps): bump @monaco-editor/react from 4.6.0 to 4.7.0 (#16829)
Bumps [@monaco-editor/react](https://github.com/suren-atoyan/monaco-react) from 4.6.0 to 4.7.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/suren-atoyan/monaco-react/releases"><code>@monaco-editor/react</code>'s releases</a>.</em></p> <blockquote> <h2>v4.7.0</h2> <ul> <li>package: update <code>@monaco-editor/loader</code> to the latest (<code>v1.5.0</code>) version (this uses <code>monaco-editor</code> <code>v0.52.2</code>)</li> <li>package: inherit all changes from <code>v4.7.0-rc.0</code></li> </ul> <h2>v4.7.0-rc.0</h2> <ul> <li>package: add support for react/react-dom <code>v19</code> as a peer dependency</li> <li>playground: update playground's React version to 19</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/suren-atoyan/monaco-react/blob/master/CHANGELOG.md"><code>@monaco-editor/react</code>'s changelog</a>.</em></p> <blockquote> <h2>4.7.0</h2> <ul> <li>package: update <code>@monaco-editor/loader</code> to the latest (v1.5.0) version (this uses monaco-editor v0.52.2)</li> <li>package: inherit all changes from v4.7.0-rc.0</li> </ul> <h2>4.7.0-rc.0</h2> <ul> <li>package: add support for react/react-dom v19 as a peer dependency</li> <li>playground: update playground's React version to 19</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/suren-atoyan/monaco-react/commit/eb120e66378471315620fe5339b73ba003f199ad"><code>eb120e6</code></a> update package to 4.7.0 version</li> <li><a href="https://github.com/suren-atoyan/monaco-react/commit/cdd070c9f080caf4a9a7b13c2c34fa4e10edc9bf"><code>cdd070c</code></a> update snapshots</li> <li><a href="https://github.com/suren-atoyan/monaco-react/commit/55a063e45d2f2672884b77059ac97850758764ae"><code>55a063e</code></a> update <code>@monaco-editor/loader</code> to the latest (v1.5.0) version</li> <li><a href="https://github.com/suren-atoyan/monaco-react/commit/52e8c75616e09730b7b1a0b5822385212a082ce8"><code>52e8c75</code></a> update package to 4.7.0-rc.o version</li> <li><a href="https://github.com/suren-atoyan/monaco-react/commit/e72be4edc1b4492eae9f7d85671ee61a43a6aee8"><code>e72be4e</code></a> add react 19 to peerDependencies</li> <li><a href="https://github.com/suren-atoyan/monaco-react/commit/642be903a9dd21d6fe639ab5c92c234dad77c813"><code>642be90</code></a> update playground's react version to 19</li> <li><a href="https://github.com/suren-atoyan/monaco-react/commit/ceee344fbe26285dabb0fe90985fe18ec867211c"><code>ceee344</code></a> Add Monaco-React AI Bot in Readme (<a href="https://redirect.github.com/suren-atoyan/monaco-react/issues/655">#655</a>)</li> <li><a href="https://github.com/suren-atoyan/monaco-react/commit/f7cac39fbad0f062dc66458831aaf57a7126dd40"><code>f7cac39</code></a> add electron blog post link</li> <li><a href="https://github.com/suren-atoyan/monaco-react/commit/ea601cf9f6fe9f2cc0c6271d6a9cde9a332b6dc0"><code>ea601cf</code></a> add tea constitution file</li> <li><a href="https://github.com/suren-atoyan/monaco-react/commit/3327f3c368cb6d56c02f2df8a9d45177ce6f52e9"><code>3327f3c</code></a> add GitHub sponsor button</li> <li>See full diff in <a href="https://github.com/suren-atoyan/monaco-react/compare/v4.6.0...v4.7.0">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Abdullah <125115953+mabdullahabaid@users.noreply.github.com> |
||
|
|
1fcb8b464c |
fix: move vite plugins into the packages that use them (#16134)
I was looking into [Dependabot Alert 107](https://github.com/twentyhq/twenty/security/dependabot/107) and figured that the alert is caused by `vite-plugin-dts`, which is a development dependency and does not make it into the production build for it to be dangerous. However, while at it, I also saw that some packages used plugins from root package.json while others had them defined in their local package.json. Therefore, I refactored to move plugins where they're required and removed a redundant package. Builds for the following succeed as intended: - twenty-ui - twenty-emails - twenty-website - twenty-front Co-authored-by: Félix Malfait <felix.malfait@gmail.com> |
||
|
|
978c0acb90 |
fix: sentry's sensitive headers are leaked when sendDefaultPii is set to true (#16122)
Resolves [Dependabot Alert 323](https://github.com/twentyhq/twenty/security/dependabot/323), [Dependabot Alert 324](https://github.com/twentyhq/twenty/security/dependabot/324) and [Dependabot Alert 325](https://github.com/twentyhq/twenty/security/dependabot/325). It updates Sentry's packages on the server from 10.21.0 to 10.27.0. I also moved @sentry/react to twenty-front package.json and updated the version from 9.26.0 to 10.27.0 - no breaking changes were introduced in the major upgrade in regards to the API exposed by the dependency. Since @sentry/profiling-node was redundant in the root package.json, I removed it - twenty-server has it already and is the only package dependent on @sentry/profiling-node. |
||
|
|
a1bfab82df |
Remove VITE_DISABLE_ESLINT_CHECKER environment variable (#15943)
The `VITE_DISABLE_ESLINT_CHECKER` environment variable is removed from
the codebase. ESLint checker no longer runs during Vite builds
(equivalent to the previous `VITE_DISABLE_ESLINT_CHECKER=true`
behavior).
**Configuration**
- Removed from `.env.example` (active and commented lines)
- Removed from `vite.config.ts` destructuring and conditional logic
- Removed from build scripts in `package.json` and `nx.json`
**Code change in vite.config.ts:**
```diff
- if (VITE_DISABLE_ESLINT_CHECKER !== 'true') {
- checkers['eslint'] = {
- lintCommand: 'eslint ../../packages/twenty-front --max-warnings 0',
- useFlatConfig: true,
- };
- }
```
**Documentation**
- Updated main English troubleshooting guide to remove references to the
variable
- Translated documentation files are intentionally not modified and will
be handled by a separate workflow
> [!NOTE]
> ESLint will not run in the background via Vite's checker plugin.
Developers will need to run `npx nx lint twenty-front` manually or rely
on their IDE's ESLint extension for real-time feedback on open files.
Created from VS Code via the <a
href="https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github">GitHub
Pull Request</a> extension.
<!-- START COPILOT CODING AGENT SUFFIX -->
<details>
<summary>Original prompt</summary>
> Your job is to delete everything related to
VITE_DISABLE_ESLINT_CHECKER in the codebase.
>
> We mention this env var in documentation: drop the content talking
about it.
>
> We use it in the vite config: drop the check and keep the code paths
that used to run when `VITE_DISABLE_ESLINT_CHECKER=true`.
>
> User has selected text in file packages/twenty-front/.env.example from
3:1 to 3:28
</details>
Created from VS Code via the [GitHub Pull
Request](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github)
extension.
<!-- START COPILOT CODING AGENT TIPS -->
---
✨ Let Copilot coding agent [set things up for
you](https://github.com/twentyhq/twenty/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
|