Agora
Codegen

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:

types/collaboration.ts
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 compile

The 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.

.adonisjs/collaboration/documents.ts
/**
 * 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 compiling

There 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 = never

never 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:

app/services/document_service.ts
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.

On this page