SaaS Factory - Interaction ShellEntity: Post - Action: listPublished - Type: queryDemo proof surface - under active polish
Query Terminal
GET /api/demo/posts
published:
Select a filter and run the query to exercise the generated endpoint.
What this exercises
Generated route:
GET /api/post/actions/listPublished
PostQuerySchema.parse(searchParams)
-> service.listPublished(query)
-> prisma.post.findMany(where)
[gap] mock only - no real Prisma connection
[gap] explainAction omits filter fields from Query block
[gap] no Zod coercion for boolean URL params
Proof narrative
The route, service, and Zod schema shown on this page are generated artifacts. They are intended to come from the same compiler decision path, so a change to the spec must move all three together. The proof ladder verifies generated behavior rather than trusting screenshots or handwritten claims. This demo is a proof surface, not a production-readiness claim.
explainAction('Post', 'listPublished')
=== Action: Post.listPublished ===

Type:      query
Target:    Post
Input (1):
  published       boolean     optional

Query: deterministic read — no state mutation, no transitions.

Semantic fingerprint:
  listPublished:query:Post:[{"name":"published","type":"boolean","required":false}]:null

Affected generators:  api, prisma, services, zod

Lowering summary:
  Artifacts: services/PostService.ts, app/api/post/[id]/route.ts, app/api/post/actions/listPublished/route.ts, app/api/post/route.ts

Invalidation signal:
  (no diff provided)

===
explainEntity('Post')
=== Entity: Post ===

Fields (4):
  id              string      required
  title           string      required
  content         string      optional
  published       boolean     required

Relations (0):
  (none)

Actions (1):
  listPublished   query       target: Post  [query]

Semantic fingerprint inputs:
  Fields:    name + {type, enumValues} per field
  Relations: name:targetEntity:cardinality per relation
  Actions:   name:type:target:input:transition per action
  (Entity contributes its stable JSON to the model fingerprint)

Affected generators:  api, prisma, services, zod

Lowering summary:
  Entity artifacts (4): app/api/post/[id]/route.ts, app/api/post/actions/listPublished/route.ts, app/api/post/route.ts, services/PostService.ts
  Shared artifacts:  lib/prisma.ts, prisma/schema.prisma, schemas/zod.ts, services/index.ts

Invalidation signals:
  (no diff provided)

===
Generated - app/api/post/actions/listPublished/route.ts
import { PostService } from "@/services";
import { PostQuerySchema } from "@/schemas/zod";
import { NextRequest } from "next/server";

const service = new PostService();

// action:query on Post — deterministic read; no mutation
export async function GET(req: NextRequest) {
  const query = PostQuerySchema.parse(Object.fromEntries(req.nextUrl.searchParams));
  const result = await service.listPublished(query);
  return Response.json(result);
}
Generated - services/PostService.ts
import { prisma } from "@/lib/prisma";
import type { Post } from "@/schemas/zod";

export class PostService {
  async findMany(): Promise<Post[]> {
    return prisma.post.findMany();
  }

  async findUnique(id: string): Promise<Post | null> {
    return prisma.post.findUnique({ where: { id } });
  }

  async create(data: Omit<Post, 'id'>): Promise<Post> {
    return prisma.post.create({ data });
  }

  async update(id: string, data: Partial<Omit<Post, 'id'>>): Promise<Post> {
    return prisma.post.update({ where: { id }, data });
  }

  async delete(id: string): Promise<Post> {
    return prisma.post.delete({ where: { id } });
  }

  // action:query — deterministic read; side-effect free
  async listPublished(input?: { published?: boolean }): Promise<Post[]> {
    return prisma.post.findMany({ where: { published: input?.published } });
  }
}
Generated - schemas/zod.ts (PostQuerySchema)
import { z } from "zod";

export const PostSchema = z.object({
  id: z.string(),
  title: z.string(),
  content: z.string().optional(),
  published: z.boolean()
});
export type Post = z.infer<typeof PostSchema>;

export const PostQuerySchema = z.object({
  published: z.preprocess(v => v === 'true' ? true : v === 'false' ? false : v, z.boolean()).optional()
});
export type PostQuery = z.infer<typeof PostQuerySchema>;