diff --git a/packages/js-runtime/test/fixtures/schema-transform/recipe-with-types.expected.tsx b/packages/js-runtime/test/fixtures/schema-transform/recipe-with-types.expected.tsx index 46c9db9e5..e16cec786 100644 --- a/packages/js-runtime/test/fixtures/schema-transform/recipe-with-types.expected.tsx +++ b/packages/js-runtime/test/fixtures/schema-transform/recipe-with-types.expected.tsx @@ -1,173 +1,127 @@ /// -import { recipe, handler, h, UI, NAME, str, Cell, derive, JSONSchema } from "commontools"; -// Define types using TypeScript interfaces -interface TodoItem { - id: string; - text: string; - completed: boolean; - createdAt: Date; +import { recipe, h, UI, NAME, Cell, Default, handler, JSONSchema } from "commontools"; +interface Item { + text: Default; } -interface TodoInput { - todos: Cell; +interface InputSchemaInterface { + title: Default; + items: Default; } -interface TodoOutput extends TodoInput { - completedCount: number; - pendingCount: number; +interface OutputSchemaInterface extends InputSchemaInterface { + items_count: number; } -interface AddTodoEvent { - text: string; -} -interface ToggleTodoEvent { - id: string; -} -// Transform to schemas at compile time +type InputEventType = { + detail: { + message: string; + }; +}; const inputSchema = { type: "object", properties: { - todos: { + title: { + type: "string", + default: "untitled" + }, + items: { type: "array", items: { type: "object", properties: { - id: { - type: "string" - }, text: { - type: "string" - }, - completed: { - type: "boolean" - }, - createdAt: { type: "string", - format: "date-time" + default: "" } }, - required: ["id", "text", "completed", "createdAt"] + required: ["text"] }, - asCell: true + default: [] } }, - required: ["todos"], - default: { - todos: [] - } + required: ["title", "items"] } as const satisfies JSONSchema; const outputSchema = { type: "object", properties: { - completedCount: { + items_count: { type: "number" }, - pendingCount: { - type: "number" + title: { + type: "string", + default: "untitled" }, - todos: { + items: { type: "array", items: { type: "object", properties: { - id: { - type: "string" - }, text: { - type: "string" - }, - completed: { - type: "boolean" - }, - createdAt: { type: "string", - format: "date-time" + default: "" } }, - required: ["id", "text", "completed", "createdAt"] + required: ["text"] }, - asCell: true + default: [] } }, - required: ["completedCount", "pendingCount", "todos"] + required: ["items_count", "title", "items"] } as const satisfies JSONSchema; -const addTodoSchema = { +// Handler that logs the message event +const addItem = handler({ type: "object", properties: { - text: { - type: "string" + detail: { + type: "object", + properties: { + message: { + type: "string" + } + }, + required: ["message"] } }, - required: ["text"], - title: "Add Todo", - description: "Add a new todo item", - examples: [{ - text: "Buy groceries" - }] -} as const satisfies JSONSchema; -const toggleTodoSchema = { + required: ["detail"] +} as const satisfies JSONSchema, { type: "object", properties: { - id: { - type: "string" + items: { + type: "array", + items: { + type: "object", + properties: { + text: { + type: "string", + default: "" + } + }, + required: ["text"] + }, + asCell: true } }, - required: ["id"], - title: "Toggle Todo", - description: "Toggle the completion status of a todo" -} as const satisfies JSONSchema; -// Handlers with full type safety -const addTodo = handler(addTodoSchema, inputSchema, (event: AddTodoEvent, state: TodoInput) => { - state.todos.push({ - id: Date.now().toString(), - text: event.text, - completed: false, - createdAt: new Date() - }); + required: ["items"] +} as const satisfies JSONSchema, (event: InputEventType, { items }: { + items: Cell; +}) => { + items.push({ text: event.detail.message }); }); -const toggleTodo = handler(toggleTodoSchema, inputSchema, (event: ToggleTodoEvent, state: TodoInput) => { - const todos = state.todos.get(); - const todo = todos.find((t: any) => t.id === event.id); - if (todo) { - todo.completed = !todo.completed; - state.todos.set(todos); - } -}); -export default recipe(inputSchema, outputSchema, ({ todos }) => { - const completedCount = derive(todos, (todos: TodoItem[]) => todos.filter((t: TodoItem) => t.completed).length); - const pendingCount = derive(todos, (todos: TodoItem[]) => todos.filter((t: TodoItem) => !t.completed).length); +export default recipe(inputSchema, outputSchema, ({ title, items }) => { + const items_count = items.length; return { - [NAME]: str `Todo List (${pendingCount} pending)`, + [NAME]: title, [UI]: (
-
{ - e.preventDefault(); - const input = e.target.text; - if (input.value) { - addTodo({ text: input.value }); - input.value = ''; - } - }}> - - -
- +

{title}

+

Basic recipe

+

Items count: {items_count}

    - {todos.map((todo: TodoItem) => (
  • - -
  • ))} + {items.map((item: Item, index: number) => (
  • {item.text}
  • ))}
