// The procedure contracts of the notes module: schemas and names, no handler and no port. // // An attachment is uploaded through the files module's own upload procedures (`files.file.beginUpload`, // `files.file.putPart` and `files.file.completeUpload`: the bytes go to the platform's Blob store, // which is where the usage fact and the audit event of an upload are recorded), and then linked to a // note by `notes.attachment.add`. This module never takes bytes. import { procedure } from "@conusai-kit/core" import { z } from "zod" const inProject = { org: z.string(), workspace: z.string(), project: z.string() } const title = z.string().min(1).max(200) const body = z.string().max(20_000) export const noteViewSchema = z.object({ noteId: z.string(), title: z.string(), body: z.string(), /** What `notes.note.update` is called with as `expectedVersion`. */ version: z.number().int(), createdBy: z.string(), createdAt: z.string(), updatedAt: z.string(), attachmentCount: z.number().int(), }) export type NoteView = z.output export const attachmentViewSchema = z.object({ attachmentId: z.string(), fileId: z.string(), filename: z.string(), contentType: z.string(), size: z.number().int(), attachedAt: z.string(), }) export type AttachmentView = z.output export const noteList = procedure("notes.note.list") .kind("read") .scope("project") .permission("notes.note.read") .tool() .input(z.object({ ...inProject, cursor: z.string().optional(), limit: z.number().int().min(1).max(200).optional() })) .output(z.object({ notes: z.array(noteViewSchema), cursor: z.string().optional(), hasMore: z.boolean() })) .contract() export const noteGet = procedure("notes.note.get") .kind("read") .scope("project") .permission("notes.note.read") .input(z.object({ ...inProject, noteId: z.string() })) .output( z.object({ note: noteViewSchema, attachments: z.array(attachmentViewSchema), /** The feature flag's answer for this project, so a page does not offer what a call would refuse. */ attachmentsEnabled: z.boolean(), }), ) .contract() // No `.audit()` and no `.usage()` on the write contracts: this module's audit events and usage // facts are written by the subscribers to its outbox records (core/subscribers.ts), which is the // exactly-once path — the transport's own `.audit()`/`.usage()` recording would write each one a // second time (and under a different customer key). export const noteCreate = procedure("notes.note.create") .kind("write") .scope("project") .permission("notes.note.write") .input(z.object({ ...inProject, title, body })) .output(noteViewSchema) .contract() export const noteUpdate = procedure("notes.note.update") .kind("write") .scope("project") .permission("notes.note.write") .input(z.object({ ...inProject, noteId: z.string(), title, body, expectedVersion: z.number().int().min(1) })) .output(noteViewSchema) .contract() export const noteDelete = procedure("notes.note.delete") .kind("write") .scope("project") .permission("notes.note.delete") .input(z.object({ ...inProject, noteId: z.string() })) .contract() export const attachmentAdd = procedure("notes.attachment.add") .kind("write") .scope("project") .permission("notes.attachment.write") .input(z.object({ ...inProject, noteId: z.string(), fileId: z.string() })) .output(attachmentViewSchema) .contract() export const attachmentRemove = procedure("notes.attachment.remove") .kind("write") .scope("project") .permission("notes.attachment.write") .input(z.object({ ...inProject, noteId: z.string(), attachmentId: z.string() })) .contract()