Compare commits

...
Author SHA1 Message Date
sonarly-bot 01b70f0c13 fix(sdk): stop runtime export of null conditional stubs
https://sonarly.com/issue/41804?type=bug

`isDefined` is exported from `twenty-sdk/front-component` but is implemented as `null`, so calling it in a custom front-component throws a runtime `TypeError` and crashes render.

Fix: Implemented the fix in the front-component conditional-availability export layer so callable helper exports are no longer `null` at runtime.

What changed:
- Updated `packages/twenty-sdk/src/sdk/front-component/conditional-availability/conditional-availability-variables.ts`.
- Kept context variables (like `pageType`, `selectedRecords`, etc.) as compile-time stubs.
- Replaced callable helper stubs with concrete implementations:
  - `isDefined`
  - `isNonEmptyString`
  - `includes`
  - `every`, `everyDefined`, `everyEquals`
  - `some`, `someDefined`, `someEquals`
  - `none`, `noneDefined`, `noneEquals`
  - `someNonEmptyString`
  - `includesEvery`
- Implementations mirror existing conditional-availability parser semantics (non-empty array gating, nested property resolution via `safeGetNestedProperty`, predicate-based checks), so runtime usage is safe and consistent.

Why this addresses the incident:
- The crash happened because `isDefined` was exported as `null` and then invoked by custom front-components.
- After this change, invoking these helper exports no longer throws `TypeError: ... is not a function`.

Validation notes:
- Attempted to run lint/typecheck, but Nx modules are unavailable in this execution environment (`Could not find Nx modules at "/work/repos/twentyhq-twenty"`).

Authored by Sonarly by autonomous analysis (run 47694).
2026-06-01 03:10:20 +00:00
@@ -1,5 +1,38 @@
import { isNonEmptyString as isGuardNonEmptyString } from '@sniptt/guards';
import { safeGetNestedProperty, isDefined as isDefinedUtil } from 'twenty-shared/utils';
import { type CommandMenuContextApi } from 'twenty-shared/types';
type ArrayMethod = 'every' | 'some';
const createArrayPropCheck = (
method: ArrayMethod,
predicate: (value: unknown) => boolean,
) => {
return (array: unknown, prop: string) => {
if (!Array.isArray(array) || array.length === 0) {
return false;
}
return array[method]((item) => predicate(safeGetNestedProperty(item, prop)));
};
};
const createArrayPropValueCheck = (
method: ArrayMethod,
predicate: (value: unknown, target: unknown) => boolean,
) => {
return (array: unknown, prop: string, value: unknown) => {
if (!Array.isArray(array) || array.length === 0) {
return false;
}
return array[method]((item) =>
predicate(safeGetNestedProperty(item, prop), value),
);
};
};
export const pageType = null as unknown as CommandMenuContextApi['pageType'];
export const isInSidePanel =
null as unknown as CommandMenuContextApi['isInSidePanel'];
@@ -36,57 +69,40 @@ export const objectMetadataItem =
export const objectMetadataLabel =
null as unknown as CommandMenuContextApi['objectMetadataLabel'];
export const isDefined = null as unknown as (value: unknown) => boolean;
export const isNonEmptyString = null as unknown as (value: unknown) => boolean;
export const includes = null as unknown as (
array: unknown,
value: unknown,
) => boolean;
export const every = null as unknown as (
array: unknown,
prop: string,
) => boolean;
export const everyDefined = null as unknown as (
array: unknown,
prop: string,
) => boolean;
export const everyEquals = null as unknown as (
array: unknown,
prop: string,
value: unknown,
) => boolean;
export const some = null as unknown as (
array: unknown,
prop: string,
) => boolean;
export const someDefined = null as unknown as (
array: unknown,
prop: string,
) => boolean;
export const someEquals = null as unknown as (
array: unknown,
prop: string,
value: unknown,
) => boolean;
export const none = null as unknown as (
array: unknown,
prop: string,
) => boolean;
export const noneDefined = null as unknown as (
array: unknown,
prop: string,
) => boolean;
export const noneEquals = null as unknown as (
array: unknown,
prop: string,
value: unknown,
) => boolean;
export const someNonEmptyString = null as unknown as (
array: unknown,
prop: string,
) => boolean;
export const includesEvery = null as unknown as (
array: unknown,
prop: string,
value: unknown,
) => boolean;
export const isDefined = isDefinedUtil;
export const isNonEmptyString = (value: unknown) => isGuardNonEmptyString(value);
export const includes = (array: unknown, value: unknown) =>
Array.isArray(array) && array.includes(value);
export const every = createArrayPropCheck('every', Boolean);
export const everyDefined = createArrayPropCheck('every', isDefinedUtil);
export const everyEquals = createArrayPropValueCheck('every', (a, b) => a === b);
export const some = createArrayPropCheck('some', Boolean);
export const someDefined = createArrayPropCheck('some', isDefinedUtil);
export const someEquals = createArrayPropValueCheck('some', (a, b) => a === b);
export const none = createArrayPropCheck('every', (value) => !Boolean(value));
export const noneDefined = createArrayPropCheck(
'every',
(value) => !isDefinedUtil(value),
);
export const noneEquals = createArrayPropValueCheck('every', (a, b) => a !== b);
export const someNonEmptyString = createArrayPropCheck(
'some',
(value) => isGuardNonEmptyString(value),
);
export const includesEvery = createArrayPropValueCheck(
'every',
(array, value) => Array.isArray(array) && array.includes(value),
);