@guinetik/primitives-ts 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -72,9 +72,77 @@ const metrics: SystemMetrics = {
72
72
  # Build the package
73
73
  npm run build
74
74
 
75
+ # Run tests
76
+ npm test
77
+
78
+ # Run tests in watch mode
79
+ npm run test:watch
80
+
81
+ # Generate coverage report
82
+ npm run test:cov
83
+
75
84
  # The build output will be in the dist/ folder
76
85
  ```
77
86
 
87
+ ## Testing
88
+
89
+ This package includes unit tests using Jest with TypeScript support.
90
+
91
+ ### Test Coverage
92
+
93
+ Tests cover:
94
+ - **Type Guards** - Runtime validation for chat types, models, and request objects
95
+ - **Enum Validation** - ClaudeModel enum consistency and format checks
96
+ - **Constants** - Model tier values and chat role validation
97
+ - **Edge Cases** - Null/undefined handling, invalid types, and boundary conditions
98
+
99
+ ### Running Tests
100
+
101
+ ```bash
102
+ # Run all tests once
103
+ npm test
104
+
105
+ # Watch mode for test-driven development
106
+ npm run test:watch
107
+
108
+ # Generate HTML coverage report (outputs to coverage/ folder)
109
+ npm run test:cov
110
+ ```
111
+
112
+ ### Type Guards
113
+
114
+ The package exports runtime type guards for validating data structures:
115
+
116
+ ```typescript
117
+ import {
118
+ isChatMessage,
119
+ isChatSession,
120
+ isModel,
121
+ isSendMessageRequest
122
+ } from '@guinetik/primitives-ts';
123
+
124
+ // Validate incoming data
125
+ if (isChatMessage(data)) {
126
+ // TypeScript knows data is ChatMessage here
127
+ console.log(data.content);
128
+ }
129
+
130
+ // Validate API requests
131
+ if (isSendMessageRequest(req.body)) {
132
+ // Safe to use req.body as SendMessageRequest
133
+ const { message, model } = req.body;
134
+ }
135
+ ```
136
+
137
+ Available type guards:
138
+ - `isChatRole(value)` - Validates 'user' | 'assistant'
139
+ - `isChatMessage(obj)` - Validates ChatMessage interface
140
+ - `isChatSession(obj)` - Validates ChatSession interface
141
+ - `isModel(obj)` - Validates Model interface
142
+ - `isClaudeModel(value)` - Validates ClaudeModel enum values
143
+ - `isSendMessageRequest(obj)` - Validates SendMessageRequest interface
144
+ - `isCreateSessionRequest(obj)` - Validates CreateSessionRequest interface
145
+
78
146
  ## Publishing Workflow
79
147
 
80
148
  ### ⚠️ IMPORTANT: Never Link Locally
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Chat Types Tests
3
+ * Validation tests for enums and constants
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=chat.types.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.types.spec.d.ts","sourceRoot":"","sources":["../src/chat.types.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ /**
3
+ * Chat Types Tests
4
+ * Validation tests for enums and constants
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const chat_types_1 = require("./chat.types");
8
+ describe('Chat Types', () => {
9
+ describe('ClaudeModel Enum', () => {
10
+ it('should have valid model IDs starting with "claude-"', () => {
11
+ const allModels = Object.values(chat_types_1.ClaudeModel);
12
+ allModels.forEach((modelId) => {
13
+ expect(modelId).toMatch(/^claude-/);
14
+ });
15
+ });
16
+ it('should have exactly 4 defined models', () => {
17
+ const modelCount = Object.keys(chat_types_1.ClaudeModel).length;
18
+ expect(modelCount).toBe(4);
19
+ });
20
+ it('should have HAIKU_3_5 with correct ID', () => {
21
+ expect(chat_types_1.ClaudeModel.HAIKU_3_5).toBe('claude-3-5-haiku-20241022');
22
+ });
23
+ it('should have SONNET_3_5 with correct ID', () => {
24
+ expect(chat_types_1.ClaudeModel.SONNET_3_5).toBe('claude-3-5-sonnet-20241022');
25
+ });
26
+ it('should have SONNET_3_7 with correct ID', () => {
27
+ expect(chat_types_1.ClaudeModel.SONNET_3_7).toBe('claude-3-5-sonnet-20250219');
28
+ });
29
+ it('should have OPUS_3 with correct ID', () => {
30
+ expect(chat_types_1.ClaudeModel.OPUS_3).toBe('claude-3-opus-20240229');
31
+ });
32
+ it('should not have duplicate model IDs', () => {
33
+ const modelIds = Object.values(chat_types_1.ClaudeModel);
34
+ const uniqueIds = new Set(modelIds);
35
+ expect(modelIds.length).toBe(uniqueIds.size);
36
+ });
37
+ it('should have model IDs with valid date format', () => {
38
+ const allModels = Object.values(chat_types_1.ClaudeModel);
39
+ const datePattern = /\d{8}$/; // YYYYMMDD at the end
40
+ allModels.forEach((modelId) => {
41
+ expect(modelId).toMatch(datePattern);
42
+ });
43
+ });
44
+ it('should be accessible by enum key', () => {
45
+ expect(chat_types_1.ClaudeModel['HAIKU_3_5']).toBeDefined();
46
+ expect(chat_types_1.ClaudeModel['SONNET_3_5']).toBeDefined();
47
+ expect(chat_types_1.ClaudeModel['SONNET_3_7']).toBeDefined();
48
+ expect(chat_types_1.ClaudeModel['OPUS_3']).toBeDefined();
49
+ });
50
+ it('should support iteration over enum values', () => {
51
+ const models = [];
52
+ for (const model of Object.values(chat_types_1.ClaudeModel)) {
53
+ models.push(model);
54
+ }
55
+ expect(models).toHaveLength(4);
56
+ expect(models).toContain('claude-3-5-haiku-20241022');
57
+ expect(models).toContain('claude-3-5-sonnet-20241022');
58
+ expect(models).toContain('claude-3-5-sonnet-20250219');
59
+ expect(models).toContain('claude-3-opus-20240229');
60
+ });
61
+ });
62
+ describe('Model Tier Values', () => {
63
+ const validTiers = ['fast', 'balanced', 'powerful', 'expert'];
64
+ it('should recognize all valid tier values', () => {
65
+ validTiers.forEach((tier) => {
66
+ expect(['fast', 'balanced', 'powerful', 'expert']).toContain(tier);
67
+ });
68
+ });
69
+ it('should have exactly 4 tier options', () => {
70
+ expect(validTiers).toHaveLength(4);
71
+ });
72
+ });
73
+ describe('ChatRole Type', () => {
74
+ const validRoles = ['user', 'assistant'];
75
+ it('should have exactly 2 valid roles', () => {
76
+ expect(validRoles).toHaveLength(2);
77
+ });
78
+ it('should include "user" role', () => {
79
+ expect(validRoles).toContain('user');
80
+ });
81
+ it('should include "assistant" role', () => {
82
+ expect(validRoles).toContain('assistant');
83
+ });
84
+ });
85
+ });
@@ -0,0 +1,93 @@
1
+ /**
2
+ * File upload and management types
3
+ *
4
+ * Shared types for file upload, storage, and management across the application.
5
+ *
6
+ * @module file.types
7
+ */
8
+ /**
9
+ * Metadata for an uploaded file
10
+ *
11
+ * Represents a file stored in the system with all its metadata.
12
+ * Used by both API endpoints and frontend components.
13
+ */
14
+ export interface FileMetadata {
15
+ /**
16
+ * Optional unique identifier for the file
17
+ */
18
+ id?: string;
19
+ /**
20
+ * Name of the file including extension
21
+ * @example "example.pdf"
22
+ */
23
+ filename: string;
24
+ /**
25
+ * Size of the file in bytes
26
+ */
27
+ size: number;
28
+ /**
29
+ * MIME type of the file
30
+ * @example "image/png", "application/pdf", "video/mp4"
31
+ */
32
+ mimeType: string;
33
+ /**
34
+ * ISO 8601 timestamp of when the file was uploaded
35
+ * @example "2024-01-15T10:30:00.000Z"
36
+ */
37
+ uploadedAt: string;
38
+ /**
39
+ * Public URL where the file can be accessed
40
+ * @example "https://example.com/uploads/example.pdf"
41
+ */
42
+ url: string;
43
+ }
44
+ /**
45
+ * Request body for renaming a file
46
+ */
47
+ export interface RenameFileRequest {
48
+ /**
49
+ * New filename (including extension)
50
+ */
51
+ newFilename: string;
52
+ }
53
+ /**
54
+ * Response from file upload operation
55
+ */
56
+ export interface FileUploadResponse {
57
+ /**
58
+ * Array of uploaded file metadata
59
+ */
60
+ files: FileMetadata[];
61
+ /**
62
+ * Number of files successfully uploaded
63
+ */
64
+ count: number;
65
+ }
66
+ /**
67
+ * File upload status tracking (frontend only)
68
+ *
69
+ * Used to track the progress of individual file uploads in the UI.
70
+ */
71
+ export interface FileUploadProgress {
72
+ /**
73
+ * Unique identifier for this upload
74
+ */
75
+ id: string;
76
+ /**
77
+ * The file being uploaded
78
+ */
79
+ file: File;
80
+ /**
81
+ * Upload progress percentage (0-100)
82
+ */
83
+ progress: number;
84
+ /**
85
+ * Current status of the upload
86
+ */
87
+ status: 'uploading' | 'success' | 'error';
88
+ /**
89
+ * Error message if status is 'error'
90
+ */
91
+ error?: string;
92
+ }
93
+ //# sourceMappingURL=file.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.types.d.ts","sourceRoot":"","sources":["../src/file.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,KAAK,EAAE,YAAY,EAAE,CAAC;IAEtB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,IAAI,EAAE,IAAI,CAAC;IAEX;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;IAE1C;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /**
3
+ * File upload and management types
4
+ *
5
+ * Shared types for file upload, storage, and management across the application.
6
+ *
7
+ * @module file.types
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/index.d.ts CHANGED
@@ -16,7 +16,9 @@ export * from './totp.types';
16
16
  export * from './deployment-history.types';
17
17
  export * from './github.types';
18
18
  export * from './chat.types';
19
+ export * from './type-guards';
19
20
  export * from './agent.types';
21
+ export * from './file.types';
20
22
  export type { Security, SecurityChallengeOptions, SecurityChallengeResult } from './security.interface';
21
23
  export { SecurityLevel } from './security.interface';
22
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,cAAc,cAAc,CAAC;AAG7B,cAAc,gBAAgB,CAAC;AAG/B,cAAc,iBAAiB,CAAC;AAGhC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,2BAA2B,CAAC;AAG1C,cAAc,cAAc,CAAC;AAG7B,cAAc,4BAA4B,CAAC;AAG3C,cAAc,gBAAgB,CAAC;AAG/B,cAAc,cAAc,CAAC;AAG7B,cAAc,eAAe,CAAC;AAG9B,YAAY,EAAE,QAAQ,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACxG,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,cAAc,cAAc,CAAC;AAG7B,cAAc,gBAAgB,CAAC;AAG/B,cAAc,iBAAiB,CAAC;AAGhC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,2BAA2B,CAAC;AAG1C,cAAc,cAAc,CAAC;AAG7B,cAAc,4BAA4B,CAAC;AAG3C,cAAc,gBAAgB,CAAC;AAG/B,cAAc,cAAc,CAAC;AAG7B,cAAc,eAAe,CAAC;AAG9B,cAAc,eAAe,CAAC;AAG9B,cAAc,cAAc,CAAC;AAG7B,YAAY,EAAE,QAAQ,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACxG,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -42,7 +42,11 @@ __exportStar(require("./deployment-history.types"), exports);
42
42
  __exportStar(require("./github.types"), exports);
43
43
  // Chat system types
44
44
  __exportStar(require("./chat.types"), exports);
45
+ // Type guards for runtime validation
46
+ __exportStar(require("./type-guards"), exports);
45
47
  // Agent monitoring types
46
48
  __exportStar(require("./agent.types"), exports);
49
+ // File upload and management types
50
+ __exportStar(require("./file.types"), exports);
47
51
  var security_interface_1 = require("./security.interface");
48
52
  Object.defineProperty(exports, "SecurityLevel", { enumerable: true, get: function () { return security_interface_1.SecurityLevel; } });
@@ -58,6 +58,35 @@ export type ContainerStatus = 'running' | 'stopped' | 'restarting' | 'error';
58
58
  * Container health status types
59
59
  */
60
60
  export type ContainerHealth = 'healthy' | 'unhealthy' | 'starting' | 'none';
61
+ /**
62
+ * Agent working status types
63
+ */
64
+ export type AgentWorkingStatus = 'idle' | 'working' | 'reviewing' | 'deploying';
65
+ /**
66
+ * Agent type definitions
67
+ */
68
+ export type AgentType = 'dev' | 'tl' | 'designer' | 'infosec';
69
+ /**
70
+ * Agent information for containers
71
+ */
72
+ export interface AgentContainerInfo {
73
+ /** Agent type (dev, tl, designer, infosec) */
74
+ type: AgentType;
75
+ /** Agent display name (Daniel, Mr. Smith, Monica, Elliot) */
76
+ name: string;
77
+ /** Claude model being used */
78
+ model: string;
79
+ /** Current working status */
80
+ workingStatus: AgentWorkingStatus;
81
+ /** Current task ID if working */
82
+ currentTaskId?: string;
83
+ /** Current task description if working */
84
+ currentTaskDescription?: string;
85
+ /** Last activity timestamp */
86
+ lastActivity?: Date;
87
+ /** Current git branch if on feature branch */
88
+ currentBranch?: string;
89
+ }
61
90
  /**
62
91
  * Docker container information
63
92
  */
@@ -82,6 +111,8 @@ export interface Container {
82
111
  image: string;
83
112
  /** Container health status */
84
113
  health: ContainerHealth;
114
+ /** Agent information (only present for agent containers) */
115
+ agentInfo?: AgentContainerInfo;
85
116
  }
86
117
  /**
87
118
  * Log level types
@@ -1 +1 @@
1
- {"version":3,"file":"system.types.d.ts","sourceRoot":"","sources":["../src/system.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"system.types.d.ts","sourceRoot":"","sources":["../src/system.types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Type Guards
3
+ * Runtime type checking for primitives
4
+ */
5
+ import { ChatMessage, ChatSession, ChatRole, Model, ClaudeModel, SendMessageRequest, CreateSessionRequest } from './chat.types';
6
+ /**
7
+ * Type guard for ChatRole
8
+ */
9
+ export declare function isChatRole(value: any): value is ChatRole;
10
+ /**
11
+ * Type guard for ChatMessage
12
+ */
13
+ export declare function isChatMessage(obj: any): obj is ChatMessage;
14
+ /**
15
+ * Type guard for ChatSession
16
+ */
17
+ export declare function isChatSession(obj: any): obj is ChatSession;
18
+ /**
19
+ * Type guard for Model
20
+ */
21
+ export declare function isModel(obj: any): obj is Model;
22
+ /**
23
+ * Type guard for ClaudeModel enum values
24
+ */
25
+ export declare function isClaudeModel(value: any): value is ClaudeModel;
26
+ /**
27
+ * Type guard for SendMessageRequest
28
+ */
29
+ export declare function isSendMessageRequest(obj: any): obj is SendMessageRequest;
30
+ /**
31
+ * Type guard for CreateSessionRequest
32
+ */
33
+ export declare function isCreateSessionRequest(obj: any): obj is CreateSessionRequest;
34
+ //# sourceMappingURL=type-guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-guards.d.ts","sourceRoot":"","sources":["../src/type-guards.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,WAAW,EACX,WAAW,EACX,QAAQ,EACR,KAAK,EACL,WAAW,EACX,kBAAkB,EAClB,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAEtB;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,QAAQ,CAExD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,WAAW,CAY1D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,WAAW,CAY1D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,KAAK,CAe9C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,WAAW,CAE9D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,kBAAkB,CASxE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,oBAAoB,CAQ5E"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * Type Guards
4
+ * Runtime type checking for primitives
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.isChatRole = isChatRole;
8
+ exports.isChatMessage = isChatMessage;
9
+ exports.isChatSession = isChatSession;
10
+ exports.isModel = isModel;
11
+ exports.isClaudeModel = isClaudeModel;
12
+ exports.isSendMessageRequest = isSendMessageRequest;
13
+ exports.isCreateSessionRequest = isCreateSessionRequest;
14
+ const chat_types_1 = require("./chat.types");
15
+ /**
16
+ * Type guard for ChatRole
17
+ */
18
+ function isChatRole(value) {
19
+ return value === 'user' || value === 'assistant';
20
+ }
21
+ /**
22
+ * Type guard for ChatMessage
23
+ */
24
+ function isChatMessage(obj) {
25
+ return (obj !== null &&
26
+ obj !== undefined &&
27
+ typeof obj === 'object' &&
28
+ typeof obj.id === 'string' &&
29
+ typeof obj.sessionId === 'string' &&
30
+ isChatRole(obj.role) &&
31
+ typeof obj.content === 'string' &&
32
+ obj.timestamp instanceof Date &&
33
+ (obj.model === undefined || typeof obj.model === 'string'));
34
+ }
35
+ /**
36
+ * Type guard for ChatSession
37
+ */
38
+ function isChatSession(obj) {
39
+ return (obj !== null &&
40
+ obj !== undefined &&
41
+ typeof obj === 'object' &&
42
+ typeof obj.id === 'string' &&
43
+ typeof obj.userId === 'string' &&
44
+ typeof obj.model === 'string' &&
45
+ obj.createdAt instanceof Date &&
46
+ obj.updatedAt instanceof Date &&
47
+ (obj.title === undefined || typeof obj.title === 'string'));
48
+ }
49
+ /**
50
+ * Type guard for Model
51
+ */
52
+ function isModel(obj) {
53
+ return (obj !== null &&
54
+ obj !== undefined &&
55
+ typeof obj === 'object' &&
56
+ obj.type === 'model' &&
57
+ typeof obj.id === 'string' &&
58
+ typeof obj.display_name === 'string' &&
59
+ typeof obj.created_at === 'string' &&
60
+ (obj.tier === undefined ||
61
+ obj.tier === 'fast' ||
62
+ obj.tier === 'balanced' ||
63
+ obj.tier === 'powerful' ||
64
+ obj.tier === 'expert'));
65
+ }
66
+ /**
67
+ * Type guard for ClaudeModel enum values
68
+ */
69
+ function isClaudeModel(value) {
70
+ return Object.values(chat_types_1.ClaudeModel).includes(value);
71
+ }
72
+ /**
73
+ * Type guard for SendMessageRequest
74
+ */
75
+ function isSendMessageRequest(obj) {
76
+ return (obj !== null &&
77
+ obj !== undefined &&
78
+ typeof obj === 'object' &&
79
+ (obj.sessionId === undefined || typeof obj.sessionId === 'string') &&
80
+ typeof obj.message === 'string' &&
81
+ typeof obj.model === 'string');
82
+ }
83
+ /**
84
+ * Type guard for CreateSessionRequest
85
+ */
86
+ function isCreateSessionRequest(obj) {
87
+ return (obj !== null &&
88
+ obj !== undefined &&
89
+ typeof obj === 'object' &&
90
+ typeof obj.model === 'string' &&
91
+ (obj.title === undefined || typeof obj.title === 'string'));
92
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Type Guards Tests
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=type-guards.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-guards.spec.d.ts","sourceRoot":"","sources":["../src/type-guards.spec.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ /**
3
+ * Type Guards Tests
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const type_guards_1 = require("./type-guards");
7
+ const chat_types_1 = require("./chat.types");
8
+ describe('Type Guards', () => {
9
+ describe('isChatRole', () => {
10
+ it('should return true for valid chat roles', () => {
11
+ expect((0, type_guards_1.isChatRole)('user')).toBe(true);
12
+ expect((0, type_guards_1.isChatRole)('assistant')).toBe(true);
13
+ });
14
+ it('should return false for invalid values', () => {
15
+ expect((0, type_guards_1.isChatRole)('admin')).toBe(false);
16
+ expect((0, type_guards_1.isChatRole)('system')).toBe(false);
17
+ expect((0, type_guards_1.isChatRole)('')).toBe(false);
18
+ expect((0, type_guards_1.isChatRole)(null)).toBe(false);
19
+ expect((0, type_guards_1.isChatRole)(undefined)).toBe(false);
20
+ expect((0, type_guards_1.isChatRole)(123)).toBe(false);
21
+ expect((0, type_guards_1.isChatRole)({})).toBe(false);
22
+ });
23
+ });
24
+ describe('isChatMessage', () => {
25
+ const validMessage = {
26
+ id: 'msg_123',
27
+ sessionId: 'session_456',
28
+ role: 'user',
29
+ content: 'Hello, world!',
30
+ timestamp: new Date(),
31
+ };
32
+ it('should return true for valid ChatMessage', () => {
33
+ expect((0, type_guards_1.isChatMessage)(validMessage)).toBe(true);
34
+ });
35
+ it('should return true for ChatMessage with optional model', () => {
36
+ const messageWithModel = {
37
+ ...validMessage,
38
+ model: 'claude-sonnet-4-5-20250929',
39
+ };
40
+ expect((0, type_guards_1.isChatMessage)(messageWithModel)).toBe(true);
41
+ });
42
+ it('should return false for missing required fields', () => {
43
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, id: undefined })).toBe(false);
44
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, sessionId: undefined })).toBe(false);
45
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, role: undefined })).toBe(false);
46
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, content: undefined })).toBe(false);
47
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, timestamp: undefined })).toBe(false);
48
+ });
49
+ it('should return false for invalid field types', () => {
50
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, id: 123 })).toBe(false);
51
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, sessionId: 123 })).toBe(false);
52
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, role: 'invalid' })).toBe(false);
53
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, content: 123 })).toBe(false);
54
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, timestamp: '2025-01-01' })).toBe(false);
55
+ expect((0, type_guards_1.isChatMessage)({ ...validMessage, model: 123 })).toBe(false);
56
+ });
57
+ it('should return false for null and undefined', () => {
58
+ expect((0, type_guards_1.isChatMessage)(null)).toBe(false);
59
+ expect((0, type_guards_1.isChatMessage)(undefined)).toBe(false);
60
+ });
61
+ it('should return false for non-object values', () => {
62
+ expect((0, type_guards_1.isChatMessage)('message')).toBe(false);
63
+ expect((0, type_guards_1.isChatMessage)(123)).toBe(false);
64
+ expect((0, type_guards_1.isChatMessage)(true)).toBe(false);
65
+ });
66
+ });
67
+ describe('isChatSession', () => {
68
+ const validSession = {
69
+ id: 'session_123',
70
+ userId: 'user_456',
71
+ model: 'claude-sonnet-4-5-20250929',
72
+ createdAt: new Date(),
73
+ updatedAt: new Date(),
74
+ };
75
+ it('should return true for valid ChatSession', () => {
76
+ expect((0, type_guards_1.isChatSession)(validSession)).toBe(true);
77
+ });
78
+ it('should return true for ChatSession with optional title', () => {
79
+ const sessionWithTitle = {
80
+ ...validSession,
81
+ title: 'My Chat Session',
82
+ };
83
+ expect((0, type_guards_1.isChatSession)(sessionWithTitle)).toBe(true);
84
+ });
85
+ it('should return false for missing required fields', () => {
86
+ expect((0, type_guards_1.isChatSession)({ ...validSession, id: undefined })).toBe(false);
87
+ expect((0, type_guards_1.isChatSession)({ ...validSession, userId: undefined })).toBe(false);
88
+ expect((0, type_guards_1.isChatSession)({ ...validSession, model: undefined })).toBe(false);
89
+ expect((0, type_guards_1.isChatSession)({ ...validSession, createdAt: undefined })).toBe(false);
90
+ expect((0, type_guards_1.isChatSession)({ ...validSession, updatedAt: undefined })).toBe(false);
91
+ });
92
+ it('should return false for invalid field types', () => {
93
+ expect((0, type_guards_1.isChatSession)({ ...validSession, id: 123 })).toBe(false);
94
+ expect((0, type_guards_1.isChatSession)({ ...validSession, userId: 123 })).toBe(false);
95
+ expect((0, type_guards_1.isChatSession)({ ...validSession, model: 123 })).toBe(false);
96
+ expect((0, type_guards_1.isChatSession)({ ...validSession, createdAt: '2025-01-01' })).toBe(false);
97
+ expect((0, type_guards_1.isChatSession)({ ...validSession, updatedAt: '2025-01-01' })).toBe(false);
98
+ expect((0, type_guards_1.isChatSession)({ ...validSession, title: 123 })).toBe(false);
99
+ });
100
+ it('should return false for null and undefined', () => {
101
+ expect((0, type_guards_1.isChatSession)(null)).toBe(false);
102
+ expect((0, type_guards_1.isChatSession)(undefined)).toBe(false);
103
+ });
104
+ });
105
+ describe('isModel', () => {
106
+ const validModel = {
107
+ type: 'model',
108
+ id: 'claude-sonnet-4-5-20250929',
109
+ display_name: 'Claude Sonnet 4.5',
110
+ created_at: '2025-09-29T00:00:00Z',
111
+ };
112
+ it('should return true for valid Model', () => {
113
+ expect((0, type_guards_1.isModel)(validModel)).toBe(true);
114
+ });
115
+ it('should return true for Model with optional tier', () => {
116
+ expect((0, type_guards_1.isModel)({ ...validModel, tier: 'fast' })).toBe(true);
117
+ expect((0, type_guards_1.isModel)({ ...validModel, tier: 'balanced' })).toBe(true);
118
+ expect((0, type_guards_1.isModel)({ ...validModel, tier: 'powerful' })).toBe(true);
119
+ expect((0, type_guards_1.isModel)({ ...validModel, tier: 'expert' })).toBe(true);
120
+ });
121
+ it('should return false for missing required fields', () => {
122
+ expect((0, type_guards_1.isModel)({ ...validModel, type: undefined })).toBe(false);
123
+ expect((0, type_guards_1.isModel)({ ...validModel, id: undefined })).toBe(false);
124
+ expect((0, type_guards_1.isModel)({ ...validModel, display_name: undefined })).toBe(false);
125
+ expect((0, type_guards_1.isModel)({ ...validModel, created_at: undefined })).toBe(false);
126
+ });
127
+ it('should return false for invalid type value', () => {
128
+ expect((0, type_guards_1.isModel)({ ...validModel, type: 'invalid' })).toBe(false);
129
+ expect((0, type_guards_1.isModel)({ ...validModel, type: 'user' })).toBe(false);
130
+ });
131
+ it('should return false for invalid tier value', () => {
132
+ expect((0, type_guards_1.isModel)({ ...validModel, tier: 'invalid' })).toBe(false);
133
+ expect((0, type_guards_1.isModel)({ ...validModel, tier: 'super-fast' })).toBe(false);
134
+ });
135
+ it('should return false for null and undefined', () => {
136
+ expect((0, type_guards_1.isModel)(null)).toBe(false);
137
+ expect((0, type_guards_1.isModel)(undefined)).toBe(false);
138
+ });
139
+ });
140
+ describe('isClaudeModel', () => {
141
+ it('should return true for valid ClaudeModel enum values', () => {
142
+ expect((0, type_guards_1.isClaudeModel)(chat_types_1.ClaudeModel.HAIKU_3_5)).toBe(true);
143
+ expect((0, type_guards_1.isClaudeModel)(chat_types_1.ClaudeModel.SONNET_3_5)).toBe(true);
144
+ expect((0, type_guards_1.isClaudeModel)(chat_types_1.ClaudeModel.SONNET_3_7)).toBe(true);
145
+ expect((0, type_guards_1.isClaudeModel)(chat_types_1.ClaudeModel.OPUS_3)).toBe(true);
146
+ });
147
+ it('should return true for valid model ID strings', () => {
148
+ expect((0, type_guards_1.isClaudeModel)('claude-3-5-haiku-20241022')).toBe(true);
149
+ expect((0, type_guards_1.isClaudeModel)('claude-3-5-sonnet-20241022')).toBe(true);
150
+ expect((0, type_guards_1.isClaudeModel)('claude-3-5-sonnet-20250219')).toBe(true);
151
+ expect((0, type_guards_1.isClaudeModel)('claude-3-opus-20240229')).toBe(true);
152
+ });
153
+ it('should return false for invalid model IDs', () => {
154
+ expect((0, type_guards_1.isClaudeModel)('claude-4-sonnet')).toBe(false);
155
+ expect((0, type_guards_1.isClaudeModel)('gpt-4')).toBe(false);
156
+ expect((0, type_guards_1.isClaudeModel)('invalid-model')).toBe(false);
157
+ expect((0, type_guards_1.isClaudeModel)('')).toBe(false);
158
+ });
159
+ it('should return false for non-string values', () => {
160
+ expect((0, type_guards_1.isClaudeModel)(null)).toBe(false);
161
+ expect((0, type_guards_1.isClaudeModel)(undefined)).toBe(false);
162
+ expect((0, type_guards_1.isClaudeModel)(123)).toBe(false);
163
+ expect((0, type_guards_1.isClaudeModel)({})).toBe(false);
164
+ });
165
+ });
166
+ describe('isSendMessageRequest', () => {
167
+ const validRequest = {
168
+ message: 'Hello, Claude!',
169
+ model: 'claude-sonnet-4-5-20250929',
170
+ };
171
+ it('should return true for valid SendMessageRequest', () => {
172
+ expect((0, type_guards_1.isSendMessageRequest)(validRequest)).toBe(true);
173
+ });
174
+ it('should return true with optional sessionId', () => {
175
+ const requestWithSession = {
176
+ ...validRequest,
177
+ sessionId: 'session_123',
178
+ };
179
+ expect((0, type_guards_1.isSendMessageRequest)(requestWithSession)).toBe(true);
180
+ });
181
+ it('should return false for missing required fields', () => {
182
+ expect((0, type_guards_1.isSendMessageRequest)({ ...validRequest, message: undefined })).toBe(false);
183
+ expect((0, type_guards_1.isSendMessageRequest)({ ...validRequest, model: undefined })).toBe(false);
184
+ });
185
+ it('should return false for invalid field types', () => {
186
+ expect((0, type_guards_1.isSendMessageRequest)({ ...validRequest, message: 123 })).toBe(false);
187
+ expect((0, type_guards_1.isSendMessageRequest)({ ...validRequest, model: 123 })).toBe(false);
188
+ expect((0, type_guards_1.isSendMessageRequest)({ ...validRequest, sessionId: 123 })).toBe(false);
189
+ });
190
+ it('should return false for null and undefined', () => {
191
+ expect((0, type_guards_1.isSendMessageRequest)(null)).toBe(false);
192
+ expect((0, type_guards_1.isSendMessageRequest)(undefined)).toBe(false);
193
+ });
194
+ });
195
+ describe('isCreateSessionRequest', () => {
196
+ const validRequest = {
197
+ model: 'claude-sonnet-4-5-20250929',
198
+ };
199
+ it('should return true for valid CreateSessionRequest', () => {
200
+ expect((0, type_guards_1.isCreateSessionRequest)(validRequest)).toBe(true);
201
+ });
202
+ it('should return true with optional title', () => {
203
+ const requestWithTitle = {
204
+ ...validRequest,
205
+ title: 'My Session',
206
+ };
207
+ expect((0, type_guards_1.isCreateSessionRequest)(requestWithTitle)).toBe(true);
208
+ });
209
+ it('should return false for missing required fields', () => {
210
+ expect((0, type_guards_1.isCreateSessionRequest)({ ...validRequest, model: undefined })).toBe(false);
211
+ expect((0, type_guards_1.isCreateSessionRequest)({})).toBe(false);
212
+ });
213
+ it('should return false for invalid field types', () => {
214
+ expect((0, type_guards_1.isCreateSessionRequest)({ ...validRequest, model: 123 })).toBe(false);
215
+ expect((0, type_guards_1.isCreateSessionRequest)({ ...validRequest, title: 123 })).toBe(false);
216
+ });
217
+ it('should return false for null and undefined', () => {
218
+ expect((0, type_guards_1.isCreateSessionRequest)(null)).toBe(false);
219
+ expect((0, type_guards_1.isCreateSessionRequest)(undefined)).toBe(false);
220
+ });
221
+ });
222
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guinetik/primitives-ts",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Shared TypeScript primitives for Guinetik projects",
5
5
  "author": "Guinetik",
6
6
  "license": "MIT",
@@ -11,6 +11,10 @@
11
11
  ],
12
12
  "scripts": {
13
13
  "build": "tsc",
14
+ "test": "jest",
15
+ "test:watch": "jest --watch",
16
+ "test:cov": "jest --coverage",
17
+ "pre-commit": "npm run test && npm run build",
14
18
  "prepublishOnly": "npm run build"
15
19
  },
16
20
  "keywords": [
@@ -20,6 +24,9 @@
20
24
  "guinetik"
21
25
  ],
22
26
  "devDependencies": {
27
+ "@types/jest": "^30.0.0",
28
+ "jest": "^30.2.0",
29
+ "ts-jest": "^29.4.5",
23
30
  "typescript": "^5.9.3"
24
31
  },
25
32
  "repository": {