Merge schema/enums: 6 TypeBox enum schemas with type aliases, 21 tests
This commit is contained in:
@@ -1 +1,67 @@
|
|||||||
// Enum definitions: TaskScope, TaskRisk, TaskImpact, TaskLevel, TaskStatus, TaskPriority
|
import { Type, type Static, type TSchema } from "@alkdev/typebox";
|
||||||
|
|
||||||
|
// --- Nullable helper ---
|
||||||
|
|
||||||
|
/** Wrap a schema to also accept `null`. */
|
||||||
|
export const Nullable = <T extends TSchema>(schema: T) =>
|
||||||
|
Type.Union([schema, Type.Null()]);
|
||||||
|
|
||||||
|
// --- Enum schemas (runtime) and type aliases (compile-time) ---
|
||||||
|
|
||||||
|
export const TaskScopeEnum = Type.Union([
|
||||||
|
Type.Literal("single"),
|
||||||
|
Type.Literal("narrow"),
|
||||||
|
Type.Literal("moderate"),
|
||||||
|
Type.Literal("broad"),
|
||||||
|
Type.Literal("system"),
|
||||||
|
]);
|
||||||
|
/** "single" | "narrow" | "moderate" | "broad" | "system" */
|
||||||
|
export type TaskScope = Static<typeof TaskScopeEnum>;
|
||||||
|
|
||||||
|
export const TaskRiskEnum = Type.Union([
|
||||||
|
Type.Literal("trivial"),
|
||||||
|
Type.Literal("low"),
|
||||||
|
Type.Literal("medium"),
|
||||||
|
Type.Literal("high"),
|
||||||
|
Type.Literal("critical"),
|
||||||
|
]);
|
||||||
|
/** "trivial" | "low" | "medium" | "high" | "critical" */
|
||||||
|
export type TaskRisk = Static<typeof TaskRiskEnum>;
|
||||||
|
|
||||||
|
export const TaskImpactEnum = Type.Union([
|
||||||
|
Type.Literal("isolated"),
|
||||||
|
Type.Literal("component"),
|
||||||
|
Type.Literal("phase"),
|
||||||
|
Type.Literal("project"),
|
||||||
|
]);
|
||||||
|
/** "isolated" | "component" | "phase" | "project" */
|
||||||
|
export type TaskImpact = Static<typeof TaskImpactEnum>;
|
||||||
|
|
||||||
|
export const TaskLevelEnum = Type.Union([
|
||||||
|
Type.Literal("planning"),
|
||||||
|
Type.Literal("decomposition"),
|
||||||
|
Type.Literal("implementation"),
|
||||||
|
Type.Literal("review"),
|
||||||
|
Type.Literal("research"),
|
||||||
|
]);
|
||||||
|
/** "planning" | "decomposition" | "implementation" | "review" | "research" */
|
||||||
|
export type TaskLevel = Static<typeof TaskLevelEnum>;
|
||||||
|
|
||||||
|
export const TaskPriorityEnum = Type.Union([
|
||||||
|
Type.Literal("low"),
|
||||||
|
Type.Literal("medium"),
|
||||||
|
Type.Literal("high"),
|
||||||
|
Type.Literal("critical"),
|
||||||
|
]);
|
||||||
|
/** "low" | "medium" | "high" | "critical" */
|
||||||
|
export type TaskPriority = Static<typeof TaskPriorityEnum>;
|
||||||
|
|
||||||
|
export const TaskStatusEnum = Type.Union([
|
||||||
|
Type.Literal("pending"),
|
||||||
|
Type.Literal("in-progress"),
|
||||||
|
Type.Literal("completed"),
|
||||||
|
Type.Literal("failed"),
|
||||||
|
Type.Literal("blocked"),
|
||||||
|
]);
|
||||||
|
/** "pending" | "in-progress" | "completed" | "failed" | "blocked" */
|
||||||
|
export type TaskStatus = Static<typeof TaskStatusEnum>;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: schema/enums
|
id: schema/enums
|
||||||
name: Define TypeBox categorical enum schemas and type aliases
|
name: Define TypeBox categorical enum schemas and type aliases
|
||||||
status: pending
|
status: completed
|
||||||
depends_on:
|
depends_on:
|
||||||
- setup/project-init
|
- setup/project-init
|
||||||
scope: narrow
|
scope: narrow
|
||||||
@@ -18,17 +18,17 @@ The six enums: `TaskScopeEnum`, `TaskRiskEnum`, `TaskImpactEnum`, `TaskLevelEnum
|
|||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
- [ ] `src/schema/enums.ts` exports all six enum schemas and their type aliases
|
- [x] `src/schema/enums.ts` exports all six enum schemas and their type aliases
|
||||||
- [ ] Each enum uses `Type.Union([Type.Literal("value"), ...])` pattern per [typebox-patterns.md](../../../docs/research/typebox-patterns.md)
|
- [x] Each enum uses `Type.Union([Type.Literal("value"), ...])` pattern per [typebox-patterns.md](../../../docs/research/typebox-patterns.md)
|
||||||
- [ ] `TaskScopeEnum`: `"single" | "narrow" | "moderate" | "broad" | "system"`
|
- [x] `TaskScopeEnum`: `"single" | "narrow" | "moderate" | "broad" | "system"`
|
||||||
- [ ] `TaskRiskEnum`: `"trivial" | "low" | "medium" | "high" | "critical"`
|
- [x] `TaskRiskEnum`: `"trivial" | "low" | "medium" | "high" | "critical"`
|
||||||
- [ ] `TaskImpactEnum`: `"isolated" | "component" | "phase" | "project"`
|
- [x] `TaskImpactEnum`: `"isolated" | "component" | "phase" | "project"`
|
||||||
- [ ] `TaskLevelEnum`: `"planning" | "decomposition" | "implementation" | "review" | "research"`
|
- [x] `TaskLevelEnum`: `"planning" | "decomposition" | "implementation" | "review" | "research"`
|
||||||
- [ ] `TaskPriorityEnum`: `"low" | "medium" | "high" | "critical"`
|
- [x] `TaskPriorityEnum`: `"low" | "medium" | "high" | "critical"`
|
||||||
- [ ] `TaskStatusEnum`: `"pending" | "in-progress" | "completed" | "failed" | "blocked"`
|
- [x] `TaskStatusEnum`: `"pending" | "in-progress" | "completed" | "failed" | "blocked"`
|
||||||
- [ ] Type aliases derived via `Static<typeof>`: `TaskScope`, `TaskRisk`, `TaskImpact`, `TaskLevel`, `TaskPriority`, `TaskStatus`
|
- [x] Type aliases derived via `Static<typeof>`: `TaskScope`, `TaskRisk`, `TaskImpact`, `TaskLevel`, `TaskPriority`, `TaskStatus`
|
||||||
- [ ] Naming convention matches spec: `Enum` suffix on schema constants only, never on type aliases
|
- [x] Naming convention matches spec: `Enum` suffix on schema constants only, never on type aliases
|
||||||
- [ ] `src/schema/index.ts` re-exports all schemas and types
|
- [x] `src/schema/index.ts` re-exports all schemas and types
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
@@ -37,8 +37,11 @@ The six enums: `TaskScopeEnum`, `TaskRiskEnum`, `TaskImpactEnum`, `TaskLevelEnum
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
> To be filled by implementation agent
|
Also exported the `Nullable` helper generic (used by downstream schemas) and added JSDoc comments on each type alias.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
> To be filled on completion
|
Implemented all six categorical enum schemas using `Type.Union([Type.Literal(...)])` pattern with `Static<typeof>` type aliases.
|
||||||
|
- Created: `src/schema/enums.ts` (6 enum schemas + 6 type aliases + Nullable helper)
|
||||||
|
- Modified: `test/schema.test.ts` (21 enum-specific tests: Value.Check validation, Nullable helper, compile-time type alias verification)
|
||||||
|
- Tests: 21 enum tests + 4 placeholders, all passing; `tsc --noEmit` clean
|
||||||
@@ -1,7 +1,185 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { Value } from '@alkdev/typebox/value';
|
||||||
|
import {
|
||||||
|
TaskScopeEnum,
|
||||||
|
TaskRiskEnum,
|
||||||
|
TaskImpactEnum,
|
||||||
|
TaskLevelEnum,
|
||||||
|
TaskPriorityEnum,
|
||||||
|
TaskStatusEnum,
|
||||||
|
Nullable,
|
||||||
|
} from '../src/schema/enums.js';
|
||||||
|
|
||||||
describe('Schema', () => {
|
describe('Enum schemas', () => {
|
||||||
it('placeholder — schema validation', () => {
|
// --- TaskScopeEnum ---
|
||||||
expect(true).toBe(true);
|
describe('TaskScopeEnum', () => {
|
||||||
|
const validValues = ['single', 'narrow', 'moderate', 'broad', 'system'] as const;
|
||||||
|
|
||||||
|
it('accepts each valid literal', () => {
|
||||||
|
for (const value of validValues) {
|
||||||
|
expect(Value.Check(TaskScopeEnum, value)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects invalid values', () => {
|
||||||
|
expect(Value.Check(TaskScopeEnum, 'unknown')).toBe(false);
|
||||||
|
expect(Value.Check(TaskScopeEnum, '')).toBe(false);
|
||||||
|
expect(Value.Check(TaskScopeEnum, 42)).toBe(false);
|
||||||
|
expect(Value.Check(TaskScopeEnum, null)).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
// --- TaskRiskEnum ---
|
||||||
|
describe('TaskRiskEnum', () => {
|
||||||
|
const validValues = ['trivial', 'low', 'medium', 'high', 'critical'] as const;
|
||||||
|
|
||||||
|
it('accepts each valid literal', () => {
|
||||||
|
for (const value of validValues) {
|
||||||
|
expect(Value.Check(TaskRiskEnum, value)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects invalid values', () => {
|
||||||
|
expect(Value.Check(TaskRiskEnum, 'unknown')).toBe(false);
|
||||||
|
expect(Value.Check(TaskRiskEnum, '')).toBe(false);
|
||||||
|
expect(Value.Check(TaskRiskEnum, 42)).toBe(false);
|
||||||
|
expect(Value.Check(TaskRiskEnum, null)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- TaskImpactEnum ---
|
||||||
|
describe('TaskImpactEnum', () => {
|
||||||
|
const validValues = ['isolated', 'component', 'phase', 'project'] as const;
|
||||||
|
|
||||||
|
it('accepts each valid literal', () => {
|
||||||
|
for (const value of validValues) {
|
||||||
|
expect(Value.Check(TaskImpactEnum, value)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects invalid values', () => {
|
||||||
|
expect(Value.Check(TaskImpactEnum, 'unknown')).toBe(false);
|
||||||
|
expect(Value.Check(TaskImpactEnum, '')).toBe(false);
|
||||||
|
expect(Value.Check(TaskImpactEnum, 42)).toBe(false);
|
||||||
|
expect(Value.Check(TaskImpactEnum, null)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- TaskLevelEnum ---
|
||||||
|
describe('TaskLevelEnum', () => {
|
||||||
|
const validValues = ['planning', 'decomposition', 'implementation', 'review', 'research'] as const;
|
||||||
|
|
||||||
|
it('accepts each valid literal', () => {
|
||||||
|
for (const value of validValues) {
|
||||||
|
expect(Value.Check(TaskLevelEnum, value)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects invalid values', () => {
|
||||||
|
expect(Value.Check(TaskLevelEnum, 'unknown')).toBe(false);
|
||||||
|
expect(Value.Check(TaskLevelEnum, '')).toBe(false);
|
||||||
|
expect(Value.Check(TaskLevelEnum, 42)).toBe(false);
|
||||||
|
expect(Value.Check(TaskLevelEnum, null)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- TaskPriorityEnum ---
|
||||||
|
describe('TaskPriorityEnum', () => {
|
||||||
|
const validValues = ['low', 'medium', 'high', 'critical'] as const;
|
||||||
|
|
||||||
|
it('accepts each valid literal', () => {
|
||||||
|
for (const value of validValues) {
|
||||||
|
expect(Value.Check(TaskPriorityEnum, value)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects invalid values', () => {
|
||||||
|
expect(Value.Check(TaskPriorityEnum, 'unknown')).toBe(false);
|
||||||
|
expect(Value.Check(TaskPriorityEnum, '')).toBe(false);
|
||||||
|
expect(Value.Check(TaskPriorityEnum, 42)).toBe(false);
|
||||||
|
expect(Value.Check(TaskPriorityEnum, null)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- TaskStatusEnum ---
|
||||||
|
describe('TaskStatusEnum', () => {
|
||||||
|
const validValues = ['pending', 'in-progress', 'completed', 'failed', 'blocked'] as const;
|
||||||
|
|
||||||
|
it('accepts each valid literal', () => {
|
||||||
|
for (const value of validValues) {
|
||||||
|
expect(Value.Check(TaskStatusEnum, value)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects invalid values', () => {
|
||||||
|
expect(Value.Check(TaskStatusEnum, 'unknown')).toBe(false);
|
||||||
|
expect(Value.Check(TaskStatusEnum, '')).toBe(false);
|
||||||
|
expect(Value.Check(TaskStatusEnum, 42)).toBe(false);
|
||||||
|
expect(Value.Check(TaskStatusEnum, null)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Nullable helper', () => {
|
||||||
|
it('accepts valid enum values', () => {
|
||||||
|
const NullableScope = Nullable(TaskScopeEnum);
|
||||||
|
expect(Value.Check(NullableScope, 'single')).toBe(true);
|
||||||
|
expect(Value.Check(NullableScope, 'system')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts null', () => {
|
||||||
|
const NullableScope = Nullable(TaskScopeEnum);
|
||||||
|
expect(Value.Check(NullableScope, null)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects undefined and invalid strings', () => {
|
||||||
|
const NullableScope = Nullable(TaskScopeEnum);
|
||||||
|
expect(Value.Check(NullableScope, 'invalid')).toBe(false);
|
||||||
|
expect(Value.Check(NullableScope, undefined)).toBe(false);
|
||||||
|
expect(Value.Check(NullableScope, 42)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Type alias correctness (compile-time)', () => {
|
||||||
|
// These tests verify that the type aliases resolve to the expected union types.
|
||||||
|
// We use type assertions to confirm the types are what we expect.
|
||||||
|
// If the types are wrong, TypeScript would fail to compile this file.
|
||||||
|
|
||||||
|
it('TaskScope type accepts valid values', () => {
|
||||||
|
const scope: TaskScope = 'single';
|
||||||
|
expect(scope).toBe('single');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TaskRisk type accepts valid values', () => {
|
||||||
|
const risk: TaskRisk = 'critical';
|
||||||
|
expect(risk).toBe('critical');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TaskImpact type accepts valid values', () => {
|
||||||
|
const impact: TaskImpact = 'project';
|
||||||
|
expect(impact).toBe('project');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TaskLevel type accepts valid values', () => {
|
||||||
|
const level: TaskLevel = 'implementation';
|
||||||
|
expect(level).toBe('implementation');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TaskPriority type accepts valid values', () => {
|
||||||
|
const priority: TaskPriority = 'high';
|
||||||
|
expect(priority).toBe('high');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TaskStatus type accepts valid values', () => {
|
||||||
|
const status: TaskStatus = 'in-progress';
|
||||||
|
expect(status).toBe('in-progress');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Intentionally import type aliases to verify they exist at compile time
|
||||||
|
type TaskScope = import('../src/schema/enums.js').TaskScope;
|
||||||
|
type TaskRisk = import('../src/schema/enums.js').TaskRisk;
|
||||||
|
type TaskImpact = import('../src/schema/enums.js').TaskImpact;
|
||||||
|
type TaskLevel = import('../src/schema/enums.js').TaskLevel;
|
||||||
|
type TaskPriority = import('../src/schema/enums.js').TaskPriority;
|
||||||
|
type TaskStatus = import('../src/schema/enums.js').TaskStatus;
|
||||||
Reference in New Issue
Block a user