You've already forked AstralRinth
forked from didirus/AstralRinth
feat: base api-client impl (#4694)
* feat: base api-client impl * fix: doc * feat: start work on module stuff * feat: migrate v2/v3 projects into module system * fix: lint & README.md contributing * refactor: remove utils old api client prototype * fix: lint * fix: api url issues * fix: baseurl in error.vue * fix: readme * fix typo in readme * Update apps/frontend/src/providers/api-client.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Calum H. <hendersoncal117@gmail.com> * Update packages/api-client/src/features/verbose-logging.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Calum H. <hendersoncal117@gmail.com> * Update packages/api-client/src/features/retry.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Calum H. <hendersoncal117@gmail.com> --------- Signed-off-by: Calum H. <hendersoncal117@gmail.com> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
106
packages/api-client/src/modules/index.ts
Normal file
106
packages/api-client/src/modules/index.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import type { AbstractModrinthClient } from '../core/abstract-client'
|
||||
import type { AbstractModule } from '../core/abstract-module'
|
||||
import { LabrinthProjectsV2Module } from './labrinth/projects/v2'
|
||||
import { LabrinthProjectsV3Module } from './labrinth/projects/v3'
|
||||
|
||||
type ModuleConstructor = new (client: AbstractModrinthClient) => AbstractModule
|
||||
|
||||
/**
|
||||
* To add a new module:
|
||||
* 1. Create your module class extending AbstractModule
|
||||
* 2. Add one line here: `<api>_<module>: YourModuleClass`
|
||||
*
|
||||
* TypeScript will automatically infer the client's field structure from this registry.
|
||||
*
|
||||
* TODO: Better way? Probably not
|
||||
*/
|
||||
export const MODULE_REGISTRY = {
|
||||
labrinth_projects_v2: LabrinthProjectsV2Module,
|
||||
labrinth_projects_v3: LabrinthProjectsV3Module,
|
||||
} as const satisfies Record<string, ModuleConstructor>
|
||||
|
||||
export type ModuleID = keyof typeof MODULE_REGISTRY
|
||||
|
||||
/**
|
||||
* Parse a module ID into [api, moduleName] tuple
|
||||
*
|
||||
* @param id - Module ID in format `<api>_<module>` (e.g., 'labrinth_projects_v2')
|
||||
* @returns Tuple of [api, moduleName] (e.g., ['labrinth', 'projects_v2'])
|
||||
* @throws Error if module ID doesn't match expected format
|
||||
*/
|
||||
export function parseModuleID(id: string): [string, string] {
|
||||
const parts = id.split('_')
|
||||
if (parts.length < 2) {
|
||||
throw new Error(
|
||||
`Invalid module ID "${id}". Expected format: <api>_<module> (e.g., "labrinth_projects_v2")`,
|
||||
)
|
||||
}
|
||||
const api = parts[0]
|
||||
const moduleName = parts.slice(1).join('_')
|
||||
return [api, moduleName]
|
||||
}
|
||||
|
||||
/**
|
||||
* Build nested module structure from flat registry
|
||||
*
|
||||
* Transforms:
|
||||
* ```
|
||||
* { labrinth_projects_v2: Constructor, labrinth_users_v2: Constructor }
|
||||
* ```
|
||||
* Into:
|
||||
* ```
|
||||
* { labrinth: { projects_v2: Constructor, users_v2: Constructor } }
|
||||
* ```
|
||||
*
|
||||
* @returns Nested structure organized by API namespace
|
||||
*/
|
||||
export function buildModuleStructure(): Record<string, Record<string, ModuleConstructor>> {
|
||||
const structure: Record<string, Record<string, ModuleConstructor>> = {}
|
||||
|
||||
for (const [id, constructor] of Object.entries(MODULE_REGISTRY)) {
|
||||
const [api, moduleName] = parseModuleID(id)
|
||||
|
||||
if (!structure[api]) {
|
||||
structure[api] = {}
|
||||
}
|
||||
|
||||
structure[api][moduleName] = constructor
|
||||
}
|
||||
|
||||
return structure
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract API name from module ID
|
||||
* @example ParseAPI<'labrinth_projects_v2'> = 'labrinth'
|
||||
*/
|
||||
type ParseAPI<T extends string> = T extends `${infer API}_${string}` ? API : never
|
||||
|
||||
/**
|
||||
* Extract module name for a given API
|
||||
* @example ParseModule<'labrinth_projects_v2', 'labrinth'> = 'projects_v2'
|
||||
*/
|
||||
type ParseModule<T extends string, API extends string> = T extends `${API}_${infer Module}`
|
||||
? Module
|
||||
: never
|
||||
|
||||
/**
|
||||
* Group registry modules by API namespace
|
||||
*
|
||||
* Transforms flat registry into nested structure at the type level:
|
||||
* ```
|
||||
* { labrinth_projects_v2: ModuleClass } → { labrinth: { projects_v2: ModuleInstance } }
|
||||
* ```
|
||||
*/
|
||||
type GroupByAPI<Registry extends Record<string, ModuleConstructor>> = {
|
||||
[API in ParseAPI<keyof Registry & string>]: {
|
||||
[Module in ParseModule<keyof Registry & string, API>]: InstanceType<
|
||||
Registry[`${API}_${Module}`]
|
||||
>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inferred client module structure
|
||||
**/
|
||||
export type InferredClientModules = GroupByAPI<typeof MODULE_REGISTRY>
|
||||
Reference in New Issue
Block a user