import type { UUID } from '../../../types.ts' import type { Db } from '../../db/mod.ts' import { Credential, Group, Machine, type Resource, Service, User, } from '../mod.ts' export type RefString = | `@ref/${T['type']}#${UUID}` | Ref export type RefResolver = ( ref: RefString, ) => T | Promise export class Ref extends String { static #toString( { uuid, type }: { uuid: UUID; type: T['type'] }, ) { return `@ref/${type}#${uuid}` as const } static parse( string: RefString, ): { type: T['type']; uuid: UUID } { const [_, value] = string.split('/') const [type, uuid] = value.split('#') as [T['type'], UUID] return { type, uuid } as const } static fromResource(resource: T): Ref { return new Ref(resource) } static fromString(string: RefString): Ref { return new Ref(Ref.parse(string)) } static dbResolver( db: Db, ): (ref: RefString) => Promise { return (ref: RefString): Promise => { const { type, uuid } = Ref.parse(ref) //@ts-expect-error force type casting to fix return db.resource[type].get({ uuid }) } } static restResolver( endpoint: string | URL, ): (ref: RefString) => Promise { return async (ref: RefString): Promise => { const { type, uuid } = Ref.parse(ref) const url = new URL(`${type}s/${uuid}`, endpoint) const response = await fetch(url) const json = await response.json() if (type === 'user') { return User.fromJSON(json) as unknown as T } if (type === 'machine') { return Machine.fromJSON(json) as unknown as T } if (type === 'service') { return Service.fromJSON(json) as unknown as T } if (type === 'group') { return Group.fromJSON(json) as unknown as T } if (type === 'credential') { return Credential.fromJSON(json) as unknown as T } throw new TypeError(`unknown ref type "${type}"`) } } private constructor({ uuid, type }: { uuid: UUID; type: T['type'] }) { super(Ref.#toString({ uuid, type })) this.#type = type this.#uuid = uuid } #type: T['type'] #uuid: UUID get type(): T['type'] { return this.#type } get uuid(): UUID { return this.#uuid } ref(resolver: RefResolver): T | Promise { return resolver(this.toString()) } toString(): `@ref/${T['type']}#${UUID}` { return Ref.#toString({ uuid: this.uuid, type: this.type }) } toJSON(): `@ref/${T['type']}#${UUID}` { return this.toString() } }