diff --git a/apps/editor/src/application/framework/mappers/case/toCasePackage.test.ts b/apps/editor/src/application/framework/mappers/case/toCasePackage.test.ts new file mode 100644 index 0000000..4b1334b --- /dev/null +++ b/apps/editor/src/application/framework/mappers/case/toCasePackage.test.ts @@ -0,0 +1,33 @@ +import { describe, it, expect } from 'vitest' +import { frameworkToCfPackage } from './toCasePackage' +import type { Framework } from '@/domain/framework/model/types' +import type { FrameworkId } from '@/domain/shared/types' + +function makeFramework(version?: string): Framework { + return { + id: 'fw-test' as FrameworkId, + metadata: { title: 'Test Framework', version }, + items: new Map(), + associations: new Map(), + } +} + +describe('frameworkToCfPackage — version', () => { + it('passes through an author-assigned version unchanged', () => { + const cfPackage = frameworkToCfPackage({ framework: makeFramework('1.4'), caseVersion: '1.1' }) + expect(cfPackage.CFDocument.version).toBe('1.4') + }) + + it('does not invent a default version when none is set', () => { + const cfPackage = frameworkToCfPackage({ framework: makeFramework(undefined), caseVersion: '1.1' }) + expect(cfPackage.CFDocument.version).toBeUndefined() + }) + + it('leaves version untouched across repeated saves (no auto-increment)', () => { + const framework = makeFramework('2.0') + const first = frameworkToCfPackage({ framework, caseVersion: '1.1' }) + const second = frameworkToCfPackage({ framework, caseVersion: '1.1' }) + expect(first.CFDocument.version).toBe('2.0') + expect(second.CFDocument.version).toBe('2.0') + }) +}) diff --git a/apps/editor/src/application/framework/mappers/case/toCasePackage.ts b/apps/editor/src/application/framework/mappers/case/toCasePackage.ts index a4f3a1d..e066e02 100644 --- a/apps/editor/src/application/framework/mappers/case/toCasePackage.ts +++ b/apps/editor/src/application/framework/mappers/case/toCasePackage.ts @@ -8,9 +8,6 @@ const nowIso = () => new Date().toISOString() /** OpenCASE extension namespace for editor-specific data */ const OPENCASE_EXT_KEY = 'ext:opencase' -/** Default version format: major.minor.build */ -const DEFAULT_VERSION = '1.0.0' - /** UUID v4 pattern */ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i @@ -82,30 +79,6 @@ function makePackageUri(uuid: string): string { return `/ims/case/v1p1/CFPackages/${uuid}` } -/** - * Increment the build number of a version string (format: major.minor.build). - * If the version doesn't match the expected format, returns the default version. - */ -function incrementVersion(currentVersion?: string): string { - if (!currentVersion) return DEFAULT_VERSION - - const parts = currentVersion.split('.') - if (parts.length !== 3) { - // If current version exists but doesn't match format, try to preserve major.minor - if (parts.length === 2) { - return `${parts[0]}.${parts[1]}.0` - } - return DEFAULT_VERSION - } - - const build = Number.parseInt(parts[2], 10) - if (Number.isNaN(build)) { - return `${parts[0]}.${parts[1]}.0` - } - - return `${parts[0]}.${parts[1]}.${build + 1}` -} - type OpencaseExtension = { layout?: NodeLayout notes?: string @@ -148,16 +121,11 @@ function frameworkToCfDocument( framework: Framework, caseVersion: CaseVersion, layout?: NodeLayout, - options?: { incrementVersion?: boolean; edgeType?: string } + options?: { edgeType?: string } ): CFDocument { const meta = framework.metadata const fwId = String(framework.id) const effectiveVersion = caseVersion === 'unknown' ? '1.1' : caseVersion - - // Handle version: either use current, increment, or set default - const documentVersion = options?.incrementVersion - ? incrementVersion(meta.version) - : (meta.version ?? DEFAULT_VERSION) const docTitle = meta.title ?? 'Untitled Framework' @@ -171,7 +139,7 @@ function frameworkToCfDocument( publisher: meta.publisher, notes: meta.notes, language: meta.language, - version: documentVersion, + version: meta.version, adoptionStatus: meta.adoptionStatus, frameworkType: meta.frameworkType, officialSourceURL: meta.officialSourceURL, @@ -349,13 +317,11 @@ function associationToCfAssociation( * @param framework - The domain Framework (source of truth) * @param caseVersion - Target CASE version for serialization ('1.0' or '1.1') * @param layout - Optional layout state to store in extensions - * @param incrementVersion - If true, increment the build number of the version (for saves) */ export function frameworkToCfPackage(params: { framework: Framework caseVersion: CaseVersion layout?: LayoutState - incrementVersion?: boolean /** Edge rendering style to persist with this framework */ edgeType?: string /** CFItemType definitions to include in CFDefinitions (from editor state) */ @@ -369,12 +335,12 @@ export function frameworkToCfPackage(params: { /** CFLicense definitions to include in CFDefinitions (from editor state) */ cfLicenses?: CFLicense[] }): CFPackage { - const { framework, caseVersion, layout, incrementVersion, edgeType, cfItemTypes, cfSubjects, cfConcepts, cfAssociationGroupings, cfLicenses } = params + const { framework, caseVersion, layout, edgeType, cfItemTypes, cfSubjects, cfConcepts, cfAssociationGroupings, cfLicenses } = params const fwId = String(framework.id) // Build CFDocument const documentLayout = layout?.byNodeId?.[fwId] - const document = frameworkToCfDocument(framework, caseVersion, documentLayout, { incrementVersion, edgeType }) + const document = frameworkToCfDocument(framework, caseVersion, documentLayout, { edgeType }) // Build CFItems const itemIds = Array.from(framework.items.keys()).map(String) @@ -783,5 +749,4 @@ export type FrameworkExportParams = { framework: Framework caseVersion: CaseVersion layout?: LayoutState - incrementVersion?: boolean } diff --git a/apps/editor/src/ui/editor/EditorCanvas.tsx b/apps/editor/src/ui/editor/EditorCanvas.tsx index fdec7e3..c096845 100644 --- a/apps/editor/src/ui/editor/EditorCanvas.tsx +++ b/apps/editor/src/ui/editor/EditorCanvas.tsx @@ -159,7 +159,7 @@ export default function EditorCanvas({ onBack, onSaveToServer, isPublishedToOpen const ctx = saveCtxRef.current const { framework, layout } = fromEditorGraph({ graph: { nodes: n, edges: e } }) const cfPackage = frameworkToCfPackage({ - framework, layout, incrementVersion: false, + framework, layout, caseVersion: ctx.caseVersion, edgeType: ctx.edgeType, cfItemTypes: ctx.cfItemTypes, cfSubjects: ctx.cfSubjects, cfConcepts: ctx.cfConcepts, cfLicenses: ctx.cfLicenses, cfAssociationGroupings: ctx.cfAssociationGroupings, @@ -170,13 +170,13 @@ export default function EditorCanvas({ onBack, onSaveToServer, isPublishedToOpen } }, [isPublishedToOpenCase, onFetchCfPackage]) - // Save: Generate CFPackage with version increment and POST to server + // Save: Generate CFPackage and POST to server const handleSave = useCallback(async () => { const { nodes: n, edges: e } = graphRef.current const ctx = saveCtxRef.current const { framework, layout } = fromEditorGraph({ graph: { nodes: n, edges: e } }) const cfPackage = frameworkToCfPackage({ - framework, layout, incrementVersion: true, + framework, layout, caseVersion: ctx.caseVersion, edgeType: ctx.edgeType, cfItemTypes: ctx.cfItemTypes, cfSubjects: ctx.cfSubjects, cfConcepts: ctx.cfConcepts, cfLicenses: ctx.cfLicenses, cfAssociationGroupings: ctx.cfAssociationGroupings, diff --git a/apps/editor/src/ui/editor/components/NodePropertiesPanel.tsx b/apps/editor/src/ui/editor/components/NodePropertiesPanel.tsx index 763ee53..e0b814f 100644 --- a/apps/editor/src/ui/editor/components/NodePropertiesPanel.tsx +++ b/apps/editor/src/ui/editor/components/NodePropertiesPanel.tsx @@ -312,12 +312,10 @@ export default memo(function NodePropertiesPanel({ updateDocument({ statusEndDate: e.target.value || undefined })} /> - {cfDocument?.version ? ( -
-
Version
-
{cfDocument.version}
-
- ) : null} +
+ + updateDocument({ version: e.target.value || undefined })} placeholder="e.g., 1.0" /> +
) : ( /* ── Item fields — single column ── */