TypeScript/Bun project scaffold
- Stack: Bun, Hono, Drizzle ORM, Zod, Handlebars, Pino - Models: ticket, queue, transaction, scrip, template, custom_field, user, lifecycle - Scrip engine: prepare/commit two-phase dispatch, template rendering, mock actions - Lifecycle validator: state machine transition validation with wildcard support - Routes: health, tickets (full CRUD + preview + transactions), queues, scrips, custom-fields, lifecycles - Middleware: Pino logging, error handler - Database: Drizzle ORM schema + initial migration (10 tables) - Type-check: passes (tsc --noEmit, zero errors)
This commit is contained in:
73
src/lifecycle/validator.ts
Normal file
73
src/lifecycle/validator.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
export interface LifecycleDefinition {
|
||||
statuses: {
|
||||
initial: string[];
|
||||
active: string[];
|
||||
inactive: string[];
|
||||
};
|
||||
transitions: Record<string, string[]>;
|
||||
}
|
||||
|
||||
export interface ValidationResult {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export class LifecycleValidator {
|
||||
validateTransition(
|
||||
lifecycleDef: LifecycleDefinition,
|
||||
fromStatus: string,
|
||||
toStatus: string,
|
||||
): ValidationResult {
|
||||
const allStatuses = [
|
||||
...lifecycleDef.statuses.initial,
|
||||
...lifecycleDef.statuses.active,
|
||||
...lifecycleDef.statuses.inactive,
|
||||
];
|
||||
|
||||
if (!allStatuses.includes(toStatus)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Status "${toStatus}" is not defined in the lifecycle`,
|
||||
};
|
||||
}
|
||||
|
||||
// Check for allowed transitions
|
||||
const allowedTransitions = this.getAllowedTransitions(lifecycleDef, fromStatus);
|
||||
|
||||
if (allowedTransitions.includes(toStatus)) {
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
// Also handle wildcard "*" -> any transition
|
||||
const wildcardTransitions = this.getAllowedTransitions(lifecycleDef, '*');
|
||||
if (wildcardTransitions.includes(toStatus)) {
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
error: `Transition from "${fromStatus}" to "${toStatus}" is not allowed`,
|
||||
};
|
||||
}
|
||||
|
||||
isResolvedStatus(lifecycleDef: LifecycleDefinition, status: string): boolean {
|
||||
return lifecycleDef.statuses.inactive.includes(status);
|
||||
}
|
||||
|
||||
private getAllowedTransitions(
|
||||
lifecycleDef: LifecycleDefinition,
|
||||
fromStatus: string,
|
||||
): string[] {
|
||||
// Direct transition
|
||||
if (lifecycleDef.transitions[fromStatus]) {
|
||||
return lifecycleDef.transitions[fromStatus]!;
|
||||
}
|
||||
|
||||
// Wildcard transitions
|
||||
if (lifecycleDef.transitions['*']) {
|
||||
return lifecycleDef.transitions['*']!;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user