Files
orca/src/shared/runtime-environments.ts
T

118 lines
3.5 KiB
TypeScript

import { z } from 'zod'
import { PAIRING_OFFER_VERSION, type PairingOffer } from './pairing'
export const RuntimeAccessEndpointSchema = z.object({
id: z.string().min(1),
kind: z.literal('websocket'),
label: z.string().min(1),
endpoint: z.string().min(1),
deviceToken: z.string().min(1),
publicKeyB64: z.string().min(1)
})
export type RuntimeAccessEndpoint = z.infer<typeof RuntimeAccessEndpointSchema>
export const PublicRuntimeAccessEndpointSchema = RuntimeAccessEndpointSchema.omit({
deviceToken: true,
publicKeyB64: true
})
export type PublicRuntimeAccessEndpoint = z.infer<typeof PublicRuntimeAccessEndpointSchema>
export const RuntimeEnvironmentSourceSchema = z.enum(['manual', 'ephemeral-vm'])
export type RuntimeEnvironmentSource = z.infer<typeof RuntimeEnvironmentSourceSchema>
export const KnownRuntimeEnvironmentSchema = z.object({
id: z.string().min(1),
name: z.string().min(1),
createdAt: z.number().finite(),
updatedAt: z.number().finite(),
lastUsedAt: z.number().finite().nullable(),
runtimeId: z.string().min(1).nullable(),
source: RuntimeEnvironmentSourceSchema.optional(),
endpoints: z.array(RuntimeAccessEndpointSchema).min(1),
preferredEndpointId: z.string().min(1)
})
export type KnownRuntimeEnvironment = z.infer<typeof KnownRuntimeEnvironmentSchema>
export type PublicKnownRuntimeEnvironment = Omit<KnownRuntimeEnvironment, 'endpoints'> & {
endpoints: PublicRuntimeAccessEndpoint[]
}
export function redactRuntimeEnvironment(
environment: KnownRuntimeEnvironment
): PublicKnownRuntimeEnvironment {
return {
...environment,
endpoints: environment.endpoints.map(
({ deviceToken: _deviceToken, publicKeyB64: _key, ...rest }) => rest
)
}
}
export const RuntimeEnvironmentStoreSchema = z.object({
version: z.literal(1),
environments: z.array(KnownRuntimeEnvironmentSchema)
})
export type RuntimeEnvironmentStore = z.infer<typeof RuntimeEnvironmentStoreSchema>
export function createEnvironmentFromPairingOffer(args: {
id: string
name: string
now: number
offer: PairingOffer
runtimeId?: string | null
source?: RuntimeEnvironmentSource
}): KnownRuntimeEnvironment {
const endpointId = `ws-${args.id}`
return KnownRuntimeEnvironmentSchema.parse({
id: args.id,
name: args.name,
createdAt: args.now,
updatedAt: args.now,
lastUsedAt: null,
runtimeId: args.runtimeId ?? null,
...(args.source ? { source: args.source } : {}),
endpoints: [
{
id: endpointId,
kind: 'websocket',
label: 'WebSocket',
endpoint: args.offer.endpoint,
deviceToken: args.offer.deviceToken,
publicKeyB64: args.offer.publicKeyB64
}
],
preferredEndpointId: endpointId
})
}
export function isEphemeralVmRuntimeEnvironment(
environment: Pick<PublicKnownRuntimeEnvironment, 'source'>
): boolean {
return environment.source === 'ephemeral-vm'
}
export function isUserManagedRuntimeEnvironment(
environment: Pick<PublicKnownRuntimeEnvironment, 'source'>
): boolean {
return !isEphemeralVmRuntimeEnvironment(environment)
}
export function getPreferredPairingOffer(environment: KnownRuntimeEnvironment): PairingOffer {
const endpoint =
environment.endpoints.find((entry) => entry.id === environment.preferredEndpointId) ??
environment.endpoints[0]
if (!endpoint) {
throw new Error(`Environment ${environment.name} has no access endpoints`)
}
return {
v: PAIRING_OFFER_VERSION,
endpoint: endpoint.endpoint,
deviceToken: endpoint.deviceToken,
publicKeyB64: endpoint.publicKeyB64
}
}