110 lines
2.6 KiB
TypeScript
110 lines
2.6 KiB
TypeScript
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<T extends Resource> =
|
|
| `@ref/${T['type']}#${UUID}`
|
|
| Ref<T>
|
|
export type RefResolver<T extends Resource> = (
|
|
ref: RefString<T>,
|
|
) => T | Promise<T>
|
|
export class Ref<T extends Resource> extends String {
|
|
static #toString<T extends Resource>(
|
|
{ uuid, type }: { uuid: UUID; type: T['type'] },
|
|
) {
|
|
return `@ref/${type}#${uuid}` as const
|
|
}
|
|
|
|
static parse<T extends Resource>(
|
|
string: RefString<T>,
|
|
): { 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<T extends Resource>(resource: T): Ref<T> {
|
|
return new Ref<T>(resource)
|
|
}
|
|
|
|
static fromString<T extends Resource>(string: RefString<T>): Ref<T> {
|
|
return new Ref<T>(Ref.parse(string))
|
|
}
|
|
|
|
static dbResolver(
|
|
db: Db,
|
|
): <T extends Resource>(ref: RefString<T>) => Promise<T> {
|
|
return <T extends Resource>(ref: RefString<T>): Promise<T> => {
|
|
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,
|
|
): <T extends Resource>(ref: RefString<T>) => Promise<T> {
|
|
return async <T extends Resource>(ref: RefString<T>): Promise<T> => {
|
|
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>): T | Promise<T> {
|
|
return resolver(this.toString())
|
|
}
|
|
|
|
toString(): `@ref/${T['type']}#${UUID}` {
|
|
return Ref.#toString<T>({ uuid: this.uuid, type: this.type })
|
|
}
|
|
|
|
toJSON(): `@ref/${T['type']}#${UUID}` {
|
|
return this.toString()
|
|
}
|
|
}
|