rspace-online/modules/rnotes/schemas.ts

125 lines
2.5 KiB
TypeScript

/**
* rNotes Automerge document schemas.
*
* Granularity: one Automerge document per notebook.
* DocId format: {space}:notes:notebooks:{notebookId}
*
* The shape matches the PG→Automerge migration adapter
* (server/local-first/migration/pg-to-automerge.ts:notesMigration)
* and the client-side NotebookDoc type in folk-notes-app.ts.
*/
import type { DocSchema } from '../../shared/local-first/document';
// ── Document types ──
export interface NoteItem {
id: string;
notebookId: string;
authorId: string | null;
title: string;
content: string;
contentPlain: string;
type: 'NOTE' | 'CLIP' | 'BOOKMARK' | 'CODE' | 'IMAGE' | 'FILE' | 'AUDIO';
url: string | null;
language: string | null;
fileUrl: string | null;
mimeType: string | null;
fileSize: number | null;
duration: number | null;
isPinned: boolean;
sortOrder: number;
tags: string[];
createdAt: number;
updatedAt: number;
}
export interface NotebookMeta {
id: string;
title: string;
slug: string;
description: string;
coverColor: string;
isPublic: boolean;
createdAt: number;
updatedAt: number;
}
export interface NotebookDoc {
meta: {
module: string;
collection: string;
version: number;
spaceSlug: string;
createdAt: number;
};
notebook: NotebookMeta;
items: Record<string, NoteItem>;
}
// ── Schema registration ──
export const notebookSchema: DocSchema<NotebookDoc> = {
module: 'notes',
collection: 'notebooks',
version: 1,
init: (): NotebookDoc => ({
meta: {
module: 'notes',
collection: 'notebooks',
version: 1,
spaceSlug: '',
createdAt: Date.now(),
},
notebook: {
id: '',
title: 'Untitled Notebook',
slug: '',
description: '',
coverColor: '#3b82f6',
isPublic: false,
createdAt: Date.now(),
updatedAt: Date.now(),
},
items: {},
}),
};
// ── Helpers ──
/** Generate a docId for a notebook. */
export function notebookDocId(space: string, notebookId: string) {
return `${space}:notes:notebooks:${notebookId}` as const;
}
/** Create a fresh NoteItem with defaults. */
export function createNoteItem(
id: string,
notebookId: string,
title: string,
opts: Partial<NoteItem> = {},
): NoteItem {
const now = Date.now();
return {
id,
notebookId,
authorId: null,
title,
content: '',
contentPlain: '',
type: 'NOTE',
url: null,
language: null,
fileUrl: null,
mimeType: null,
fileSize: null,
duration: null,
isPinned: false,
sortOrder: 0,
tags: [],
createdAt: now,
updatedAt: now,
...opts,
};
}