35 lines
1.3 KiB
TypeScript
35 lines
1.3 KiB
TypeScript
import { CommandError, NOT_FOUND_CODES, type CommandName, type GameEngine } from "@deadhead/engine";
|
|
import type { RouteName } from "@deadhead/schema";
|
|
import { ApiError, type ApiClient } from "./client";
|
|
|
|
/** The HTTP status the server sent for a code, so screens read refusals as before. */
|
|
function statusFor(code: CommandError["code"]): number {
|
|
if (code === "validation-error") return 400;
|
|
if (code === "unauthorized") return 401;
|
|
return NOT_FOUND_CODES.has(code) ? 404 : 409;
|
|
}
|
|
|
|
/**
|
|
* The typed client over the engine instead of HTTP (CR-014 Step 14.7): the
|
|
* same catalog names, inputs and outputs, and refusals as the same `ApiError`s.
|
|
*/
|
|
export function createEngineClient(engine: GameEngine): ApiClient {
|
|
let prompt: Promise<void> = Promise.resolve();
|
|
return {
|
|
async call(name, input) {
|
|
try {
|
|
const result = await engine.call(name as RouteName as CommandName, input as never);
|
|
// Notifications are asked for once the player owns the business (decision 0.5.91).
|
|
if (name === "acceptDeal") prompt = engine.requestNotificationPermission().catch(() => {});
|
|
return result as never;
|
|
} catch (error) {
|
|
if (error instanceof CommandError) {
|
|
throw new ApiError(statusFor(error.code), error.code, error.message);
|
|
}
|
|
throw error;
|
|
}
|
|
},
|
|
notificationPrompt: () => prompt,
|
|
};
|
|
}
|