- -
- Completed: {completedCount} | Pending: {pendingCount} -
+
), - todos, - completedCount, - pendingCount + title, + items, + items_count }; }); diff --git a/packages/js-runtime/test/fixtures/schema-transform/recipe-with-types.input.tsx b/packages/js-runtime/test/fixtures/schema-transform/recipe-with-types.input.tsx index 148540425..9efb573a0 100644 --- a/packages/js-runtime/test/fixtures/schema-transform/recipe-with-types.input.tsx +++ b/packages/js-runtime/test/fixtures/schema-transform/recipe-with-types.input.tsx @@ -1,127 +1,65 @@ /// -import { recipe, handler, toSchema, h, UI, NAME, str, Cell, derive } from "commontools"; +import { recipe, h, UI, NAME, toSchema, Cell, Default, handler } from "commontools"; -// Define types using TypeScript interfaces -interface TodoItem { - id: string; - text: string; - completed: boolean; - createdAt: Date; +interface Item { + text: Default; } -interface TodoInput { - todos: Cell; +interface InputSchemaInterface { + title: Default; + items: Default; } -interface TodoOutput extends TodoInput { - completedCount: number; - pendingCount: number; +interface OutputSchemaInterface extends InputSchemaInterface { + items_count: number; } -interface AddTodoEvent { - text: string; -} - -interface ToggleTodoEvent { - id: string; -} - -// Transform to schemas at compile time -const inputSchema = toSchema({ - default: { todos: [] } -}); - -const outputSchema = toSchema(); - -const addTodoSchema = toSchema({ - title: "Add Todo", - description: "Add a new todo item", - examples: [{ text: "Buy groceries" }] -}); - -const toggleTodoSchema = toSchema({ - title: "Toggle Todo", - description: "Toggle the completion status of a todo" -}); - -// Handlers with full type safety -const addTodo = handler( - addTodoSchema, - inputSchema, - (event: AddTodoEvent, state: TodoInput) => { - state.todos.push({ - id: Date.now().toString(), - text: event.text, - completed: false, - createdAt: new Date() - }); +type InputEventType = { + detail: { + message: string } -); +}; + +const inputSchema = toSchema(); +const outputSchema = toSchema(); -const toggleTodo = handler( - toggleTodoSchema, - inputSchema, - (event: ToggleTodoEvent, state: TodoInput) => { - const todos = state.todos.get(); - const todo = todos.find((t: any) => t.id === event.id); - if (todo) { - todo.completed = !todo.completed; - state.todos.set(todos); - } +// Handler that logs the message event +const addItem = handler +// < +// { detail: { message: string } }, +// { items: Item[] } +// > +( + (event: InputEventType, { items }: {items: Cell}) => { + items.push({text: event.detail.message}); } ); -export default recipe(inputSchema, outputSchema, ({ todos }) => { - const completedCount = derive(todos, (todos: TodoItem[]) => - todos.filter((t: TodoItem) => t.completed).length - ); +export default recipe(inputSchema, outputSchema, ({ title, items }) => { + const items_count = items.length; - const pendingCount = derive(todos, (todos: TodoItem[]) => - todos.filter((t: TodoItem) => !t.completed).length - ); - return { - [NAME]: str`Todo List (${pendingCount} pending)`, + [NAME]: title, [UI]: (
-
{ - e.preventDefault(); - const input = e.target.text; - if (input.value) { - addTodo({ text: input.value }); - input.value = ''; - } - }}> - - -
- +

{title}

+

Basic recipe

+

Items count: {items_count}

    - {todos.map((todo: TodoItem) => ( -
  • - -
  • + {items.map((item: Item, index: number) => ( +
  • {item.text}
  • ))}
- -
- Completed: {completedCount} | Pending: {pendingCount} -
+
), - todos, - completedCount, - pendingCount + title, + items, + items_count }; }); diff --git a/packages/js-runtime/test/fixtures/schema-transform/type-to-schema.expected.ts b/packages/js-runtime/test/fixtures/schema-transform/type-to-schema.expected.ts index c7d813ee9..57c7ab898 100644 --- a/packages/js-runtime/test/fixtures/schema-transform/type-to-schema.expected.ts +++ b/packages/js-runtime/test/fixtures/schema-transform/type-to-schema.expected.ts @@ -1,5 +1,5 @@ /// -import { recipe, handler, Cell, Stream, JSONSchema } from "commontools"; +import { recipe, NAME, handler, Cell, Stream, JSONSchema } from "commontools"; // Define types using TypeScript - more compact! interface UpdaterInput { newValues: string[]; @@ -125,3 +125,9 @@ const userSchema = { required: ["name", "age", "tags", "metadata"], description: "A user in the system" } as const satisfies JSONSchema; +export default recipe("test", (state) => { + return { + [NAME]: "test", + }; +}); + diff --git a/packages/js-runtime/test/fixtures/schema-transform/type-to-schema.input.ts b/packages/js-runtime/test/fixtures/schema-transform/type-to-schema.input.ts index 84d22dd48..3f42ef6bf 100644 --- a/packages/js-runtime/test/fixtures/schema-transform/type-to-schema.input.ts +++ b/packages/js-runtime/test/fixtures/schema-transform/type-to-schema.input.ts @@ -1,5 +1,5 @@ /// -import { recipe, handler, toSchema, Cell, Stream } from "commontools"; +import { recipe, NAME, handler, toSchema, Cell, Stream } from "commontools"; // Define types using TypeScript - more compact! interface UpdaterInput { @@ -54,4 +54,10 @@ interface User { const userSchema = toSchema({ description: "A user in the system", -}); \ No newline at end of file +}); + +export default recipe("test", (state) => { + return { + [NAME]: "test", + }; +});