diff --git a/v2/src/lib/types/ids.test.ts b/v2/src/lib/types/ids.test.ts new file mode 100644 index 0000000..43798b3 --- /dev/null +++ b/v2/src/lib/types/ids.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect } from 'vitest'; +import { SessionId, ProjectId, type SessionId as SessionIdType, type ProjectId as ProjectIdType } from './ids'; + +describe('branded types', () => { + describe('SessionId', () => { + it('creates a SessionId from a string', () => { + const id = SessionId('sess-abc-123'); + expect(id).toBe('sess-abc-123'); + }); + + it('is usable as a string (template literal)', () => { + const id = SessionId('sess-1'); + expect(`session: ${id}`).toBe('session: sess-1'); + }); + + it('is usable as a Map key', () => { + const map = new Map(); + const id = SessionId('sess-1'); + map.set(id, 42); + expect(map.get(id)).toBe(42); + }); + + it('equality works between two SessionIds with same value', () => { + const a = SessionId('sess-1'); + const b = SessionId('sess-1'); + expect(a === b).toBe(true); + }); + }); + + describe('ProjectId', () => { + it('creates a ProjectId from a string', () => { + const id = ProjectId('proj-xyz'); + expect(id).toBe('proj-xyz'); + }); + + it('is usable as a Map key', () => { + const map = new Map(); + const id = ProjectId('proj-1'); + map.set(id, 'test-project'); + expect(map.get(id)).toBe('test-project'); + }); + }); + + describe('type safety (compile-time)', () => { + it('both types are strings at runtime', () => { + const sid = SessionId('s1'); + const pid = ProjectId('p1'); + expect(typeof sid).toBe('string'); + expect(typeof pid).toBe('string'); + }); + }); +}); diff --git a/v2/src/lib/types/ids.ts b/v2/src/lib/types/ids.ts new file mode 100644 index 0000000..1a51aca --- /dev/null +++ b/v2/src/lib/types/ids.ts @@ -0,0 +1,18 @@ +// Branded types for domain identifiers — prevents accidental swapping of sessionId/projectId +// These are compile-time only; at runtime they are plain strings. + +/** Unique identifier for an agent session */ +export type SessionId = string & { readonly __brand: 'SessionId' }; + +/** Unique identifier for a project */ +export type ProjectId = string & { readonly __brand: 'ProjectId' }; + +/** Create a SessionId from a raw string */ +export function SessionId(value: string): SessionId { + return value as SessionId; +} + +/** Create a ProjectId from a raw string */ +export function ProjectId(value: string): ProjectId { + return value as ProjectId; +}