From bea553c2b0149aab4e0a51900f94816f1f6bb8ab Mon Sep 17 00:00:00 2001 From: "glm-5.1" Date: Thu, 21 May 2026 20:51:25 +0000 Subject: [PATCH] Define TypeBox enum schemas (CallStatus, NodeStatus, OperationType, EdgeType) with Nullable helper --- src/schema/enums.ts | 54 +++++++++++++++++++- src/schema/index.ts | 12 ++++- test/schema/enums.test.ts | 105 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 164 insertions(+), 7 deletions(-) diff --git a/src/schema/enums.ts b/src/schema/enums.ts index 8cec2e9..3816bd9 100644 --- a/src/schema/enums.ts +++ b/src/schema/enums.ts @@ -1 +1,53 @@ -export {}; \ No newline at end of file +import { Type, type Static, type TSchema } from "@alkdev/typebox"; + +/** + * Call lifecycle states. + * + * Transitions: + * - `pending → running`: Handler starts executing + * - `running → completed`: `call.responded` + `call.completed` received + * - `running → failed`: `call.error` received + * - `pending → aborted`: `call.aborted` received before handler started + * - `running → aborted`: `call.aborted` received during execution + * + * `completed`, `failed`, and `aborted` are terminal states. + */ +export const CallStatusEnum = Type.Union([ + Type.Literal("pending"), + Type.Literal("running"), + Type.Literal("completed"), + Type.Literal("failed"), + Type.Literal("aborted"), +]); +export type CallStatus = Static; + +export const NodeStatusEnum = Type.Union([ + Type.Literal("idle"), + Type.Literal("waiting"), + Type.Literal("ready"), + Type.Literal("running"), + Type.Literal("completed"), + Type.Literal("failed"), + Type.Literal("skipped"), + Type.Literal("aborted"), +]); +export type NodeStatus = Static; + +export const OperationTypeEnum = Type.Union([ + Type.Literal("query"), + Type.Literal("mutation"), + Type.Literal("subscription"), +]); +export type OperationType = Static; + +export const EdgeTypeEnum = Type.Union([ + Type.Literal("triggered"), + Type.Literal("depends_on"), + Type.Literal("typed"), + Type.Literal("sequential"), + Type.Literal("conditional"), +]); +export type EdgeType = Static; + +export const Nullable = (schema: T) => + Type.Union([schema, Type.Null()]); diff --git a/src/schema/index.ts b/src/schema/index.ts index 8cec2e9..acc9d94 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -1 +1,11 @@ -export {}; \ No newline at end of file +export { + CallStatusEnum, + type CallStatus, + NodeStatusEnum, + type NodeStatus, + OperationTypeEnum, + type OperationType, + EdgeTypeEnum, + type EdgeType, + Nullable, +} from "./enums.js"; diff --git a/test/schema/enums.test.ts b/test/schema/enums.test.ts index eb3d102..e16f6b3 100644 --- a/test/schema/enums.test.ts +++ b/test/schema/enums.test.ts @@ -1,7 +1,102 @@ -import { describe, it, expect } from 'vitest'; +import { describe, it, expect } from "vitest"; +import { Value } from "@alkdev/typebox/value"; +import { + CallStatusEnum, + NodeStatusEnum, + OperationTypeEnum, + EdgeTypeEnum, + Nullable, + type CallStatus, + type NodeStatus, + type OperationType, + type EdgeType, +} from "../../src/schema/enums"; -describe('schema enums', () => { - it('placeholder', () => { - expect(true).toBe(true); +describe("CallStatusEnum", () => { + const valid: CallStatus[] = [ + "pending", + "running", + "completed", + "failed", + "aborted", + ]; + it.each(valid)("accepts %s", (v) => { + expect(Value.Check(CallStatusEnum, v)).toBe(true); }); -}); \ No newline at end of file + + it("rejects invalid values", () => { + expect(Value.Check(CallStatusEnum, "idle")).toBe(false); + expect(Value.Check(CallStatusEnum, "unknown")).toBe(false); + expect(Value.Check(CallStatusEnum, 42)).toBe(false); + expect(Value.Check(CallStatusEnum, null)).toBe(false); + }); +}); + +describe("NodeStatusEnum", () => { + const valid: NodeStatus[] = [ + "idle", + "waiting", + "ready", + "running", + "completed", + "failed", + "skipped", + "aborted", + ]; + it.each(valid)("accepts %s", (v) => { + expect(Value.Check(NodeStatusEnum, v)).toBe(true); + }); + + it("rejects invalid values", () => { + expect(Value.Check(NodeStatusEnum, "pending")).toBe(false); + expect(Value.Check(NodeStatusEnum, "unknown")).toBe(false); + expect(Value.Check(NodeStatusEnum, 1)).toBe(false); + }); +}); + +describe("OperationTypeEnum", () => { + const valid: OperationType[] = ["query", "mutation", "subscription"]; + it.each(valid)("accepts %s", (v) => { + expect(Value.Check(OperationTypeEnum, v)).toBe(true); + }); + + it("rejects invalid values", () => { + expect(Value.Check(OperationTypeEnum, "fetch")).toBe(false); + expect(Value.Check(OperationTypeEnum, 0)).toBe(false); + }); +}); + +describe("EdgeTypeEnum", () => { + const valid: EdgeType[] = [ + "triggered", + "depends_on", + "typed", + "sequential", + "conditional", + ]; + it.each(valid)("accepts %s", (v) => { + expect(Value.Check(EdgeTypeEnum, v)).toBe(true); + }); + + it("rejects invalid values", () => { + expect(Value.Check(EdgeTypeEnum, "parent")).toBe(false); + expect(Value.Check(EdgeTypeEnum, false)).toBe(false); + }); +}); + +describe("Nullable", () => { + it("accepts null", () => { + const schema = Nullable(EdgeTypeEnum); + expect(Value.Check(schema, null)).toBe(true); + }); + + it("accepts valid enum values", () => { + const schema = Nullable(EdgeTypeEnum); + expect(Value.Check(schema, "typed")).toBe(true); + }); + + it("rejects invalid non-null values", () => { + const schema = Nullable(EdgeTypeEnum); + expect(Value.Check(schema, "invalid")).toBe(false); + }); +});