Registering your app

One declare module block tells Chelone your resources, permissions and message catalog. Every typed source, hook and component reads it.

Chelone cannot know your app's resources, permissions or messages on its own. Register, in packages/core/src/resource/register.ts, is the one place you say. It is an empty interface that your app augments, and every Registered* type reads what you put on it.

Registering is optional. Without it each of those types falls back to a loose shape and nothing breaks. With it, a misspelt resource name or source stops compiling.

The registration

One file defines the resources and augments Register:

src/registry.ts
import { function defineResources<const T extends ResourceMap>(resources: T & Checked<T>): T
Identity helper that pins literal resource names + schema types via `const`. `defineResources({ posts: { schema: postSchema } })` keeps `"posts"` literal and `typeof postSchema` exact, enabling end-to-end inference.
defineResources
,
const englishMessages: {
    readonly "chelone.page.list": "{name}";
    readonly "chelone.page.create": "Create {name}";
    readonly "chelone.page.edit": "{name} {recordRepresentation}";
    readonly "chelone.page.show": "{name} {recordRepresentation}";
    readonly "chelone.page.dashboard": "Dashboard";
    readonly "chelone.page.error": "Something went wrong";
    readonly "chelone.page.not_found": "Not Found";
    readonly "chelone.page.loading": "Loading";
    readonly "chelone.page.empty": "No {name} yet.";
    readonly "chelone.page.access_denied": "Access denied";
    readonly "chelone.page.authentication_error": "Authentication error";
    ... 360 more ...;
    readonly "chelone.validation.required": "Required";
}
The English catalog an `<Admin>` with no `i18n` prop reads from, and the base an app extends: spread it, then add or override entries. Every key is `chelone.<group>.<leaf>`: the ported shadcn components used to translate the react-admin `ra.*` keys they were ported from, and those keys were moved under `chelone.*` with their group names kept (`page`, `action`, `navigation`). Chelone's i18n never throws on a missing key (it returns the key or a `_` fallback), so a partial catalog shows raw `chelone.page.list`-style keys. Interpolation is single-brace `{name}` (not react-admin's `%{name}`). Plural strings are `{ one, other }` objects keyed by Intl.PluralRules categories (react-admin's `a |||| b` form). `defaultMessages` (the `chelone.*` keys the core controllers emit) and `mapMessages` (the maps add-on's labels) sit underneath, and no entry here repeats one of them. Where a moved key landed on a controller key that says something different, the component's own key keeps the group and names the role: `access_denied_detail`, `authentication_error_detail`, `loading_detail` and `http_error_label` sit beside the controller's `access_denied`, `authentication_error`, `loading` and `http_error`.
englishMessages
} from "@chelone/core";
import { import zz } from "zod"; export const
const resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
resources
=
defineResources<{
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}>(resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
} & Checked<{
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}>): {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
Identity helper that pins literal resource names + schema types via `const`. `defineResources({ posts: { schema: postSchema } })` keeps `"posts"` literal and `typeof postSchema` exact, enabling end-to-end inference.
defineResources
({
posts: {
    readonly schema: z.ZodObject<{
        id: z.ZodNumber;
        title: z.ZodString;
    }, z.core.$strip>;
} & {
    references?: {} | undefined;
}
posts
: {
schema: z.ZodObject<{
    id: z.ZodNumber;
    title: z.ZodString;
}, z.core.$strip>
schema
: import zz.
function object<{
    id: z.ZodNumber;
    title: z.ZodString;
}>(shape?: {
    id: z.ZodNumber;
    title: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodNumber;
    title: z.ZodString;
}, z.core.$strip>
object
({ id: z.ZodNumberid: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber(), title: z.ZodStringtitle: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }) },
comments: {
    readonly schema: z.ZodObject<{
        id: z.ZodNumber;
        post_id: z.ZodNumber;
    }, z.core.$strip>;
} & {
    references?: {} | undefined;
}
comments
: {
schema: z.ZodObject<{
    id: z.ZodNumber;
    post_id: z.ZodNumber;
}, z.core.$strip>
schema
: import zz.
function object<{
    id: z.ZodNumber;
    post_id: z.ZodNumber;
}>(shape?: {
    id: z.ZodNumber;
    post_id: z.ZodNumber;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodNumber;
    post_id: z.ZodNumber;
}, z.core.$strip>
object
({ id: z.ZodNumberid: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber(), post_id: z.ZodNumberpost_id: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber() }) },
}); declare module "@chelone/core" { interface Register {
Register.resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
resources
: typeof
const resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
resources
;
Register.permissions: string[]permissions: string[];
Register.messages: {
    readonly "chelone.page.list": "{name}";
    readonly "chelone.page.create": "Create {name}";
    readonly "chelone.page.edit": "{name} {recordRepresentation}";
    readonly "chelone.page.show": "{name} {recordRepresentation}";
    readonly "chelone.page.dashboard": "Dashboard";
    readonly "chelone.page.error": "Something went wrong";
    readonly "chelone.page.not_found": "Not Found";
    readonly "chelone.page.loading": "Loading";
    readonly "chelone.page.empty": "No {name} yet.";
    readonly "chelone.page.access_denied": "Access denied";
    readonly "chelone.page.authentication_error": "Authentication error";
    ... 360 more ...;
    readonly "chelone.validation.required": "Required";
}
messages
: typeof
const englishMessages: {
    readonly "chelone.page.list": "{name}";
    readonly "chelone.page.create": "Create {name}";
    readonly "chelone.page.edit": "{name} {recordRepresentation}";
    readonly "chelone.page.show": "{name} {recordRepresentation}";
    readonly "chelone.page.dashboard": "Dashboard";
    readonly "chelone.page.error": "Something went wrong";
    readonly "chelone.page.not_found": "Not Found";
    readonly "chelone.page.loading": "Loading";
    readonly "chelone.page.empty": "No {name} yet.";
    readonly "chelone.page.access_denied": "Access denied";
    readonly "chelone.page.authentication_error": "Authentication error";
    ... 360 more ...;
    readonly "chelone.validation.required": "Required";
}
The English catalog an `<Admin>` with no `i18n` prop reads from, and the base an app extends: spread it, then add or override entries. Every key is `chelone.<group>.<leaf>`: the ported shadcn components used to translate the react-admin `ra.*` keys they were ported from, and those keys were moved under `chelone.*` with their group names kept (`page`, `action`, `navigation`). Chelone's i18n never throws on a missing key (it returns the key or a `_` fallback), so a partial catalog shows raw `chelone.page.list`-style keys. Interpolation is single-brace `{name}` (not react-admin's `%{name}`). Plural strings are `{ one, other }` objects keyed by Intl.PluralRules categories (react-admin's `a |||| b` form). `defaultMessages` (the `chelone.*` keys the core controllers emit) and `mapMessages` (the maps add-on's labels) sit underneath, and no entry here repeats one of them. Where a moved key landed on a controller key that says something different, the component's own key keeps the group and names the role: `access_denied_detail`, `authentication_error_detail`, `loading_detail` and `http_error_label` sit beside the controller's `access_denied`, `authentication_error`, `loading` and `http_error`.
englishMessages
;
} }

Hand resources to <Admin resources={resources}> in React and to <Admin :registry="resources"> in Vue. The augmentation is what the compiler reads, and the map is what the app runs.

Once registered, the names are a union:

import { function defineResources<const T extends ResourceMap>(resources: T & Checked<T>): T
Identity helper that pins literal resource names + schema types via `const`. `defineResources({ posts: { schema: postSchema } })` keeps `"posts"` literal and `typeof postSchema` exact, enabling end-to-end inference.
defineResources
, type type RegisteredResourceName = "posts" | "comments"
The registered resource names, or `string` when nothing is registered.
RegisteredResourceName
} from "@chelone/core";
import { import zz } from "zod"; const
const resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
resources
=
defineResources<{
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}>(resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
} & Checked<{
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}>): {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
Identity helper that pins literal resource names + schema types via `const`. `defineResources({ posts: { schema: postSchema } })` keeps `"posts"` literal and `typeof postSchema` exact, enabling end-to-end inference.
defineResources
({
posts: {
    readonly schema: z.ZodObject<{
        id: z.ZodNumber;
        title: z.ZodString;
    }, z.core.$strip>;
} & {
    references?: {} | undefined;
}
posts
: {
schema: z.ZodObject<{
    id: z.ZodNumber;
    title: z.ZodString;
}, z.core.$strip>
schema
: import zz.
function object<{
    id: z.ZodNumber;
    title: z.ZodString;
}>(shape?: {
    id: z.ZodNumber;
    title: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodNumber;
    title: z.ZodString;
}, z.core.$strip>
object
({ id: z.ZodNumberid: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber(), title: z.ZodStringtitle: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }) },
comments: {
    readonly schema: z.ZodObject<{
        id: z.ZodNumber;
        post_id: z.ZodNumber;
    }, z.core.$strip>;
} & {
    references?: {} | undefined;
}
comments
: {
schema: z.ZodObject<{
    id: z.ZodNumber;
    post_id: z.ZodNumber;
}, z.core.$strip>
schema
: import zz.
function object<{
    id: z.ZodNumber;
    post_id: z.ZodNumber;
}>(shape?: {
    id: z.ZodNumber;
    post_id: z.ZodNumber;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodNumber;
    post_id: z.ZodNumber;
}, z.core.$strip>
object
({ id: z.ZodNumberid: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber(), post_id: z.ZodNumberpost_id: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber() }) },
}); declare module "@chelone/core" { interface Register {
Register.resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
resources
: typeof
const resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
resources
;
} } type
type Name = "posts" | "comments"
Name
= type RegisteredResourceName = "posts" | "comments"
The registered resource names, or `string` when nothing is registered.
RegisteredResourceName
;

A record type is inferred from the resource's schema:

import { function defineResources<const T extends ResourceMap>(resources: T & Checked<T>): T
Identity helper that pins literal resource names + schema types via `const`. `defineResources({ posts: { schema: postSchema } })` keeps `"posts"` literal and `typeof postSchema` exact, enabling end-to-end inference.
defineResources
, type
type RegisteredRecord<TName extends RegisteredResourceName> = {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
}[TName] extends {
    schema: infer S;
} ? S extends StandardSchemaV1<unknown, infer Output> ? Output : LooseRecord : LooseRecord
The record type of a registered resource, or {@link LooseRecord } when it has no schema.
RegisteredRecord
} from "@chelone/core";
import { import zz } from "zod"; const
const resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
}
resources
=
defineResources<{
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
}>(resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
} & Checked<{
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
}>): {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
}
Identity helper that pins literal resource names + schema types via `const`. `defineResources({ posts: { schema: postSchema } })` keeps `"posts"` literal and `typeof postSchema` exact, enabling end-to-end inference.
defineResources
({
posts: {
    readonly schema: z.ZodObject<{
        id: z.ZodNumber;
        title: z.ZodString;
    }, z.core.$strip>;
} & {
    references?: {} | undefined;
}
posts
: {
schema: z.ZodObject<{
    id: z.ZodNumber;
    title: z.ZodString;
}, z.core.$strip>
schema
: import zz.
function object<{
    id: z.ZodNumber;
    title: z.ZodString;
}>(shape?: {
    id: z.ZodNumber;
    title: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodNumber;
    title: z.ZodString;
}, z.core.$strip>
object
({ id: z.ZodNumberid: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber(), title: z.ZodStringtitle: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }) },
}); declare module "@chelone/core" { interface Register {
Register.resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
}
resources
: typeof
const resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
}
resources
;
} } type
type Post = {
    id: number;
    title: string;
}
Post
=
type RegisteredRecord<TName extends RegisteredResourceName> = {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
}[TName] extends {
    schema: infer S;
} ? S extends StandardSchemaV1<unknown, infer Output> ? Output : LooseRecord : LooseRecord
The record type of a registered resource, or {@link LooseRecord } when it has no schema.
RegisteredRecord
<"posts">;

A name the registry lacks is an error:

import { function defineResources<const T extends ResourceMap>(resources: T & Checked<T>): T
Identity helper that pins literal resource names + schema types via `const`. `defineResources({ posts: { schema: postSchema } })` keeps `"posts"` literal and `typeof postSchema` exact, enabling end-to-end inference.
defineResources
, type
type RegisteredRecord<TName extends RegisteredResourceName> = {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}[TName] extends {
    schema: infer S;
} ? S extends StandardSchemaV1<unknown, infer Output> ? Output : LooseRecord : LooseRecord
The record type of a registered resource, or {@link LooseRecord } when it has no schema.
RegisteredRecord
} from "@chelone/core";
import { import zz } from "zod"; const
const resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
resources
=
defineResources<{
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}>(resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
} & Checked<{
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}>): {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
Identity helper that pins literal resource names + schema types via `const`. `defineResources({ posts: { schema: postSchema } })` keeps `"posts"` literal and `typeof postSchema` exact, enabling end-to-end inference.
defineResources
({
posts: {
    readonly schema: z.ZodObject<{
        id: z.ZodNumber;
        title: z.ZodString;
    }, z.core.$strip>;
} & {
    references?: {} | undefined;
}
posts
: {
schema: z.ZodObject<{
    id: z.ZodNumber;
    title: z.ZodString;
}, z.core.$strip>
schema
: import zz.
function object<{
    id: z.ZodNumber;
    title: z.ZodString;
}>(shape?: {
    id: z.ZodNumber;
    title: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodNumber;
    title: z.ZodString;
}, z.core.$strip>
object
({ id: z.ZodNumberid: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber(), title: z.ZodStringtitle: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }) },
comments: {
    readonly schema: z.ZodObject<{
        id: z.ZodNumber;
        post_id: z.ZodNumber;
    }, z.core.$strip>;
} & {
    references?: {} | undefined;
}
comments
: {
schema: z.ZodObject<{
    id: z.ZodNumber;
    post_id: z.ZodNumber;
}, z.core.$strip>
schema
: import zz.
function object<{
    id: z.ZodNumber;
    post_id: z.ZodNumber;
}>(shape?: {
    id: z.ZodNumber;
    post_id: z.ZodNumber;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodNumber;
    post_id: z.ZodNumber;
}, z.core.$strip>
object
({ id: z.ZodNumberid: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber(), post_id: z.ZodNumberpost_id: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber() }) },
}); declare module "@chelone/core" { interface Register {
Register.resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
resources
: typeof
const resources: {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}
resources
;
} } type
type Missing = ResourceRecord<Identifier> & {
    [key: string]: unknown;
}
Missing
=
type RegisteredRecord<TName extends RegisteredResourceName> = {
    readonly posts: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            title: z.ZodString;
        }, z.core.$strip>;
    };
    readonly comments: {
        readonly schema: z.ZodObject<{
            id: z.ZodNumber;
            post_id: z.ZodNumber;
        }, z.core.$strip>;
    };
}[TName] extends {
    schema: infer S;
} ? S extends StandardSchemaV1<unknown, infer Output> ? Output : LooseRecord : LooseRecord
The record type of a registered resource, or {@link LooseRecord } when it has no schema.
RegisteredRecord
<"commnets">;
Type '"commnets"' does not satisfy the constraint 'RegisteredResourceName'.

What each key types

KeyRead byTypes
resourcesRegisteredResources, RegisteredResourceName, RegisteredRecord<Resource name>, every source, and createResourceComponents
permissionsRegisteredPermissionsAuthProvider, usePermissions and the <Admin> function child in React; usePermissions in Vue
messagesRegisteredMessagesuseTranslate: its keys and their {placeholders}
literalMessagesRegisteredLiteralMessages and HasLiteralMessagestranslate, which returns a key's text instead of string when the value is true

literalMessages: true sits beside messages on Register. It is off by default, because a union of literal text is not a string you can reassign.

Where it goes

Put the block in one file the TypeScript program includes. The file needs a top-level import or export: without one it is a script, and declare module replaces the module instead of augmenting it.

The augmentation is global to the program. Every file reads it, and a second interface Register in another file merges with the first, so register each key once.

Without it

A key you leave off keeps its fallback:

KeyFalls back to
resourcesResourceMap, so a resource name is a string and a record is LooseRecord
permissionsunknown
messagesMessageCatalog, so any key is accepted
literalMessagesoff, so translate returns string

See When types widen for the cases where a registered app still falls back, such as a resource with no schema.

Slots an adapter fills

ResourceSlots, in packages/core/src/resource/registry-store.ts, is a second augmentation point. Core does not know what a menu icon or a view is, because that depends on the framework. Each adapter augments ResourceSlots with an icon, a representation and a component, so an app writes none of them. They resolve ResourceIcon, ResourceRepresentation and ResourceComponent per adapter.

React declares:

packages/react/src/index.ts
declare module "@chelone/core" {
  interface ResourceSlots {
    icon: ComponentType;
    representation: RecordRepresentation;
    component: ResourceViewChild;
  }
}

Vue declares:

packages/vue/src/index.ts
declare module "@chelone/core" {
  interface ResourceSlots {
    icon: Component;
    representation: RecordRepresentation;
    component: Component;
  }
}

Import an adapter and the slot resolves to its type:

import "@chelone/react";
import type { type ResourceIcon = ComponentClass<{}, any> | FunctionComponent<{}>
A resource's menu icon: the adapter's icon type, or `unknown` when none is declared.
ResourceIcon
} from "@chelone/core";
type
type Icon = ComponentClass<{}, any> | FunctionComponent<{}>
Icon
= type ResourceIcon = ComponentClass<{}, any> | FunctionComponent<{}>
A resource's menu icon: the adapter's icon type, or `unknown` when none is declared.
ResourceIcon
;

A program that imports both adapters sees both augmentations. They declare the same properties with different types, so TypeScript keeps one declaration per property. Keep one adapter in a program. With neither, each of the three types is unknown.

Pages that use the registration