All checks were successful
CD / Build and push images (push) Successful in 1m52s
CI / Lint, typecheck, test (push) Successful in 1m34s
CI / Auth e2e pack (push) Successful in 1m44s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m7s
CD / Promote to Int (push) Successful in 10s
Prisma models `pages`/`page_updates`/`page_content_cache` per data-model.md. Endpoints: POST /ponds/:id/pages (title -> empty Yjs doc state, seeded via y-prosemirror), GET /pages/:id (meta + base64 state), PUT /pages/:id/state (client-encoded Yjs state, rejected above the 5 MiB operations.md limit or if it doesn't decode into a valid document for the schema), PATCH /pages/:id (title/slug — explicit slug changes validate uniqueness per pond, title-only renames keep the slug), DELETE (soft). Access follows InterimAccessService via the page's pond, same 404-not-403 interim rule as ponds. State saves decode the Yjs update with yjs + y-prosemirror and run it through the #24 shared derivation functions (docToPlainText/ docToMarkdown/docToHtml/extractOutline) to refresh page_content_cache. The Yjs XmlFragment name ("default") and the derivation call are factored so the collab server's persistence hooks (#35) can reuse both. Raised the API's JSON body limit to 8 MiB (main.ts and the e2e test app) to fit base64-encoded page state. Closes #23
59 lines
1.5 KiB
TypeScript
59 lines
1.5 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
/**
|
|
* Page schemas and views shared between api and web (issue #23). A page
|
|
* carries a Yjs document from day one (ADR 0003); in M2 its state is
|
|
* saved wholesale over REST as a base64 string.
|
|
*/
|
|
|
|
export const pageTitleSchema = z
|
|
.string()
|
|
.trim()
|
|
.min(1, 'validation.required')
|
|
.max(200, 'validation.tooLong');
|
|
|
|
export const pageSlugSchema = z
|
|
.string()
|
|
.trim()
|
|
.min(1, 'validation.required')
|
|
.max(60, 'validation.tooLong');
|
|
|
|
export const createPageInputSchema = z.object({
|
|
title: pageTitleSchema,
|
|
});
|
|
export type CreatePageInput = z.infer<typeof createPageInputSchema>;
|
|
|
|
export const updatePageInputSchema = z
|
|
.object({
|
|
title: pageTitleSchema,
|
|
slug: pageSlugSchema,
|
|
})
|
|
.partial();
|
|
export type UpdatePageInput = z.infer<typeof updatePageInputSchema>;
|
|
|
|
export const savePageStateInputSchema = z.object({
|
|
/** Base64-encoded Yjs state (`Y.encodeStateAsUpdate`). */
|
|
state: z.string().min(1, 'validation.required'),
|
|
});
|
|
export type SavePageStateInput = z.infer<typeof savePageStateInputSchema>;
|
|
|
|
/** Max Yjs document size (operations.md §Limits) — a fixed operational
|
|
* ceiling, not a per-pond/user quota. */
|
|
export const MAX_PAGE_DOCUMENT_BYTES = 5 * 1024 * 1024;
|
|
|
|
export interface PageView {
|
|
id: string;
|
|
pondId: string;
|
|
title: string;
|
|
slug: string;
|
|
sortKey: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
deletedAt: string | null;
|
|
}
|
|
|
|
/** What `GET /pages/:id` returns: page meta plus its base64 Yjs state. */
|
|
export interface PageStateView extends PageView {
|
|
state: string;
|
|
}
|