@@ -0,0 +1,118 @@
|
||||
// The notes module's manifest (spec §4.1, §16): the one place it registers anything.
|
||||
//
|
||||
// The example module of the template and the pattern a product's own follows. It is small on
|
||||
// purpose and uses every seam a module has:
|
||||
//
|
||||
// - a contract, a core of pure use cases and a Kysely-bound repository (entities.ts, migrations/);
|
||||
// - the kernel's files module for attachments: the browser uploads through the files procedures
|
||||
// (`files.file.beginUpload`, `putPart`, `completeUpload`; the platform's Blob store; one audit
|
||||
// event and one usage fact per upload, recorded by the files module) and links the file with
|
||||
// `notes.attachment.add`;
|
||||
// - audit events and a usage meter that hang off the module's own outbox events, delivered at least once
|
||||
// with a key derived from the record, so the platform counts each once;
|
||||
// - a module event delivered to people: `notes.NoteCreated`, written by the note's own transaction and
|
||||
// run by the worker's outbox relay, tells a project's other named members through the notifications
|
||||
// kit's inbox (adapters/notifications.ts), in each recipient's language: exactly once on the
|
||||
// Postgres-family relay, which writes it in the delivery's own transaction, and one row per
|
||||
// record-derived key everywhere;
|
||||
// - a feature flag (`notes.attachments`), evaluated through a port;
|
||||
// - messages in English and Lithuanian, a navigation entry and the pages (`ui/pages.tsx`), which
|
||||
// the web shell mounts from `modules/index.ts` with no route file of its own.
|
||||
|
||||
import { defineModule, subscribe } from "@conusai-kit/core"
|
||||
import { repositoryBindings } from "@conusai-kit/data"
|
||||
import { fileDeleted } from "@conusai-kit/files"
|
||||
import { notifyNoteCreated } from "./adapters/notifications.js"
|
||||
import { NOTE_PROCEDURES } from "./adapters/procedures.js"
|
||||
import {
|
||||
ATTACHMENT_ADDED,
|
||||
ATTACHMENT_REMOVED,
|
||||
attachmentAdded,
|
||||
attachmentRemoved,
|
||||
NOTE_CREATED,
|
||||
NOTE_DELETED,
|
||||
NOTE_UPDATED,
|
||||
NOTES_COLLECTIONS,
|
||||
NOTES_CREATED_METER,
|
||||
NOTES_ENTITIES,
|
||||
NOTES_EVENTS,
|
||||
NOTES_FLAG_DEFAULTS,
|
||||
noteCreated,
|
||||
noteDeleted,
|
||||
noteUpdated,
|
||||
} from "./contract/index.js"
|
||||
import {
|
||||
onAttachmentAdded,
|
||||
onAttachmentRemoved,
|
||||
onFileDeleted,
|
||||
onNoteCreated,
|
||||
onNoteDeleted,
|
||||
onNoteUpdated,
|
||||
} from "./core/subscribers.js"
|
||||
import { NOTES_GUIDES } from "./guides/index.js" // kit:guides
|
||||
import { en, lt } from "./i18n/index.js"
|
||||
|
||||
const tables = repositoryBindings(...NOTES_ENTITIES)
|
||||
|
||||
export const notesModule = defineModule({
|
||||
id: "notes",
|
||||
version: "0.1.0",
|
||||
kit: "^0.1.0",
|
||||
requires: ["tenancy", "files", "notifications"],
|
||||
permissions: {
|
||||
"notes.note.read": "viewer",
|
||||
"notes.note.write": "editor",
|
||||
"notes.note.delete": "editor",
|
||||
"notes.attachment.write": "editor",
|
||||
},
|
||||
procedures: NOTE_PROCEDURES,
|
||||
// The note list is live (TS6.1): a change of notes_note reaches every member's open page.
|
||||
collections: [...NOTES_COLLECTIONS],
|
||||
repositories: { postgres: tables, mariadb: tables, mongodb: tables, convex: tables },
|
||||
// Forward-only migrations, one numbered file per change per dialect (spec §5.3: the dialect
|
||||
// differences are explicit, not generated), run by the migrate job after the app's own.
|
||||
migrations: {
|
||||
postgres: "migrations",
|
||||
mariadb: "adapters/kysely/mysql/migrations",
|
||||
mongodb: "adapters/mongo/migrations",
|
||||
},
|
||||
events: {
|
||||
publishes: NOTES_EVENTS,
|
||||
subscribes: [
|
||||
// One subscription per event: the relay records a delivery per (record, module), so a second
|
||||
// subscription of this module to the same event would never run. The audit event and the usage fact
|
||||
// first, then the project's other members are told (adapters/notifications.ts).
|
||||
subscribe(noteCreated, async (event, ctx) => {
|
||||
await onNoteCreated(event, ctx)
|
||||
await notifyNoteCreated(event, ctx)
|
||||
}),
|
||||
subscribe(noteUpdated, onNoteUpdated),
|
||||
subscribe(noteDeleted, onNoteDeleted),
|
||||
subscribe(attachmentAdded, onAttachmentAdded),
|
||||
subscribe(attachmentRemoved, onAttachmentRemoved),
|
||||
// A deleted file takes its links with it.
|
||||
subscribe(fileDeleted, onFileDeleted),
|
||||
],
|
||||
},
|
||||
audit: [NOTE_CREATED, NOTE_UPDATED, NOTE_DELETED, ATTACHMENT_ADDED, ATTACHMENT_REMOVED],
|
||||
usage: { meters: [NOTES_CREATED_METER] },
|
||||
flags: Object.fromEntries(Object.entries(NOTES_FLAG_DEFAULTS).map(([flag, value]) => [flag, { default: value }])),
|
||||
// The project-level entry the org home lists next to each project; `notes` is the segment the
|
||||
// web shell's module mount routes to `notesPages` (ui/pages.tsx).
|
||||
// The workspace entry is linked above every page of the workspace, to `workspacePages` of modules/pages.ts.
|
||||
ui: {
|
||||
nav: [
|
||||
{ scope: "project", label: "notes.title", href: "notes", permission: "notes.note.read" },
|
||||
{
|
||||
scope: "workspace",
|
||||
label: "notes.workspace.title",
|
||||
href: "notes",
|
||||
permission: "notes.note.read",
|
||||
icon: "file",
|
||||
},
|
||||
],
|
||||
},
|
||||
// Both languages, as objects: kit-i18n merges them from the registry (AT-D23).
|
||||
i18n: { en, lt },
|
||||
extends: [...NOTES_GUIDES], // kit:guides -- the code tour, checklist and survey of guides/
|
||||
})
|
||||
Reference in New Issue
Block a user