Référence d'API — @nodefony/framework — racine
stable
Documentation › Référence d’API › @nodefony/framework › racine
Générée depuis les déclarations de types publiées de
@nodefony/framework10.0.0-beta.5 : exactement ce que reçoit une application qui l’installe. Cette page n’existe dans aucun dépôt — pour la corriger, on corrige le TSDoc du code.
@nodefony/framework#
import { … } from "@nodefony/framework" — 34 symboles.
Classes (5)#
Controller#
class Controller extends Service implements IController
static prefix: stringstatic scope: ControllerScope— Portée d’instanciation de la classe —"singleton"par défaut,"request"posé par le décorateur@Scope(statique hérité, lu vianew.targetau constructeur et par le Resolver : 0 Reflect).static assertScope(): void— Vérifie que la portée de la classe est admise par sa lignée.module?: Moduletemplate?: Eta | nullget context(): ContextType— Contexte transport courant.set context(context: ContextType)— Contexte transport courant.log(pci: Pci, severity?: Severity, msgid?: Msgid, msg?: Message, moduleName?: string): import("nodefony").Pdu— Journalise au nom du module du contrôleur, pas du noyau :moduleNameexplicite, sinon le module posé sur la requête (context.logModule), sinon le module propriétaire du contrôleur.get route(): Route | null— Route de l’appel en cours.get request(): contextRequestset request(request: contextRequest)get response(): HttpResponse | Http2Response | WebsocketResponse | nullset response(response: HttpResponse | Http2Response | WebsocketResponse | null)get user(): IRequestUser | null— L’utilisateur CONNECTÉ, ounull— anonyme, ou route hors de toute zone du pare-feu.get method(): HTTPMethodset method(method: HTTPMethod)get query(): Record<string, unknown>— Paramètres d’URL de l’appel en cours — jamais le corps, qui reste dansbody.set query(value: Record<string, unknown>)— Paramètres d’URL de l’appel en cours — jamais le corps, qui reste dansbody.get files(): readonly unknown[]— Les fichiers reçus dans un corps multipart (UploadedFile) ; les champs texte du même corps sont dansbody.set files(value: readonly unknown[])— Les fichiers reçus dans un corps multipart (UploadedFile) ; les champs texte du même corps sont dansbody.get body(): Record<string, unknown>— Le CORPS de la requête, parsé — nom universel de l’écosystème (Express, Fastify, NestJS).set body(value: Record<string, unknown>)— Le CORPS de la requête, parsé — nom universel de l’écosystème (Express, Fastify, NestJS).get session(): Session | null— Session courante, ounull.constructor(name: string, context: ContextType)setContext(context: ContextType): void— Lie l’instance au contexte de SA requête — contrôleur par requête seulement : tout l’état de la requête (request,response,method,query*) en dérive ensuite, toujours frais.setContextJson(encoding?: BufferEncoding): voidsetContextHtml(encoding?: BufferEncoding): voidrender(data: unknown, encoding?: BufferEncoding, status?: string | number, headers?: Record<string, string | number>): MaybePromise<Http2Response | HttpResponse>— Rend une donnée sur la réponse de la requête courante (JSON, HTML ou texte selon son type).renderResponse(data: unknown, encoding?: BufferEncoding, status?: string | number, headers?: OutgoingHttpHeaders): MaybePromise<Http2Response | HttpResponse | WebsocketResponse>— Envoie une donnée déjà sérialisée sur le transport de la requête (HTTP ou WebSocket), après statut et en-têtes éventuels.renderView(path: string | FileClass, param?: Record<string, unknown>, status?: string | number, headers?: Record<string, string | number>): Promise<Http2Response | HttpResponse | WebsocketResponse>renderJson(obj: unknown, status?: string | number, headers?: OutgoingHttpHeaders): MaybePromise<Http2Response | HttpResponse | WebsocketResponse>— Sérialise en JSON puis envoie (renderResponse).acceptsSse(): boolean— Le client demande-t-il un flux d’événements (Accept: text/event-stream) ?renderSse(options?: ISseStreamOptions): SseStream | Promise<SseStream>— Répond par un flux d’événements serveur, en HTTP/1.1 comme en HTTP/2.setRoute(route: Route): Route— Pose la route de la requête courante sur l’instance (per-request).setRoute(route: Route | null): Route | null— Pose la route de la requête courante sur l’instance (per-request).getSession(): Session | nullredirect(url: string, status?: string | number, headers?: Record<string, string | number>): voidgetFlashBag(key: string): unknownsetFlashBag(key: string, value: unknown): unknownaddFlash(key: string, value: unknown): unknownforward(name: string, param?: unknown[]): unknowngetFile(file: FileClass | string): FileClassgetFileAsync(file: FileClass | string): Promise<FileClass>— Variante async degetFile()— résout les stats viaFileClass.from(pas delstatSyncbloquant).renderFileDownload(file: FileClass | string, options?: ReadStreamOptions, headers?: OutgoingHttpHeaders): Promise<ReadStream>— Sert un fichier en TÉLÉCHARGEMENT (Content-Disposition: attachment).streamFile(file: FileClass | string, headers?: OutgoingHttpHeaders, options?: ReadStreamOptions): Promise<ReadStream>— Envoie un fichier en FLUX, du disque vers la réponse, sans jamais le charger en mémoire.renderMediaStream(file: FileClass | string, headers?: OutgoingHttpHeaders, options?: ReadStreamOptions): Promise<HttpResponse | ReadStream | WebsocketResponse>
Resolver#
class Resolver implements IResolver
injector?: Injector | nullcontroller: ControllerConstructor | null— Classe du controller résolu — seul son nom est lu (erreurs, journal).actionName?: string— Nom de l’action résolue.action?: (...args: unknown[]) => unknowncontext: ContextTyperoute: Route | null— Route matchée,nulltant qu’aucune ne l’est.resolve: boolean—truesi une route a matché la requête.variables: unknown[]exception?: HttpError | Error | null— Erreur levée pendant la résolution, relue par le pipeline.unrendered?: string— Ce que l’action a rendu quand le Resolver n’a rien pu envoyer — absent sinon.acceptedProtocol: string | nullbypassFirewall: booleanqueryOverride: Record<string, unknown> | null— Query d’une invocation par message (pont WS-RPCapi.request) — pendant decleanPathOverridepour le?…du path invoqué.methodOverride: string | null— Méthode HTTP logique d’une invocation par le pont WS-RPCapi.requestquand c’est une MUTATION (POST/PUT/PATCH/DELETE).messageInvocation: boolean— Cette résolution est-elle une invocation par message (pont WS-RPCapi.request) plutôt que l’établissement de la connexion ?constructor(context: ContextType)match(route: Route, context: ContextType, cleanPath?: string, literal?: boolean): ((string | null)[] & Record<string, unknown>) | null— Rejoue le match d’une route sur un contexte (WS : handshake puis frames).getMatchedParams(): Record<string, unknown>— Snapshot per-requête des variables de route matchées ({name}→ valeur, + wildcard*éventuel).parsePathernController(name: string): voidgetAction(name: string): (...args: unknown[]) => unknown | nullnewController(context?: ContextType): MaybePromise<Controller>— L’instance du controller de la route, posée sur le container du contexte.executeAction(data?: unknown[], reload?: boolean, metaArg?: RouteActionMeta): MaybePromise<ActionOutcome>— Exécute l’action résolue et retourne sa valeur brute, SANS la rendre sur le transport (pas dereturnController/send).authorizeAction(): Promise<void>— Juge les gardes de l’action résolue SANS l’exécuter — appelé par le pipeline WebSocket AVANT d’accepter le handshake (#596), pour qu’un connecté sans le rôle reçoive son 403 en statut HTTP au lieu d’u …callController(data?: unknown[], reload?: boolean): MaybePromise<unknown>— Exécute l’action PUIS rend sa valeur sur le transport.executeActionGuarded(data?: unknown[], reload?: boolean): Promise<{ result: unknown; }>— Exécute l’action AVEC la porte d’idempotence mais SANS rendu (returnController) — le chemin du pontapi.request(WS) : la valeur nue est enveloppée{id, result}par le peer, jamais rendue su …returnController(result: unknown): MaybePromise<unknown>— Rend la valeur d’une action sur le transport, selon sa nature : promesse (démêlée puis re-rendue), texte, Buffer, réponse déjà construite, scalaire ou objet (auto-JSON), rien (l’action envoie elle-mêm …
ResourceController#
class ResourceController<T = unknown> extends Controller
Controller de ressource duplex (V4.2 — une action, deux transports) : la logique métier est écrite UNE fois dans le service de ressource, les actions de la sous-classe ne sont que des portes (REST, WS-RPC invoke, GraphQL à venir) qui retournent la valeur brute — returnController l’auto-JSON en REST, le pont WS l’enveloppe ({id,result}), sans réécrire l’action par transport.
static scope: ControllerScope— Singleton PAR DÉFAUT : la classe est conçue stateless — premier client du scope V4.3.protected resource: IResourceService<T> | null— Service de ressource — état de BOOT (posé 1× ici, jamais réassigné).constructor(name: string, context: ContextType, resource?: IResourceService<T>)protected requireResource(): IResourceService<T>— Service de ressource garanti — 500 explicite si la sous-classe ne l’a pas fourni (erreur de câblage, pas une erreur client).protected listResource(criteria?: Record<string, unknown>, options?: IResourceReadOptions): Promise<T[]>— Liste la ressource.protected listPageResource(page: IResourcePageQuery): Promise<IPage<T>>— Liste la ressource en rendant une page ({ items, hasNext, total? }) plutôt qu’un tableau nu.protected getResource(id: string, options?: IResourceReadOptions): Promise<T | null>— Lit une entité par id —nullsi absente (la porte décide du 404).protected createResource(data: PartialInput<T>): Promise<T>— Crée une entité — 501 si la ressource est read-only.protected updateResource(criteria: Record<string, unknown>, data: PartialInput<T>): Promise<T | null>— Met à jour une entité ciblée par critères — 501 si non supporté.protected removeResource(criteria: Record<string, unknown>): Promise<number>— Supprime par critères (nombre d’entités touchées) — 501 si non supporté.
Route#
class Route implements IRoute
name: string— Nom de la route (journalisation, diagnostic).path?: string— Gabarit de la route, préfixe compris (/users/{id}) — faible cardinalité, publié commeroutePathsur le canalnodefony.http.request.controller?: ControllerConstructorclassMethod?: stringprefix?: stringmethod?: HTTPMethodpattern?: RegExpvariables: string[]unreachableChars?: string[]— Caractères du chemin déclaré qu’une requête ne peut PAS porter — la route est donc inatteignable.defaults: Partial<Record<string, unknown>>requirements: Partial<RouteRequirements>hash?: stringhost?: string | string[]hostRegexp?: RegExp[]— Patterns de domaine pré-compilés (host +requirements.domain), RegExp ancrées/wildcard.methodsSet?: Set<string>— P3a — requirements pré-compilés au boot (0 alloc par match) :methodsSet= méthodes autorisées normalisées UPPERCASE (lookup O(1)) ;methodsAllow= valeur du headerAllowdu 405, jointe UNE fois …methodsAllow?: stringvarRegexp?: Record<string, RegExp>bypassFirewall: booleanareaRoleExempt: boolean— La route décide seule de son autorisation → la zone du firewall n’applique pas son rôle par défaut.fallback: boolean— Route de repli, résolue après toutes les autres.bodyStream?: boolean— P2.9 — Cache mémoïsé : l’action attend-elle le flux brut du body (@Body({ stream:true })) ?actionMeta?: RouteActionMeta— P5 — Metadata d’action figées (@HttpCode/@Header/@Redirect/params/ session), memo au 1er hit commebodyStream:undefined= pas encore résolu (viaresolveActionMeta, 1 lecture Reflect par …filePath?: stringmodule?: { name: string; }— Module propriétaire de la route — set parRouter.setController()àonBoot, donc PAS disponible à la création de la route (via@controller) qui s’évalue à l’import.constructor(name: string, obj?: RouteOptions)static cleanPathname(context: ContextType): string— Normalise le pathname de la requête pour le matching : retire le(s) slash(es) final(aux) viastripTrailingSlashes.match(context: ContextType, cleanPath?: string, methodOverride?: string, literal?: boolean): ((string | null)[] & Record<string, unknown>) | null— Confronte la requête à cette route : chemin, hôte, puis exigences.compile(): RegExp— Compile the route into a regular expression pattern.compileRequirements(): void— Pré-compile les requirements (P3a) — méthodes normalisées en Set UPPERCASE + RegExp des requirements de variables.hydrateDefaultParameters(res: RegExpMatchArray): voidtoString(): string— Returns a string representation of an object.toLogLine(): string— Formatte la route en une seule ligne lisible pour le log debug — évite le JSON multi-ligne dutoString()qui polluait ~7 lignes par route au boot.toObject(): objectsetDefaults(arg?: Record<string, unknown>): voidaddDefault(key: string, value: unknown): voidsetPrefix(prefix?: string): voidsetPattern(pattern?: string): stringsetHostname(hostname?: string | string[]): voidcompileHost(): void— Pré-compile les patterns de domaine de la route (host+requirements.domain) enRegExp[]ancrées, via le matcher partagé@nodefony/http.matchHostname(context: ContextType): booleangenerateId(): string— Empreinte stable de la route — sert d’identité pour la comparer à elle-même (deux déclarations identiques donnent la même empreinte, deux chemins différents non).addRequirement<K extends keyof RouteRequirements>(key: K, value: RouteRequirements[K]): RouteRequirements[K]getRequirement<K extends keyof RouteRequirements>(key: K): RouteRequirements[K]hasRequirements(): numbermatchRequirements(context: ContextType, methodOverride?: string): boolean
default#
class Framework extends Module<IFrameworkConfig>
constructor(kernel: Kernel)configSchema(): unknown— JSON Schema de la config framework → data plane admin (config riche Studio).onKernelRegister(): Promise<this>— PhaseonRegister: valide la config du module via le builder (defineFrameworkConfig) AVANT que les@services(Router, AdminBroker — qui lisentmodule.options.router/.adminBroker) ne soient i …onKernelBoot(): Promise<this>— PhaseonBoot(après les@servicesàonPreBoot→ le défaut mémoireidempotencyStoreest déjà enregistré, ET après l’onPreBootdes autres modules → le serviceredisest résoluble) : si la co …onKernelReady(): Promise<this>— PhaseonReady(après le boot de tous les modules) : enregistre le producteur admin du kernel puis monte le data plane/nodefony/<ns>/api/*.
Fonctions (8)#
buildPlaygroundSnapshot#
function buildPlaygroundSnapshot(): { controllers: PlaygroundController[]; }
Construit le snapshot playground : toutes les routes à controller, groupées par classe de controller, triées (module puis nom, actions par path).
createFrameworkAdminApi#
function createFrameworkAdminApi(broker?: IAdminBroker, opts?: FrameworkAdminApiOptions): IAdminApi
Producteur IAdminApi du module framework — exposé sous /nodefony/framework/api/*.
createKernelAdminApi#
function createKernelAdminApi(kernel: IKernel): IAdminApi
createSyslogAdminApi#
function createSyslogAdminApi(syslog: ISyslog, options?: SyslogAdminApiOptions): IAdminApi
Producteur IAdminApi du syslog (core) — exposé sous /nodefony/syslog/api/*. 4ᵉ et dernier producteur de P10.3.
getIdempotencyStoreFactory#
function getIdempotencyStoreFactory(name: string): IdempotencyStoreFactory
Fabrique d’un store par nom, ou undefined si inconnu.
listIdempotencyStores#
function listIdempotencyStores(): string[]
Noms de stores distribués enregistrés (résolution auto + validation boot). N’inclut PAS "memory" : dans resolveAutoStore, memory est le fallback (per-pod), jamais une préférence à sélectionner — l’y mettre fausserait le choix.
registerIdempotencyStore#
function registerIdempotencyStore(name: string, factory: IdempotencyStoreFactory): void
Enregistre (ou remplace) la fabrique d’un store d’idempotence distribué. Appelée par l’application pour les adapters (redis/drizzle).
validateRouteInput#
function validateRouteInput(schema: IRouteSchema, raw: RawRouteInput): MaybePromise<RawRouteInput>
Valide l’entrée d’une action contre son schéma — synchrone tant que chaque validateur l’est, asynchrone seulement si l’un d’eux rend une promesse.
Interfaces (12)#
FrameworkAdminApiOptions#
interface FrameworkAdminApiOptions
Options de composition du producteur framework.
playground?: boolean— Monte les endpoints Playground (playground/routes) — dev uniquement : la console Studio exécute des mutations depuis le navigateur.
IIdempotencyStore#
interface IIdempotencyStore
Cache d’idempotence borné (modèle Stripe Idempotency-Key) — dédoublonne les mutations rejouées (reconnexion socket, double-clic) du data plane admin comme des controllers userland décorés @Idempotent.
begin(key: string, fingerprint: string): IdempotencyOutcome | Promise<IdempotencyOutcome>— Réserve atomiquement la clé et renvoie le verdict à suivre.complete(key: string, response: IdempotentResponse): void | Promise<void>— Mémorise la réponse d’une clé in-flight (TTL) → rejeux futurs =replayed.abort(key: string): void | Promise<void>— Libère une clé in-flight dont l’exécution a échoué : rien n’est mémorisé (un échec doit pouvoir être réessayé).gc?(now?: number): Promise<number> | number— Purge les entrées expirées — présente UNIQUEMENT sur les impls SANS expiration native (drizzle→DELETE WHERE expiresAt <= now).listPage(query: IIdempotencyListQuery): Promise<IPage<IIdempotencyKeyEntry>>— Page de clés vivantes — introspection admin (repérer une clé figée in-flight qui bloque les rejeux d’un client).readonly size: number— Nombre d’entrées vivantes (observabilité / tests).
IIdempotencyStoreFactoryContext#
interface IIdempotencyStoreFactoryContext
Contexte passé à une fabrique de store : de quoi se construire (résolutions coûteuses en lazy à l’intérieur de l’instance). Le module donne accès au container kernel (ctx.module.kernel?.container?.get("redis")).
readonly module: Module— Module framework (portekernel.containerpour résoudreredis, etc.).readonly config: IFrameworkConfig— Config framework validée + gelée.
IResourcePageQuery#
interface IResourcePageQuery extends IPageQuery
Requête de page telle qu’une porte la passe au service — IPageQuery du core, plus les critères que la sous-classe a EXPLICITEMENT décidé d’exposer.
criteria?: Record<string, unknown>— Critères de filtrage — jamais dérivés de la query string automatiquement.
IResourceReadOptions#
interface IResourceReadOptions
Options de lecture d’une liste — pagination avant tout.
limit?: number— Nombre maximum d’enregistrements rendus.offset?: number— Enregistrements sautés (⚠️ se dégrade sur les grandes tables — curseur à venir).order?: Array<[ string, "ASC" | "DESC" ]>— Tri, sous la forme[[champ, sens], …]— même écriture queIPageQuery.order(core) et queRepositoryReadOptions(orm-core).relations?: string[]— Associations à charger avec l’enregistrement.
IResourceService#
interface IResourceService<T = unknown>
find(criteria?: Record<string, unknown>, options?: IResourceReadOptions): Promise<T[]> | T[]findById(id: string, options?: IResourceReadOptions): Promise<T | null> | T | null— Lit par identifiant.findPage?(page: IResourcePageQuery): Promise<IPage<T>> | IPage<T>— Rend une PAGE plutôt qu’un tableau nu —AbstractCrudServicel’hérite déjà (findPage→paginate).create?(data: PartialInput<T>): Promise<T> | TupdateOne?(criteria: Record<string, unknown>, data: PartialInput<T>): Promise<T | null> | T | nulldelete?(criteria: Record<string, unknown>): Promise<number> | number
IRouteSchema#
interface IRouteSchema
Le schéma d’une route : un Standard Schema par partie de l’entrée (Zod, Valibot, ArkType…). Chaque partie est facultative ; une partie déclarée est validée AVANT l’action, une partie omise est servie brute.
readonly params?: IStandardSchemaV1— Variables de route (/posts/{id}).readonly query?: IStandardSchemaV1— Query (?page=2) — celle du chemin INVOQUÉ sur le pont WebSocket.readonly body?: IStandardSchemaV1— Corps DÉCODÉ par le transport : JSON, formulaire, ou octets (Uint8Array— unBufferHTTP en est un).readonly headers?: IStandardSchemaV1— En-têtes (clés en minuscules) ; sur WebSocket, ceux du handshake.
IdempotentResponse#
interface IdempotentResponse
Réponse mémorisée d’une mutation idempotente — la forme {status, headers?, body} produite par un endpoint (data plane admin AdminApiController.runAdmin, ou action userland @Idempotent), pour la rejouer telle quelle à un rejeu (même statut, même corps, même en-têtes).
status: number— Code HTTP de la réponse mémorisée.headers?: Readonly<Record<string, string>>— En-têtes additionnels éventuels.body: unknown— Charge utile sérialisable JSON.
PlaygroundAction#
interface PlaygroundAction
Une action invocable depuis le playground (1 route du Router).
route: string— Nom de route (unique) — clé de deep-link et de rejeu.path: string | nullmethods: string[]— Transports déclarés (["POST","WEBSOCKET"]…) — majuscules.duplex: boolean— Vrai si l’action déclare AUSSI le transport WEBSOCKET (pontapi.request).action: string | null— Nom de la méthode de classe (action).variables: string[]— Variables de path ({id}→["id"]), ordre de capture.defaults: Record<string, unknown>— Défauts de variables (hors clé internecontroller).params: PlaygroundParam[]— Entrées de l’action.schema: Partial<Record<RoutePart, Record<string, unknown> | null>> | null— Le contrat d’une action à schéma — JSON Schema d’entrée de chaque partie déclarée (nullquand la bibliothèque ne sait pas le décrire).guards: PlaygroundGuards
PlaygroundController#
interface PlaygroundController
Un controller et ses actions, groupés pour la navigation Studio.
name: string— Nom de classe du controller.module: string | null— Module propriétaire (nullsi non rattaché).actions: PlaygroundAction[]
PlaygroundGuards#
interface PlaygroundGuards
Les gardes déclaratives d’une action, prêtes à afficher (badges Studio).
security: { clauses: { anyOf: string[]; subjectParam: string | null; }[]; } | null— Clauses@IsGranted/@RequireScopefusionnées (AND) —null= non gardée.scopes: string[]— Scopesapi:actiondéclarés (@RequireScope), dédupliqués.idempotent: { required: boolean; } | null—@Idempotent—required:true= clé obligatoire sur mutation.csrfProtect: booleancsrfExempt: booleansession: unknown— Intent@UseSession/@Session—nullsi la route n’en déclare pas.bypassFirewall: boolean— Route hors firewall (mécanisme d’auth lui-même).
PlaygroundParam#
interface PlaygroundParam
Un paramètre décoré d’une action (@Param/@Body/@Query…), JSON-safe.
source: string— Source d’injection (param,body,query,headers,cookie,user…).key: string | null— Clé ciblée (@Query("q")→"q") —null= l’objet complet.index: number— Position dans la signature de l’action.stream: boolean—@Body({ stream: true })— flux brut, non rejouable par formulaire.
Types (9)#
ControllerScope#
type ControllerScope = "request" | "singleton"
Portée d’instanciation d’un contrôleur.
IdempotencyOutcome#
type IdempotencyOutcome = { state: "fresh"; } | { state: "in-flight"; } | { state: "replayed"; response: IdempotentResponse; } | { state: "mismatch"; }
Verdict de IIdempotencyStore.begin pour une clé d’idempotence donnée. Aligné sur draft-ietf-httpapi-idempotency-key-header-06 §2.6/§2.7.
IdempotencyStoreFactory#
type IdempotencyStoreFactory = (ctx: IIdempotencyStoreFactoryContext) => IIdempotencyStore
Fabrique d’un store d’idempotence pour un nom donné.
RawRouteHeaders#
type RawRouteHeaders = Readonly<Record<string, string | string[]>>
En-têtes bruts, clés en minuscules.
RawRouteInput#
type RawRouteInput = { params: unknown; query: unknown; body: unknown; headers: unknown; }
L’entrée brute d’une action, avant validation.
RawRouteParams#
type RawRouteParams = Readonly<Record<string, unknown>>
Variables de route brutes (une valeur par défaut n’est pas forcément une chaîne).
RawRouteQuery#
type RawRouteQuery = Readonly<Record<string, unknown>>
Query brute.
RouteInput#
type RouteInput<S extends IRouteSchema = IRouteSchema, Stream extends boolean = false> = { params: RoutePartOf<S, "params", RawRouteParams>; query: RoutePartOf<S, "query", RawRouteQuery>; body: Stream extends true ? Readable : RoutePartOf<S, "body", unknown>; headers: RoutePartOf<S, "headers", RawRouteHeaders>; }
L’argument UNIQUE d’une action à schéma : les quatre apports de la requête, toujours présents — validés et typés par le schéma quand il les déclare, servis bruts et typés large sinon.
RoutePart#
type RoutePart = keyof IRouteSchema
Parties de l’entrée d’une route, dans l’ordre où elles sont validées.