Typed registry
The generated .adonisjs/collaboration/documents.ts — how the union is derived from disk, when it refreshes, what it looks like when empty, and how to use it on both sides.
There are two ways to get a checkable document-name type, and which one you get depends on how you declared your documents.
Declared inline in config? Use the config, not the registry
The generated registry below indexes the app/collaboration/documents/ directory, which only
exists if you ran node ace make:collab-document. If you declared your documents the other
supported way — inline under documents in config/collaboration.ts — that directory is not there,
the init hook returns early and nothing is generated. Derive the union from the config instead:
import type { InferCollabDocumentNames } from '@adonis-agora/collaboration'
import collaborationConfig from '#config/collaboration'
export type CollabDocumentName = InferCollabDocumentNames<typeof collaborationConfig>
// → 'researches/:id/writing' | 'whiteboards/:id'This works because defineConfig is generic and returns your literal config — a widened return
type would erase the keys, which is exactly why there was no bridge from config.documents to a
type before 0.10.0.
To check a resolved name and not just the pattern, run it through CollabDocumentNameFor:
import type { CollabDocumentNameFor } from '@adonis-agora/collaboration'
type Writing = CollabDocumentNameFor<'researches/:id/writing'>
// → `researches/${string}/writing`
const ok: Writing = `researches/${research.id}/writing`
const typo: Writing = 'research/42/writing' // ✗ does not compileThe generated registry
AdonisJS generates code from your application into .adonisjs/. The collaboration package plugs into
that: an Assembler init hook scans app/collaboration/documents/, strips the _types suffix from
each file, and writes the result as a union.
/**
* Generated by @adonis-agora/collaboration — do not edit.
* Refresh with `node ace codegen`.
*/
export type CollabDocumentName = 'researches/writing' | 'whiteboards/board'The map it builds is type-only (typeof import(...)), so the generated file emits no runtime
code. It is consumed by tsc and can be re-exported to the frontend without dragging server modules
across the boundary.
When it refreshes
Whenever the Assembler runs:
node ace codegen # explicitly
node ace serve --hmr # on boot, and again when a document file changes
node ace test # before the suite
node ace build # before compilingThere is no cache to clear and no list to update. Delete a document file and the union shrinks on the next run.
Before you declare anything
A fresh install generates a valid file rather than an empty one:
/** No collaborative documents declared yet. Use `node ace make:collab-document`. */
export type CollabDocumentName = nevernever is the right answer here, and it behaves usefully: code that takes a CollabDocumentName
stops compiling until you declare one, instead of accepting anything.
Using it
Server-side, to keep helpers honest:
import type { CollabDocumentName } from '.adonisjs/collaboration/documents.js'
import collaboration from '@adonis-agora/collaboration/services/main'
export function textOf(name: CollabDocumentName, id: string) {
return collaboration.current.getDocumentText({ docName: `${name}/${id}` })
}Client-side, alongside the per-document types:
import type { WritingSpace } from '#collaboration/documents/writing_types'
import { useComments } from '@adonis-agora/collaboration-client'
function CommentSidebar({ docName, space }: { docName: string; space: WritingSpace }) {
const { comments } = useComments({ docName, space })
// 'txt' would not compile
}The union is of document types, not document instances
CollabDocumentName contains 'researches/writing' — the shape — not 'researches/42/writing',
which is one instance of it. Instance names are built at runtime from an id, so the union types the
family and your pattern in config/collaboration.ts types the parameters. The two together are
what make a full name checkable end to end.
Committing it
.adonisjs/ is generated, and the usual practice is to leave it out of version control — every
command that matters regenerates it. If your CI runs tsc without an Assembler step first, either
add node ace codegen to the pipeline or commit the folder; a stale registry is a confusing type
error, not a runtime one.
Commands
collaboration:init, collaboration:prune and make:collab-document — what each one writes, every flag, and the generated document-types file explained line by line.
Client hooks
The React package — the provider and its config, the hooks and which one to reach for, why the client never hard-codes an engine, and how sessions are reference counted.