// Creates a post. The `block`-action moderation rule (app.config.ts) checks // title + body BEFORE this handler runs; banned content fails typed // `ContentRejected` and the write never commits. There's no moderation code // in the handler. Declaring `error: ContentRejected` surfaces it to the client // typed. `ContentRejected` comes from the browser-safe `/errors` subpath. import { defineMutation } from '@voltro/protocol' import { ContentRejected } from '@voltro/plugin-moderation/errors' import { Schema } from 'effect' export const createPost = defineMutation({ name: 'posts.create', target: { table: 'posts', op: 'insert' }, // Content moderation is not access control, and this template is the place // to be exact about that: the `block` rule inspects WHAT is being written and // can reject it; `guards:` / `openAccess:` decide WHO may write at all. The // moderation interceptor lets an authorized troll through and stops an // unauthorized poet — neither substitutes for the other. // // Open because no auth strategy and no rbac ship here: every caller is an // anonymous Subject with no scopes, so a scope guard would deny all of them. // The write carries only caller-supplied text into the request's tenant. openAccess: 'inserts caller-supplied text into the request\'s tenant, after the `block` moderation ' + 'rule has vetted it. Moderation judges the CONTENT, not the caller — it is not an ' + 'access decision, which is why this line exists beside it.', input: Schema.Struct({ title: Schema.NonEmptyString, body: Schema.String, }), output: Schema.Struct({ id: Schema.String, title: Schema.String, body: Schema.String, tenantId: Schema.String, }), error: ContentRejected, })