From 9c124f8851fede98ab3fca4cc8f8c1194dae8f2c Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sun, 24 May 2026 02:24:37 +0100 Subject: [PATCH] feat(base): guard system-source/primary type changes; allow rename mid-conversion --- .../base/services/base-property.service.ts | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/apps/server/src/core/base/services/base-property.service.ts b/apps/server/src/core/base/services/base-property.service.ts index 48f3f5d0e..e9d63b5f6 100644 --- a/apps/server/src/core/base/services/base-property.service.ts +++ b/apps/server/src/core/base/services/base-property.service.ts @@ -198,9 +198,13 @@ export class BasePropertyService { await this.ensureNameUnique(dto.pageId, dto.name, dto.propertyId); } - // Block concurrent type changes — the worker still owns the previous - // conversion, and letting a second one through would race on `type`. - if (property.pendingType) { + // Block competing type / typeOptions edits — the worker still owns + // the previous conversion. A name-only rename is safe and passes + // through; it doesn't touch the type/cells the worker is rewriting. + if ( + property.pendingType && + (dto.type !== undefined || dto.typeOptions !== undefined) + ) { throw new ConflictException( 'A type conversion is already in progress for this property', ); @@ -211,6 +215,19 @@ export class BasePropertyService { const oldTypeOptions = property.typeOptions; const newType = (dto.type ?? property.type) as BasePropertyTypeValue; + if (isTypeChange && isSystemPropertyType(oldType)) { + throw new BadRequestException( + 'Cannot change the type of a system property', + ); + } + if ( + isTypeChange && + property.isPrimary && + newType !== BasePropertyType.TEXT + ) { + throw new BadRequestException('The primary property must be text'); + } + // --- Formula-specific type-option compilation --------------------------- // If the update is a formula (either staying a formula and editing source, // or converting TO formula), compile the source, cycle-check, and replace @@ -500,6 +517,12 @@ export class BasePropertyService { throw new BadRequestException('Cannot delete the primary property'); } + if (property.pendingType) { + throw new ConflictException( + 'Cannot delete a property while a type conversion is in progress', + ); + } + // Compute dependents BEFORE the delete — once soft-deleted the graph // wouldn't include them. const allProps = await this.basePropertyRepo.findByPageId(dto.pageId);