aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-14 20:32:47 +0900
committernsfisis <nsfisis@gmail.com>2026-02-14 20:32:47 +0900
commit9185367fcd7d95af89fac36dd892d8b064dbd94f (patch)
tree6085f0c4ab695d0f83348f32b49b5481f1da6548
parent6bd35e345a4c5d74578b0f8a5c969027e7e15f02 (diff)
downloadphperkaigi-2026-albatross-9185367fcd7d95af89fac36dd892d8b064dbd94f.tar.gz
phperkaigi-2026-albatross-9185367fcd7d95af89fac36dd892d8b064dbd94f.tar.zst
phperkaigi-2026-albatross-9185367fcd7d95af89fac36dd892d8b064dbd94f.zip
feat(openapi): generate OpenAPI specs from TypeSpec sources
Migrate hand-written OpenAPI YAML to TypeSpec (.tsp) source files. TypeSpec compiles to OpenAPI 3.0 YAML, enabling type-safe API definitions. - Add typespec/ directory with api-server and fortee definitions - Integrate TypeSpec build into `just gen` and `just build` pipelines - Update backend handler code to match new generated type names (inlined error responses, separate GameType/ProblemLanguage enums) - Regenerate frontend TypeScript types from new OpenAPI output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
-rw-r--r--.gitignore4
-rw-r--r--backend/api/generated.go254
-rw-r--r--backend/api/handler.go28
-rw-r--r--backend/api/handler_wrapper.go40
-rw-r--r--backend/gen/api/handler_wrapper_gen.go8
-rw-r--r--frontend/app/api/schema.d.ts589
-rw-r--r--justfile2
-rw-r--r--openapi/api-server.yaml630
-rw-r--r--openapi/fortee.yaml52
-rw-r--r--typespec/api-server/main.tsp17
-rw-r--r--typespec/api-server/models.tsp119
-rw-r--r--typespec/api-server/routes.tsp126
-rw-r--r--typespec/api-server/tspconfig.yaml8
-rw-r--r--typespec/fortee/main.tsp45
-rw-r--r--typespec/fortee/tspconfig.yaml8
-rw-r--r--typespec/package-lock.json1435
-rw-r--r--typespec/package.json15
17 files changed, 2638 insertions, 742 deletions
diff --git a/.gitignore b/.gitignore
index f10862a..1fbcab5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
/.env
+/typespec/node_modules/
+/typespec/tsp-output/
+/typespec/api-server/tsp-output/
+/typespec/fortee/tsp-output/
diff --git a/backend/api/generated.go b/backend/api/generated.go
index 781e709..1cc692c 100644
--- a/backend/api/generated.go
+++ b/backend/api/generated.go
@@ -34,10 +34,10 @@ const (
WrongAnswer ExecutionStatus = "wrong_answer"
)
-// Defines values for GameGameType.
+// Defines values for GameType.
const (
- Multiplayer GameGameType = "multiplayer"
- N1V1 GameGameType = "1v1"
+ Multiplayer GameType = "multiplayer"
+ N1V1 GameType = "1v1"
)
// Defines values for ProblemLanguage.
@@ -56,18 +56,18 @@ type ExecutionStatus string
// Game defines model for Game.
type Game struct {
- DisplayName string `json:"display_name"`
- DurationSeconds int `json:"duration_seconds"`
- GameID int `json:"game_id"`
- GameType GameGameType `json:"game_type"`
- IsPublic bool `json:"is_public"`
- MainPlayers []User `json:"main_players"`
- Problem Problem `json:"problem"`
- StartedAt *int64 `json:"started_at,omitempty"`
+ DisplayName string `json:"display_name"`
+ DurationSeconds int `json:"duration_seconds"`
+ GameID int `json:"game_id"`
+ GameType GameType `json:"game_type"`
+ IsPublic bool `json:"is_public"`
+ MainPlayers []User `json:"main_players"`
+ Problem Problem `json:"problem"`
+ StartedAt *int64 `json:"started_at,omitempty"`
}
-// GameGameType defines model for Game.GameType.
-type GameGameType string
+// GameType defines model for GameType.
+type GameType string
// LatestGameState defines model for LatestGameState.
type LatestGameState struct {
@@ -86,7 +86,7 @@ type Problem struct {
Title string `json:"title"`
}
-// ProblemLanguage defines model for Problem.Language.
+// ProblemLanguage defines model for ProblemLanguage.
type ProblemLanguage string
// RankingEntry defines model for RankingEntry.
@@ -122,21 +122,6 @@ type User struct {
Username string `json:"username"`
}
-// PathGameID defines model for path_game_id.
-type PathGameID = int
-
-// BadRequest defines model for BadRequest.
-type BadRequest = Error
-
-// Forbidden defines model for Forbidden.
-type Forbidden = Error
-
-// NotFound defines model for NotFound.
-type NotFound = Error
-
-// Unauthorized defines model for Unauthorized.
-type Unauthorized = Error
-
// PostGamePlayCodeJSONBody defines parameters for PostGamePlayCode.
type PostGamePlayCodeJSONBody struct {
Code string `json:"code"`
@@ -173,37 +158,37 @@ type PostLoginJSONRequestBody PostLoginJSONBody
// ServerInterface represents all server handlers.
type ServerInterface interface {
- // List games
+
// (GET /games)
GetGames(ctx echo.Context) error
- // Get a game
+
// (GET /games/{game_id})
- GetGame(ctx echo.Context, gameID PathGameID) error
- // Post the latest code
+ GetGame(ctx echo.Context, gameID int) error
+
// (POST /games/{game_id}/play/code)
- PostGamePlayCode(ctx echo.Context, gameID PathGameID) error
- // Get the latest execution result for player
+ PostGamePlayCode(ctx echo.Context, gameID int) error
+
// (GET /games/{game_id}/play/latest_state)
- GetGamePlayLatestState(ctx echo.Context, gameID PathGameID) error
- // Submit the answer
+ GetGamePlayLatestState(ctx echo.Context, gameID int) error
+
// (POST /games/{game_id}/play/submit)
- PostGamePlaySubmit(ctx echo.Context, gameID PathGameID) error
- // Get all the latest game states of the main players
+ PostGamePlaySubmit(ctx echo.Context, gameID int) error
+
// (GET /games/{game_id}/watch/latest_states)
- GetGameWatchLatestStates(ctx echo.Context, gameID PathGameID) error
- // Get the latest player ranking
+ GetGameWatchLatestStates(ctx echo.Context, gameID int) error
+
// (GET /games/{game_id}/watch/ranking)
- GetGameWatchRanking(ctx echo.Context, gameID PathGameID) error
- // User login
+ GetGameWatchRanking(ctx echo.Context, gameID int) error
+
// (POST /login)
PostLogin(ctx echo.Context) error
- // User logout
+
// (POST /logout)
PostLogout(ctx echo.Context) error
- // Get current user
+
// (GET /me)
GetMe(ctx echo.Context) error
- // Get tournament bracket data
+
// (GET /tournament)
GetTournament(ctx echo.Context, params GetTournamentParams) error
}
@@ -226,7 +211,7 @@ func (w *ServerInterfaceWrapper) GetGames(ctx echo.Context) error {
func (w *ServerInterfaceWrapper) GetGame(ctx echo.Context) error {
var err error
// ------------- Path parameter "game_id" -------------
- var gameID PathGameID
+ var gameID int
err = runtime.BindStyledParameterWithOptions("simple", "game_id", ctx.Param("game_id"), &gameID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
@@ -242,7 +227,7 @@ func (w *ServerInterfaceWrapper) GetGame(ctx echo.Context) error {
func (w *ServerInterfaceWrapper) PostGamePlayCode(ctx echo.Context) error {
var err error
// ------------- Path parameter "game_id" -------------
- var gameID PathGameID
+ var gameID int
err = runtime.BindStyledParameterWithOptions("simple", "game_id", ctx.Param("game_id"), &gameID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
@@ -258,7 +243,7 @@ func (w *ServerInterfaceWrapper) PostGamePlayCode(ctx echo.Context) error {
func (w *ServerInterfaceWrapper) GetGamePlayLatestState(ctx echo.Context) error {
var err error
// ------------- Path parameter "game_id" -------------
- var gameID PathGameID
+ var gameID int
err = runtime.BindStyledParameterWithOptions("simple", "game_id", ctx.Param("game_id"), &gameID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
@@ -274,7 +259,7 @@ func (w *ServerInterfaceWrapper) GetGamePlayLatestState(ctx echo.Context) error
func (w *ServerInterfaceWrapper) PostGamePlaySubmit(ctx echo.Context) error {
var err error
// ------------- Path parameter "game_id" -------------
- var gameID PathGameID
+ var gameID int
err = runtime.BindStyledParameterWithOptions("simple", "game_id", ctx.Param("game_id"), &gameID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
@@ -290,7 +275,7 @@ func (w *ServerInterfaceWrapper) PostGamePlaySubmit(ctx echo.Context) error {
func (w *ServerInterfaceWrapper) GetGameWatchLatestStates(ctx echo.Context) error {
var err error
// ------------- Path parameter "game_id" -------------
- var gameID PathGameID
+ var gameID int
err = runtime.BindStyledParameterWithOptions("simple", "game_id", ctx.Param("game_id"), &gameID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
@@ -306,7 +291,7 @@ func (w *ServerInterfaceWrapper) GetGameWatchLatestStates(ctx echo.Context) erro
func (w *ServerInterfaceWrapper) GetGameWatchRanking(ctx echo.Context) error {
var err error
// ------------- Path parameter "game_id" -------------
- var gameID PathGameID
+ var gameID int
err = runtime.BindStyledParameterWithOptions("simple", "game_id", ctx.Param("game_id"), &gameID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
@@ -353,35 +338,35 @@ func (w *ServerInterfaceWrapper) GetTournament(ctx echo.Context) error {
var params GetTournamentParams
// ------------- Required query parameter "game1" -------------
- err = runtime.BindQueryParameter("form", true, true, "game1", ctx.QueryParams(), &params.Game1)
+ err = runtime.BindQueryParameter("form", false, true, "game1", ctx.QueryParams(), &params.Game1)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter game1: %s", err))
}
// ------------- Required query parameter "game2" -------------
- err = runtime.BindQueryParameter("form", true, true, "game2", ctx.QueryParams(), &params.Game2)
+ err = runtime.BindQueryParameter("form", false, true, "game2", ctx.QueryParams(), &params.Game2)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter game2: %s", err))
}
// ------------- Required query parameter "game3" -------------
- err = runtime.BindQueryParameter("form", true, true, "game3", ctx.QueryParams(), &params.Game3)
+ err = runtime.BindQueryParameter("form", false, true, "game3", ctx.QueryParams(), &params.Game3)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter game3: %s", err))
}
// ------------- Required query parameter "game4" -------------
- err = runtime.BindQueryParameter("form", true, true, "game4", ctx.QueryParams(), &params.Game4)
+ err = runtime.BindQueryParameter("form", false, true, "game4", ctx.QueryParams(), &params.Game4)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter game4: %s", err))
}
// ------------- Required query parameter "game5" -------------
- err = runtime.BindQueryParameter("form", true, true, "game5", ctx.QueryParams(), &params.Game5)
+ err = runtime.BindQueryParameter("form", false, true, "game5", ctx.QueryParams(), &params.Game5)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter game5: %s", err))
}
@@ -433,14 +418,6 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
}
-type BadRequestJSONResponse Error
-
-type ForbiddenJSONResponse Error
-
-type NotFoundJSONResponse Error
-
-type UnauthorizedJSONResponse Error
-
type GetGamesRequestObject struct {
}
@@ -459,7 +436,7 @@ func (response GetGames200JSONResponse) VisitGetGamesResponse(w http.ResponseWri
return json.NewEncoder(w).Encode(response)
}
-type GetGames401JSONResponse struct{ UnauthorizedJSONResponse }
+type GetGames401JSONResponse Error
func (response GetGames401JSONResponse) VisitGetGamesResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -468,7 +445,7 @@ func (response GetGames401JSONResponse) VisitGetGamesResponse(w http.ResponseWri
return json.NewEncoder(w).Encode(response)
}
-type GetGames403JSONResponse struct{ ForbiddenJSONResponse }
+type GetGames403JSONResponse Error
func (response GetGames403JSONResponse) VisitGetGamesResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -478,7 +455,7 @@ func (response GetGames403JSONResponse) VisitGetGamesResponse(w http.ResponseWri
}
type GetGameRequestObject struct {
- GameID PathGameID `json:"game_id"`
+ GameID int `json:"game_id"`
}
type GetGameResponseObject interface {
@@ -496,7 +473,7 @@ func (response GetGame200JSONResponse) VisitGetGameResponse(w http.ResponseWrite
return json.NewEncoder(w).Encode(response)
}
-type GetGame401JSONResponse struct{ UnauthorizedJSONResponse }
+type GetGame401JSONResponse Error
func (response GetGame401JSONResponse) VisitGetGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -505,7 +482,7 @@ func (response GetGame401JSONResponse) VisitGetGameResponse(w http.ResponseWrite
return json.NewEncoder(w).Encode(response)
}
-type GetGame403JSONResponse struct{ ForbiddenJSONResponse }
+type GetGame403JSONResponse Error
func (response GetGame403JSONResponse) VisitGetGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -514,7 +491,7 @@ func (response GetGame403JSONResponse) VisitGetGameResponse(w http.ResponseWrite
return json.NewEncoder(w).Encode(response)
}
-type GetGame404JSONResponse struct{ NotFoundJSONResponse }
+type GetGame404JSONResponse Error
func (response GetGame404JSONResponse) VisitGetGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -524,7 +501,7 @@ func (response GetGame404JSONResponse) VisitGetGameResponse(w http.ResponseWrite
}
type PostGamePlayCodeRequestObject struct {
- GameID PathGameID `json:"game_id"`
+ GameID int `json:"game_id"`
Body *PostGamePlayCodeJSONRequestBody
}
@@ -540,7 +517,7 @@ func (response PostGamePlayCode200Response) VisitPostGamePlayCodeResponse(w http
return nil
}
-type PostGamePlayCode401JSONResponse struct{ UnauthorizedJSONResponse }
+type PostGamePlayCode401JSONResponse Error
func (response PostGamePlayCode401JSONResponse) VisitPostGamePlayCodeResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -549,7 +526,7 @@ func (response PostGamePlayCode401JSONResponse) VisitPostGamePlayCodeResponse(w
return json.NewEncoder(w).Encode(response)
}
-type PostGamePlayCode403JSONResponse struct{ ForbiddenJSONResponse }
+type PostGamePlayCode403JSONResponse Error
func (response PostGamePlayCode403JSONResponse) VisitPostGamePlayCodeResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -558,7 +535,7 @@ func (response PostGamePlayCode403JSONResponse) VisitPostGamePlayCodeResponse(w
return json.NewEncoder(w).Encode(response)
}
-type PostGamePlayCode404JSONResponse struct{ NotFoundJSONResponse }
+type PostGamePlayCode404JSONResponse Error
func (response PostGamePlayCode404JSONResponse) VisitPostGamePlayCodeResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -568,7 +545,7 @@ func (response PostGamePlayCode404JSONResponse) VisitPostGamePlayCodeResponse(w
}
type GetGamePlayLatestStateRequestObject struct {
- GameID PathGameID `json:"game_id"`
+ GameID int `json:"game_id"`
}
type GetGamePlayLatestStateResponseObject interface {
@@ -586,7 +563,7 @@ func (response GetGamePlayLatestState200JSONResponse) VisitGetGamePlayLatestStat
return json.NewEncoder(w).Encode(response)
}
-type GetGamePlayLatestState401JSONResponse struct{ UnauthorizedJSONResponse }
+type GetGamePlayLatestState401JSONResponse Error
func (response GetGamePlayLatestState401JSONResponse) VisitGetGamePlayLatestStateResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -595,7 +572,7 @@ func (response GetGamePlayLatestState401JSONResponse) VisitGetGamePlayLatestStat
return json.NewEncoder(w).Encode(response)
}
-type GetGamePlayLatestState403JSONResponse struct{ ForbiddenJSONResponse }
+type GetGamePlayLatestState403JSONResponse Error
func (response GetGamePlayLatestState403JSONResponse) VisitGetGamePlayLatestStateResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -604,7 +581,7 @@ func (response GetGamePlayLatestState403JSONResponse) VisitGetGamePlayLatestStat
return json.NewEncoder(w).Encode(response)
}
-type GetGamePlayLatestState404JSONResponse struct{ NotFoundJSONResponse }
+type GetGamePlayLatestState404JSONResponse Error
func (response GetGamePlayLatestState404JSONResponse) VisitGetGamePlayLatestStateResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -614,7 +591,7 @@ func (response GetGamePlayLatestState404JSONResponse) VisitGetGamePlayLatestStat
}
type PostGamePlaySubmitRequestObject struct {
- GameID PathGameID `json:"game_id"`
+ GameID int `json:"game_id"`
Body *PostGamePlaySubmitJSONRequestBody
}
@@ -630,7 +607,7 @@ func (response PostGamePlaySubmit200Response) VisitPostGamePlaySubmitResponse(w
return nil
}
-type PostGamePlaySubmit401JSONResponse struct{ UnauthorizedJSONResponse }
+type PostGamePlaySubmit401JSONResponse Error
func (response PostGamePlaySubmit401JSONResponse) VisitPostGamePlaySubmitResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -639,7 +616,7 @@ func (response PostGamePlaySubmit401JSONResponse) VisitPostGamePlaySubmitRespons
return json.NewEncoder(w).Encode(response)
}
-type PostGamePlaySubmit403JSONResponse struct{ ForbiddenJSONResponse }
+type PostGamePlaySubmit403JSONResponse Error
func (response PostGamePlaySubmit403JSONResponse) VisitPostGamePlaySubmitResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -648,7 +625,7 @@ func (response PostGamePlaySubmit403JSONResponse) VisitPostGamePlaySubmitRespons
return json.NewEncoder(w).Encode(response)
}
-type PostGamePlaySubmit404JSONResponse struct{ NotFoundJSONResponse }
+type PostGamePlaySubmit404JSONResponse Error
func (response PostGamePlaySubmit404JSONResponse) VisitPostGamePlaySubmitResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -658,7 +635,7 @@ func (response PostGamePlaySubmit404JSONResponse) VisitPostGamePlaySubmitRespons
}
type GetGameWatchLatestStatesRequestObject struct {
- GameID PathGameID `json:"game_id"`
+ GameID int `json:"game_id"`
}
type GetGameWatchLatestStatesResponseObject interface {
@@ -676,7 +653,7 @@ func (response GetGameWatchLatestStates200JSONResponse) VisitGetGameWatchLatestS
return json.NewEncoder(w).Encode(response)
}
-type GetGameWatchLatestStates401JSONResponse struct{ UnauthorizedJSONResponse }
+type GetGameWatchLatestStates401JSONResponse Error
func (response GetGameWatchLatestStates401JSONResponse) VisitGetGameWatchLatestStatesResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -685,7 +662,7 @@ func (response GetGameWatchLatestStates401JSONResponse) VisitGetGameWatchLatestS
return json.NewEncoder(w).Encode(response)
}
-type GetGameWatchLatestStates403JSONResponse struct{ ForbiddenJSONResponse }
+type GetGameWatchLatestStates403JSONResponse Error
func (response GetGameWatchLatestStates403JSONResponse) VisitGetGameWatchLatestStatesResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -694,7 +671,7 @@ func (response GetGameWatchLatestStates403JSONResponse) VisitGetGameWatchLatestS
return json.NewEncoder(w).Encode(response)
}
-type GetGameWatchLatestStates404JSONResponse struct{ NotFoundJSONResponse }
+type GetGameWatchLatestStates404JSONResponse Error
func (response GetGameWatchLatestStates404JSONResponse) VisitGetGameWatchLatestStatesResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -704,7 +681,7 @@ func (response GetGameWatchLatestStates404JSONResponse) VisitGetGameWatchLatestS
}
type GetGameWatchRankingRequestObject struct {
- GameID PathGameID `json:"game_id"`
+ GameID int `json:"game_id"`
}
type GetGameWatchRankingResponseObject interface {
@@ -722,7 +699,7 @@ func (response GetGameWatchRanking200JSONResponse) VisitGetGameWatchRankingRespo
return json.NewEncoder(w).Encode(response)
}
-type GetGameWatchRanking401JSONResponse struct{ UnauthorizedJSONResponse }
+type GetGameWatchRanking401JSONResponse Error
func (response GetGameWatchRanking401JSONResponse) VisitGetGameWatchRankingResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -731,7 +708,7 @@ func (response GetGameWatchRanking401JSONResponse) VisitGetGameWatchRankingRespo
return json.NewEncoder(w).Encode(response)
}
-type GetGameWatchRanking403JSONResponse struct{ ForbiddenJSONResponse }
+type GetGameWatchRanking403JSONResponse Error
func (response GetGameWatchRanking403JSONResponse) VisitGetGameWatchRankingResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -740,7 +717,7 @@ func (response GetGameWatchRanking403JSONResponse) VisitGetGameWatchRankingRespo
return json.NewEncoder(w).Encode(response)
}
-type GetGameWatchRanking404JSONResponse struct{ NotFoundJSONResponse }
+type GetGameWatchRanking404JSONResponse Error
func (response GetGameWatchRanking404JSONResponse) VisitGetGameWatchRankingResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -768,7 +745,7 @@ func (response PostLogin200JSONResponse) VisitPostLoginResponse(w http.ResponseW
return json.NewEncoder(w).Encode(response)
}
-type PostLogin401JSONResponse struct{ UnauthorizedJSONResponse }
+type PostLogin401JSONResponse Error
func (response PostLogin401JSONResponse) VisitPostLoginResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -792,7 +769,7 @@ func (response PostLogout200Response) VisitPostLogoutResponse(w http.ResponseWri
return nil
}
-type PostLogout401JSONResponse struct{ UnauthorizedJSONResponse }
+type PostLogout401JSONResponse Error
func (response PostLogout401JSONResponse) VisitPostLogoutResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -819,7 +796,7 @@ func (response GetMe200JSONResponse) VisitGetMeResponse(w http.ResponseWriter) e
return json.NewEncoder(w).Encode(response)
}
-type GetMe401JSONResponse struct{ UnauthorizedJSONResponse }
+type GetMe401JSONResponse Error
func (response GetMe401JSONResponse) VisitGetMeResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -847,7 +824,7 @@ func (response GetTournament200JSONResponse) VisitGetTournamentResponse(w http.R
return json.NewEncoder(w).Encode(response)
}
-type GetTournament401JSONResponse struct{ UnauthorizedJSONResponse }
+type GetTournament401JSONResponse Error
func (response GetTournament401JSONResponse) VisitGetTournamentResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -856,7 +833,7 @@ func (response GetTournament401JSONResponse) VisitGetTournamentResponse(w http.R
return json.NewEncoder(w).Encode(response)
}
-type GetTournament403JSONResponse struct{ ForbiddenJSONResponse }
+type GetTournament403JSONResponse Error
func (response GetTournament403JSONResponse) VisitGetTournamentResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -865,7 +842,7 @@ func (response GetTournament403JSONResponse) VisitGetTournamentResponse(w http.R
return json.NewEncoder(w).Encode(response)
}
-type GetTournament404JSONResponse struct{ NotFoundJSONResponse }
+type GetTournament404JSONResponse Error
func (response GetTournament404JSONResponse) VisitGetTournamentResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -876,37 +853,37 @@ func (response GetTournament404JSONResponse) VisitGetTournamentResponse(w http.R
// StrictServerInterface represents all server handlers.
type StrictServerInterface interface {
- // List games
+
// (GET /games)
GetGames(ctx context.Context, request GetGamesRequestObject) (GetGamesResponseObject, error)
- // Get a game
+
// (GET /games/{game_id})
GetGame(ctx context.Context, request GetGameRequestObject) (GetGameResponseObject, error)
- // Post the latest code
+
// (POST /games/{game_id}/play/code)
PostGamePlayCode(ctx context.Context, request PostGamePlayCodeRequestObject) (PostGamePlayCodeResponseObject, error)
- // Get the latest execution result for player
+
// (GET /games/{game_id}/play/latest_state)
GetGamePlayLatestState(ctx context.Context, request GetGamePlayLatestStateRequestObject) (GetGamePlayLatestStateResponseObject, error)
- // Submit the answer
+
// (POST /games/{game_id}/play/submit)
PostGamePlaySubmit(ctx context.Context, request PostGamePlaySubmitRequestObject) (PostGamePlaySubmitResponseObject, error)
- // Get all the latest game states of the main players
+
// (GET /games/{game_id}/watch/latest_states)
GetGameWatchLatestStates(ctx context.Context, request GetGameWatchLatestStatesRequestObject) (GetGameWatchLatestStatesResponseObject, error)
- // Get the latest player ranking
+
// (GET /games/{game_id}/watch/ranking)
GetGameWatchRanking(ctx context.Context, request GetGameWatchRankingRequestObject) (GetGameWatchRankingResponseObject, error)
- // User login
+
// (POST /login)
PostLogin(ctx context.Context, request PostLoginRequestObject) (PostLoginResponseObject, error)
- // User logout
+
// (POST /logout)
PostLogout(ctx context.Context, request PostLogoutRequestObject) (PostLogoutResponseObject, error)
- // Get current user
+
// (GET /me)
GetMe(ctx context.Context, request GetMeRequestObject) (GetMeResponseObject, error)
- // Get tournament bracket data
+
// (GET /tournament)
GetTournament(ctx context.Context, request GetTournamentRequestObject) (GetTournamentResponseObject, error)
}
@@ -947,7 +924,7 @@ func (sh *strictHandler) GetGames(ctx echo.Context) error {
}
// GetGame operation middleware
-func (sh *strictHandler) GetGame(ctx echo.Context, gameID PathGameID) error {
+func (sh *strictHandler) GetGame(ctx echo.Context, gameID int) error {
var request GetGameRequestObject
request.GameID = gameID
@@ -972,7 +949,7 @@ func (sh *strictHandler) GetGame(ctx echo.Context, gameID PathGameID) error {
}
// PostGamePlayCode operation middleware
-func (sh *strictHandler) PostGamePlayCode(ctx echo.Context, gameID PathGameID) error {
+func (sh *strictHandler) PostGamePlayCode(ctx echo.Context, gameID int) error {
var request PostGamePlayCodeRequestObject
request.GameID = gameID
@@ -1003,7 +980,7 @@ func (sh *strictHandler) PostGamePlayCode(ctx echo.Context, gameID PathGameID) e
}
// GetGamePlayLatestState operation middleware
-func (sh *strictHandler) GetGamePlayLatestState(ctx echo.Context, gameID PathGameID) error {
+func (sh *strictHandler) GetGamePlayLatestState(ctx echo.Context, gameID int) error {
var request GetGamePlayLatestStateRequestObject
request.GameID = gameID
@@ -1028,7 +1005,7 @@ func (sh *strictHandler) GetGamePlayLatestState(ctx echo.Context, gameID PathGam
}
// PostGamePlaySubmit operation middleware
-func (sh *strictHandler) PostGamePlaySubmit(ctx echo.Context, gameID PathGameID) error {
+func (sh *strictHandler) PostGamePlaySubmit(ctx echo.Context, gameID int) error {
var request PostGamePlaySubmitRequestObject
request.GameID = gameID
@@ -1059,7 +1036,7 @@ func (sh *strictHandler) PostGamePlaySubmit(ctx echo.Context, gameID PathGameID)
}
// GetGameWatchLatestStates operation middleware
-func (sh *strictHandler) GetGameWatchLatestStates(ctx echo.Context, gameID PathGameID) error {
+func (sh *strictHandler) GetGameWatchLatestStates(ctx echo.Context, gameID int) error {
var request GetGameWatchLatestStatesRequestObject
request.GameID = gameID
@@ -1084,7 +1061,7 @@ func (sh *strictHandler) GetGameWatchLatestStates(ctx echo.Context, gameID PathG
}
// GetGameWatchRanking operation middleware
-func (sh *strictHandler) GetGameWatchRanking(ctx echo.Context, gameID PathGameID) error {
+func (sh *strictHandler) GetGameWatchRanking(ctx echo.Context, gameID int) error {
var request GetGameWatchRankingRequestObject
request.GameID = gameID
@@ -1211,33 +1188,28 @@ func (sh *strictHandler) GetTournament(ctx echo.Context, params GetTournamentPar
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "H4sIAAAAAAAC/+xaW2/buBL+KwTPAfqixs7lBD3Zp7TbFl20hdELFouiEGhpbDOlSJWk6noL/ffFkJJ1",
- "t+XELRJg3yKJM5yZ75vhDOMfNFJJqiRIa+jVD5oyzRKwoIsnuwqXLIGQx/jMJb1yL2lAJUuAXtHya0A1",
- "fM24hpheWZ1BQE20goShmN2kuJRLC0vQNM9zXG1SJQ24fZ6y+B18zcBYfIqUtCDdnyxNBY+Y5UpOboyS",
- "+K7S+18NC3pF/zOpfJj4r2byXGtVbBWDiTRPUQm9wr2ILjbLA/pC6TmPY5A/f+dqqzygb5V9oTIZ//xt",
- "3ypLFm6rPKAfJcvsSmn+N/yCrRu74edCAhV6ISSZViloyz0VEjCGLQH/hO8sSQUy55X8xgSvcAtKShmr",
- "uVxST6iSfp+2Sj5vF6r5DUQO8OffIcrQvveW2cztCTJLUEwqCUjkTErUGlCTRREYQwO61kouQybNGjRu",
- "zxNQGRqC4eACQnDuOGH8uH1G0mvJRPHic1Bzq1LfciegL112tYMTc5MKtgll8bVShevJaZ+mONMO09BA",
- "pGRsGnLnl9Ogk54BraX8dunp4EL/ugrj6Tc0JMmE5WgttLz2nzt2chOm2VzwqLGrLyXF4rlSAphLn4Rx",
- "GXrtziNuITH7ePrReKsLdUxrtsHnVKu5gGSf+KxYhjy2TFuIQ2Yb1v7/4vLyycWTaTeoAf3+eKkeV28v",
- "LzqsrUppFdZ6XIIm/j3QVq60ItSXCK+ZBWOROJgJPWybg7GhiZSG0GTzhNvdHstMCDbvgLYzApg/cYvK",
- "EK0UebQCIRRZKy3iR7/18cUZ1mToCCs8dkXe76xprTLRBsvZXVoRDMZqu10fArOKd608r1fRemw+rLgh",
- "3BBGKqA7kRFMLrOyhhY5ma5StGXNF7aZjf5DR0ehflQRMO5jeGskLbeiJVlEpq+itXCoGVpqap5CtXg0",
- "Te1D5B2TX7hcPpdWb7qwjPVwgIO16PqyOLJcDTC9B4gRWXqLulRYW5G9xfDBaH5QmcZS5fuL1jnPbLSC",
- "8bW70vUGJbtlvN0CFPp32+V1dYwbff750JyORbJYHvYgOqz97DDtZ2O1r7mUnoS71g2cUX1hdXYc0LP8",
- "oVaS/K6gtxuIlAzdpNEQmfCELcFMbtRKntyky4FGgsUJb1bOBROmt5EQbA6iuYmxbLEYk8aZAd2hydl5",
- "X7BxaTcC6MbeClfuUlPS6QS2PpcOdfFBtVwulBvIfMWl12LOrFbGkLJNJWuYk+vZKxrQb6CNb+OnJ+cn",
- "U/RCpSBZyukVPT+Znkyx32B25WCeIDN88oBLd+SA605exdifgus0DG1NfmfT6UFjSDdPx5cQ11Pvqxte",
- "ZX/4msPNa24sUQviJfKAXkwHC8HW50lzJEKh8/1CtcnRlfkkYXg+eROK/fOgwGDyo0jSfB8aDr9q3v/U",
- "b0a1ZNK4D8g/Hx3LcQj2IDYKsGsXql+GFEpc7JfYXgQ0oX0JlrDC4B5oJ5j9k7IhSZXpQXmmfHs/E2zz",
- "zLerd4bbzd9PVby5A9K3bBT7GvB+2Js3UXk/TZvceO9H8UUmxIZkacxsCfq9ZwrCTOwKiHADHXGBGeaM",
- "XxWacubbVSCQOX5M9CPifaoXWwd2FYz2jNsmkVcypnj8pTJdRhg9Il704RSTGkOgnG2JBpMJSxZKk6LL",
- "HyaO7/rHlZv3fu2/BWdcwdkOVA+ETx5eR6niSrSXNmucrhoFZ2+D+CeK1EqOuX81x/3F4pijCBOzxoqD",
- "ilGXSz3VaVwzei1EPcOrCmWwRcUvCeOSlPeBD6gHOtCxYR5qf7czioHFPdC9Il/N/lHjTuMua9/YUyof",
- "w7WZizUpRR7kEZi2fEDaCLX0twbD59trt+RY51LKjFkr3bxA2L49PTunA5cOd7hJkOXcV2x9hwPtll5n",
- "ZuzVZ4/5owjaOFuRdCAtWnfr87XBJLSNeK6UtFGZ3csb/+/CAxsDoZZLiAnKHtFypw5NT3ZOAG+APjDg",
- "n2Vag7QEBYi77DpC1LByRDXFPnS2cbc9FMLaDXjnKHE/pviagd40f01xethvKYJhTWdH03R+NE0XR9P0",
- "v8M0HfcwbsI/7h8XHV7XlIxhd6WJxMyyh3TwVpbPNYu+QOlBnuf/BAAA//9b1VG2giQAAA==",
+ "H4sIAAAAAAAC/+xaW2/bNhT+KwK3R9V2LtuD37IhKAqkgLFm2ENRCJR0bLOjSJWXOF7g/z6QlKwbJctN",
+ "ssGp3xyJPNfvO+eQyhNKeJZzBkxJNH9CMllDhu3PWyG4MD9ywXMQioB9nIGUeAXmp9rmgOZIKkHYCu12",
+ "IRLwTRMBKZp/3i/8EpYLefwVEoV2Ibp9hEQrwtknhZW2coHpzGxjnAEKkdCMGakhkjpJQEoUoo3gbBVh",
+ "JjcgUIgUyYBrhULrA6EQgTXZbjYv938TpkAwTIsHlUWl6SF6jzPoOpsSmVO8jVjxtrMt1QIbPyIJCWep",
+ "rC0ySlcgzKoVziAi6cBL9/gJ/Sxgiebop2mVlmmRk6kx8d6s24WIyCjXMSVJTWbMOQXMzOsMExYZy0FY",
+ "k4iCTB6S/6d0BhXisBB4a/7OBY8pZIe2L4pluxBJhYWCNMLK43KIHt+t+Lvq6a/XHeyUAatHp+502MyM",
+ "Jw+V2a1o+OC4j2wNhxcPF2arpoq4rV7Y3GEFUpn9BskeBMUgVSQTLiCSOs6IquLCNKU4poDmSmgIR8XJ",
+ "YD31Q9EqGSHX5adg3VBC2yRtJ8laUuoNez3dq/NFflFhq8U8kIkguVHv9ZZittJFHRqByrtyeYXnXkJK",
+ "nOUUot5AK6LoiPpXU1PuCRtu1ZxoKh0I1F3N7RKp+To3AjZkqbwY/QOzvwlb3TIltt1Al272oKYSU7Bg",
+ "ZBHZo9ET3xYPvqM+FLZU4GshrjeM91wLUzKY8nQ2rJI1jK+XlayPZme3dLYbYiF/2C4nq2PcYAtx4bgY",
+ "m5tieTSQI7fk8jiJl0MSN4QxB5/2u57a7wuT1Xd8lyYJZ1GO1dr/VkY4zQjzt1KKY6CjCKIliN4UmZc9",
+ "9rX8L8XU9nTa3d7k0r5usIxYwpbcKnQVC93QGCvBpQzKiSjYQBzcLD6gED2AkLbaotnkajIzRvMcGM4J",
+ "mqOryWwyM00Vq7WN+dSkySETLJdMQmwL/pCiOXoPtimaPixA5pxJt/hyNnNFh6mCgzjPKUnszulX6aq9",
+ "g5efBOP5aUe6Q6R0Iv3ha/QgdL+GwOwEqYI1loGdSyGFdGKUXM8ujnJssPHaKdVjwo2dhAMiA82wVmsu",
+ "yD97/Vf/pf4lFzFJU2ATs24XFniYPhXs3R1ChsWSwBkoO6J+fkKGgBZfKESOKbUxsEqZI1/lSKeafHlx",
+ "yI0DmgdYZ1w9B1dG+fXrKzfxlyAeQAQJZoyrYElYGqgqLZAGAiTXIoE+uE9NdZ6Ws1TOpQf5C+5OCguK",
+ "t7+72fk1KWBN/42n22egv2cG9h0E/FBvGr3zU/PMhzfKB2pPx5Esj8ZDDcGwwp2m3Un6RPrD3rehWLdv",
+ "CdoEckLOzeKHIoc7so5rF5/c2nPDODeMt8qJDVbJutExDp4u/zJbaj1DnlTTsL9wmhKzBdNFY8VR3aRL",
+ "JE97OR9yfzQuCXfvO4pFxR3xqRCo5tqoq6DGFfihK6FS+Jkvb5UvlK/cdW//5HVnl7zUWJRjKTdcpN77",
+ "5+Ouhll5eVZIfMYY9Z3OaDn2M5DH/JNn1R5BXKuDEHL/nnDaI6xzOBs8wH8EdIbYC0dcNT5V9kW+9kGz",
+ "07vhMaf2PLbEVELoevk3DWLbbOYXx7XycLzky1eTfPVqkq9fTfIv/+fI1ITTuO/aHXrVhJynozc5He12",
+ "/wYAAP//Hl9qBRMoAAA=",
}
// GetSwagger returns the content of the embedded swagger specification file
diff --git a/backend/api/handler.go b/backend/api/handler.go
index 3b04665..25aea01 100644
--- a/backend/api/handler.go
+++ b/backend/api/handler.go
@@ -53,18 +53,14 @@ func (h *Handler) PostLogin(ctx context.Context, request PostLoginRequestObject)
msg = "ユーザー名またはパスワードが誤っています"
}
return PostLogin401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: msg,
- },
+ Message: msg,
}, nil
}
dbUser, err := h.q.GetUserByID(ctx, int32(userID))
if err != nil {
return PostLogin401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "ログインに失敗しました",
- },
+ Message: "ログインに失敗しました",
}, nil
}
@@ -100,9 +96,7 @@ func (h *Handler) GetMe(ctx context.Context, _ GetMeRequestObject, claims *auth.
dbUser, err := h.q.GetUserByID(ctx, int32(claims.UserID))
if err != nil {
return GetMe401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return GetMe200JSONResponse{
@@ -157,7 +151,7 @@ func (h *Handler) GetGames(ctx context.Context, _ GetGamesRequestObject, _ *auth
}
games[i] = Game{
GameID: int(row.GameID),
- GameType: GameGameType(row.GameType),
+ GameType: GameType(row.GameType),
IsPublic: row.IsPublic,
DisplayName: row.DisplayName,
DurationSeconds: int(row.DurationSeconds),
@@ -200,18 +194,14 @@ func (h *Handler) GetGame(ctx context.Context, request GetGameRequestObject, use
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return GetGame404JSONResponse{
- NotFoundJSONResponse: NotFoundJSONResponse{
- Message: "Game not found",
- },
+ Message: "Game not found",
}, nil
}
return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
if !row.IsPublic && !user.IsAdmin {
return GetGame404JSONResponse{
- NotFoundJSONResponse: NotFoundJSONResponse{
- Message: "Game not found",
- },
+ Message: "Game not found",
}, nil
}
var startedAt *int64
@@ -236,7 +226,7 @@ func (h *Handler) GetGame(ctx context.Context, request GetGameRequestObject, use
}
game := Game{
GameID: int(row.GameID),
- GameType: GameGameType(row.GameType),
+ GameType: GameType(row.GameType),
IsPublic: row.IsPublic,
DisplayName: row.DisplayName,
DurationSeconds: int(row.DurationSeconds),
@@ -312,9 +302,7 @@ func (h *Handler) GetGameWatchLatestStates(ctx context.Context, request GetGameW
if int(row.UserID) == user.UserID && !user.IsAdmin {
return GetGameWatchLatestStates403JSONResponse{
- ForbiddenJSONResponse: ForbiddenJSONResponse{
- Message: "You are one of the main players of this game",
- },
+ Message: "You are one of the main players of this game",
}, nil
}
}
diff --git a/backend/api/handler_wrapper.go b/backend/api/handler_wrapper.go
index b88ddb2..5feaac7 100644
--- a/backend/api/handler_wrapper.go
+++ b/backend/api/handler_wrapper.go
@@ -29,9 +29,7 @@ func (h *HandlerWrapper) GetGame(ctx context.Context, request GetGameRequestObje
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return GetGame401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return h.impl.GetGame(ctx, request, user)
@@ -41,9 +39,7 @@ func (h *HandlerWrapper) GetGamePlayLatestState(ctx context.Context, request Get
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return GetGamePlayLatestState401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return h.impl.GetGamePlayLatestState(ctx, request, user)
@@ -53,9 +49,7 @@ func (h *HandlerWrapper) GetGameWatchLatestStates(ctx context.Context, request G
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return GetGameWatchLatestStates401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return h.impl.GetGameWatchLatestStates(ctx, request, user)
@@ -65,9 +59,7 @@ func (h *HandlerWrapper) GetGameWatchRanking(ctx context.Context, request GetGam
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return GetGameWatchRanking401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return h.impl.GetGameWatchRanking(ctx, request, user)
@@ -77,9 +69,7 @@ func (h *HandlerWrapper) GetGames(ctx context.Context, request GetGamesRequestOb
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return GetGames401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return h.impl.GetGames(ctx, request, user)
@@ -89,9 +79,7 @@ func (h *HandlerWrapper) GetMe(ctx context.Context, request GetMeRequestObject)
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return GetMe401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return h.impl.GetMe(ctx, request, user)
@@ -101,9 +89,7 @@ func (h *HandlerWrapper) GetTournament(ctx context.Context, request GetTournamen
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return GetTournament401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return h.impl.GetTournament(ctx, request, user)
@@ -113,9 +99,7 @@ func (h *HandlerWrapper) PostGamePlayCode(ctx context.Context, request PostGameP
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return PostGamePlayCode401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return h.impl.PostGamePlayCode(ctx, request, user)
@@ -125,9 +109,7 @@ func (h *HandlerWrapper) PostGamePlaySubmit(ctx context.Context, request PostGam
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return PostGamePlaySubmit401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return h.impl.PostGamePlaySubmit(ctx, request, user)
@@ -141,9 +123,7 @@ func (h *HandlerWrapper) PostLogout(ctx context.Context, request PostLogoutReque
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return PostLogout401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
return h.impl.PostLogout(ctx, request, user)
diff --git a/backend/gen/api/handler_wrapper_gen.go b/backend/gen/api/handler_wrapper_gen.go
index 5a5ce2d..c6e3e8a 100644
--- a/backend/gen/api/handler_wrapper_gen.go
+++ b/backend/gen/api/handler_wrapper_gen.go
@@ -131,17 +131,13 @@ func NewHandler(queries *db.Queries, hub GameHubInterface, conf *config.Config)
user, ok := GetJWTClaimsFromContext(ctx)
if !ok {
return {{ .Name }}401JSONResponse{
- UnauthorizedJSONResponse: UnauthorizedJSONResponse{
- Message: "Unauthorized",
- },
+ Message: "Unauthorized",
}, nil
}
{{ if .RequiresAdminRole -}}
if !user.IsAdmin {
return {{ .Name }}403JSONResponse{
- ForbiddenJSONResponse: ForbiddenJSONResponse{
- Message: "Forbidden",
- },
+ Message: "Forbidden",
}, nil
}
{{ end -}}
diff --git a/frontend/app/api/schema.d.ts b/frontend/app/api/schema.d.ts
index 6f9e270..6d27df0 100644
--- a/frontend/app/api/schema.d.ts
+++ b/frontend/app/api/schema.d.ts
@@ -4,66 +4,62 @@
*/
export interface paths {
- "/login": {
+ "/games": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- get?: never;
+ get: operations["getGames"];
put?: never;
- /** User login */
- post: operations["postLogin"];
+ post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
- "/logout": {
+ "/games/{game_id}": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- get?: never;
+ get: operations["getGame"];
put?: never;
- /** User logout */
- post: operations["postLogout"];
+ post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
- "/me": {
+ "/games/{game_id}/play/code": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- /** Get current user */
- get: operations["getMe"];
+ get?: never;
put?: never;
- post?: never;
+ post: operations["postGamePlayCode"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
- "/games": {
+ "/games/{game_id}/play/latest_state": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- /** List games */
- get: operations["getGames"];
+ get: operations["getGamePlayLatestState"];
put?: never;
post?: never;
delete?: never;
@@ -72,32 +68,30 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/games/{game_id}": {
+ "/games/{game_id}/play/submit": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- /** Get a game */
- get: operations["getGame"];
+ get?: never;
put?: never;
- post?: never;
+ post: operations["postGamePlaySubmit"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
- "/games/{game_id}/play/latest_state": {
+ "/games/{game_id}/watch/latest_states": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- /** Get the latest execution result for player */
- get: operations["getGamePlayLatestState"];
+ get: operations["getGameWatchLatestStates"];
put?: never;
post?: never;
delete?: never;
@@ -106,24 +100,23 @@ export interface paths {
patch?: never;
trace?: never;
};
- "/games/{game_id}/play/code": {
+ "/games/{game_id}/watch/ranking": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- get?: never;
+ get: operations["getGameWatchRanking"];
put?: never;
- /** Post the latest code */
- post: operations["postGamePlayCode"];
+ post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
- "/games/{game_id}/play/submit": {
+ "/login": {
parameters: {
query?: never;
header?: never;
@@ -132,40 +125,37 @@ export interface paths {
};
get?: never;
put?: never;
- /** Submit the answer */
- post: operations["postGamePlaySubmit"];
+ post: operations["postLogin"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
- "/games/{game_id}/watch/ranking": {
+ "/logout": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- /** Get the latest player ranking */
- get: operations["getGameWatchRanking"];
+ get?: never;
put?: never;
- post?: never;
+ post: operations["postLogout"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
- "/games/{game_id}/watch/latest_states": {
+ "/me": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- /** Get all the latest game states of the main players */
- get: operations["getGameWatchLatestStates"];
+ get: operations["getMe"];
put?: never;
post?: never;
delete?: never;
@@ -181,7 +171,6 @@ export interface paths {
path?: never;
cookie?: never;
};
- /** Get tournament bracket data */
get: operations["getTournament"];
put?: never;
post?: never;
@@ -196,409 +185,517 @@ export type webhooks = Record<string, never>;
export interface components {
schemas: {
Error: {
- /** @example Invalid request */
message: string;
};
- User: {
- /** @example 123 */
- user_id: number;
- /** @example john */
- username: string;
- /** @example John Doe */
- display_name: string;
- /** @example /images/john.jpg */
- icon_path?: string;
- /** @example false */
- is_admin: boolean;
- /** @example staff */
- label: string | null;
- };
+ /** @enum {string} */
+ ExecutionStatus: "none" | "running" | "success" | "wrong_answer" | "timeout" | "compile_error" | "runtime_error" | "internal_error";
Game: {
- /** @example 1 */
game_id: number;
- /**
- * @example 1v1
- * @enum {string}
- */
- game_type: "1v1" | "multiplayer";
- /** @example true */
+ game_type: components["schemas"]["GameType"];
is_public: boolean;
- /** @example Game 1 */
display_name: string;
- /** @example 360 */
duration_seconds: number;
- /** @example 946684800 */
started_at?: number;
problem: components["schemas"]["Problem"];
main_players: components["schemas"]["User"][];
};
- Problem: {
- /** @example 1 */
- problem_id: number;
- /** @example Problem 1 */
- title: string;
- /** @example This is a problem */
- description: string;
- /**
- * @example php
- * @enum {string}
- */
- language: "php" | "swift";
- /** @example echo 'hello world'; */
- sample_code: string;
- };
- /**
- * @example success
- * @enum {string}
- */
- ExecutionStatus: "none" | "running" | "success" | "wrong_answer" | "timeout" | "compile_error" | "runtime_error" | "internal_error";
+ /** @enum {string} */
+ GameType: "1v1" | "multiplayer";
LatestGameState: {
- /** @example echo 'hello world'; */
code: string;
- /** @example 100 */
score: number | null;
- /** @example 946684800 */
best_score_submitted_at: number | null;
status: components["schemas"]["ExecutionStatus"];
};
+ Problem: {
+ problem_id: number;
+ title: string;
+ description: string;
+ language: components["schemas"]["ProblemLanguage"];
+ sample_code: string;
+ };
+ /** @enum {string} */
+ ProblemLanguage: "php" | "swift";
RankingEntry: {
player: components["schemas"]["User"];
- /** @example 100 */
score: number;
- /** @example 946684800 */
submitted_at: number;
- /** @example echo 'hello world'; */
code: string | null;
};
Tournament: {
matches: components["schemas"]["TournamentMatch"][];
};
TournamentMatch: {
- /** @example 1 */
game_id: number;
player1?: components["schemas"]["User"];
player2?: components["schemas"]["User"];
- /** @example 1 */
player1_score?: number;
- /** @example 1 */
player2_score?: number;
- /** @example 1 */
winner?: number;
};
- };
- responses: {
- /** @description Bad request */
- BadRequest: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": components["schemas"]["Error"];
- };
- };
- /** @description Unauthorized */
- Unauthorized: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": components["schemas"]["Error"];
- };
- };
- /** @description Forbidden */
- Forbidden: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": components["schemas"]["Error"];
- };
- };
- /** @description Not found */
- NotFound: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": components["schemas"]["Error"];
- };
+ User: {
+ user_id: number;
+ username: string;
+ display_name: string;
+ icon_path?: string;
+ is_admin: boolean;
+ label: string | null;
};
};
- parameters: {
- path_game_id: number;
- };
+ responses: never;
+ parameters: never;
requestBodies: never;
headers: never;
pathItems: never;
}
export type $defs = Record<string, never>;
export interface operations {
- postLogin: {
+ getGames: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- requestBody: {
- content: {
- "application/json": {
- /** @example john */
- username: string;
- /** @example password123 */
- password: string;
- };
- };
- };
+ requestBody?: never;
responses: {
- /** @description Successfully authenticated */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
- user: components["schemas"]["User"];
+ games: components["schemas"]["Game"][];
};
};
};
- 401: components["responses"]["Unauthorized"];
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Access is forbidden. */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
};
};
- postLogout: {
+ getGame: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ game_id: number;
+ };
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Successfully logged out */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
};
- content?: never;
+ content: {
+ "application/json": {
+ game: components["schemas"]["Game"];
+ };
+ };
+ };
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Access is forbidden. */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description The server cannot find the requested resource. */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
};
- 401: components["responses"]["Unauthorized"];
};
};
- getMe: {
+ postGamePlayCode: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ game_id: number;
+ };
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": {
+ code: string;
+ };
+ };
+ };
responses: {
- /** @description Current user info */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
};
+ content?: never;
+ };
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
content: {
- "application/json": {
- user: components["schemas"]["User"];
- };
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Access is forbidden. */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description The server cannot find the requested resource. */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
};
};
- 401: components["responses"]["Unauthorized"];
};
};
- getGames: {
+ getGamePlayLatestState: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ game_id: number;
+ };
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description List of games */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
- games: components["schemas"]["Game"][];
+ state: components["schemas"]["LatestGameState"];
};
};
};
- 401: components["responses"]["Unauthorized"];
- 403: components["responses"]["Forbidden"];
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Access is forbidden. */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description The server cannot find the requested resource. */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
};
};
- getGame: {
+ postGamePlaySubmit: {
parameters: {
query?: never;
header?: never;
path: {
- game_id: components["parameters"]["path_game_id"];
+ game_id: number;
};
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": {
+ code: string;
+ };
+ };
+ };
responses: {
- /** @description A game */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
};
+ content?: never;
+ };
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
content: {
- "application/json": {
- game: components["schemas"]["Game"];
- };
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Access is forbidden. */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description The server cannot find the requested resource. */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
};
};
- 401: components["responses"]["Unauthorized"];
- 403: components["responses"]["Forbidden"];
- 404: components["responses"]["NotFound"];
};
};
- getGamePlayLatestState: {
+ getGameWatchLatestStates: {
parameters: {
query?: never;
header?: never;
path: {
- game_id: components["parameters"]["path_game_id"];
+ game_id: number;
};
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Your latest game state */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
- state: components["schemas"]["LatestGameState"];
+ states: {
+ [key: string]: components["schemas"]["LatestGameState"];
+ };
};
};
};
- 401: components["responses"]["Unauthorized"];
- 403: components["responses"]["Forbidden"];
- 404: components["responses"]["NotFound"];
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Access is forbidden. */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description The server cannot find the requested resource. */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
};
};
- postGamePlayCode: {
+ getGameWatchRanking: {
parameters: {
query?: never;
header?: never;
path: {
- game_id: components["parameters"]["path_game_id"];
+ game_id: number;
};
cookie?: never;
};
- requestBody: {
- content: {
- "application/json": {
- /** @example echo 'hello world'; */
- code: string;
- };
- };
- };
+ requestBody?: never;
responses: {
- /** @description Successfully updated */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
};
- content?: never;
+ content: {
+ "application/json": {
+ ranking: components["schemas"]["RankingEntry"][];
+ };
+ };
+ };
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Access is forbidden. */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description The server cannot find the requested resource. */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
};
- 401: components["responses"]["Unauthorized"];
- 403: components["responses"]["Forbidden"];
- 404: components["responses"]["NotFound"];
};
};
- postGamePlaySubmit: {
+ postLogin: {
parameters: {
query?: never;
header?: never;
- path: {
- game_id: components["parameters"]["path_game_id"];
- };
+ path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/json": {
- /** @example echo 'hello world'; */
- code: string;
+ username: string;
+ password: string;
};
};
};
responses: {
- /** @description Successfully submitted */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
};
- content?: never;
+ content: {
+ "application/json": {
+ user: components["schemas"]["User"];
+ };
+ };
+ };
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
};
- 401: components["responses"]["Unauthorized"];
- 403: components["responses"]["Forbidden"];
- 404: components["responses"]["NotFound"];
};
};
- getGameWatchRanking: {
+ postLogout: {
parameters: {
query?: never;
header?: never;
- path: {
- game_id: components["parameters"]["path_game_id"];
- };
+ path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Player ranking */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
};
+ content?: never;
+ };
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
content: {
- "application/json": {
- ranking: components["schemas"]["RankingEntry"][];
- };
+ "application/json": components["schemas"]["Error"];
};
};
- 401: components["responses"]["Unauthorized"];
- 403: components["responses"]["Forbidden"];
- 404: components["responses"]["NotFound"];
};
};
- getGameWatchLatestStates: {
+ getMe: {
parameters: {
query?: never;
header?: never;
- path: {
- game_id: components["parameters"]["path_game_id"];
- };
+ path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description All the latest game states of the main players */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
- states: {
- [key: string]: components["schemas"]["LatestGameState"];
- };
+ user: components["schemas"]["User"];
};
};
};
- 401: components["responses"]["Unauthorized"];
- 403: components["responses"]["Forbidden"];
- 404: components["responses"]["NotFound"];
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
};
};
getTournament: {
@@ -616,7 +713,7 @@ export interface operations {
};
requestBody?: never;
responses: {
- /** @description Tournament data */
+ /** @description The request has succeeded. */
200: {
headers: {
[name: string]: unknown;
@@ -627,9 +724,33 @@ export interface operations {
};
};
};
- 401: components["responses"]["Unauthorized"];
- 403: components["responses"]["Forbidden"];
- 404: components["responses"]["NotFound"];
+ /** @description Access is unauthorized. */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Access is forbidden. */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description The server cannot find the requested resource. */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
};
};
}
diff --git a/justfile b/justfile
index de2a8fa..4c621f6 100644
--- a/justfile
+++ b/justfile
@@ -4,6 +4,7 @@ default: down build up
build:
{{ docker_compose }} build
+ cd typespec; npm install
cd frontend; npm install
up:
@@ -48,6 +49,7 @@ initdb:
just psql-query < ./backend/fixtures/dev.sql
gen:
+ cd typespec; npm run build
cd backend; just gen
cd frontend; npm run openapi-typescript
diff --git a/openapi/api-server.yaml b/openapi/api-server.yaml
index 15b75b6..21fb989 100644
--- a/openapi/api-server.yaml
+++ b/openapi/api-server.yaml
@@ -2,75 +2,15 @@ openapi: 3.0.0
info:
title: Albatross internal web API
version: 0.3.0
+tags: []
paths:
- /login:
- post:
- operationId: postLogin
- summary: User login
- requestBody:
- required: true
- content:
- application/json:
- schema:
- type: object
- properties:
- username:
- type: string
- example: "john"
- password:
- type: string
- example: "password123"
- required:
- - username
- - password
- responses:
- '200':
- description: Successfully authenticated
- content:
- application/json:
- schema:
- type: object
- properties:
- user:
- $ref: '#/components/schemas/User'
- required:
- - user
- '401':
- $ref: '#/components/responses/Unauthorized'
- /logout:
- post:
- operationId: postLogout
- summary: User logout
- responses:
- '200':
- description: Successfully logged out
- '401':
- $ref: '#/components/responses/Unauthorized'
- /me:
- get:
- operationId: getMe
- summary: Get current user
- responses:
- '200':
- description: Current user info
- content:
- application/json:
- schema:
- type: object
- properties:
- user:
- $ref: '#/components/schemas/User'
- required:
- - user
- '401':
- $ref: '#/components/responses/Unauthorized'
/games:
get:
operationId: getGames
- summary: List games
+ parameters: []
responses:
'200':
- description: List of games
+ description: The request has succeeded.
content:
application/json:
schema:
@@ -83,18 +23,29 @@ paths:
required:
- games
'401':
- $ref: '#/components/responses/Unauthorized'
+ description: Access is unauthorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'403':
- $ref: '#/components/responses/Forbidden'
+ description: Access is forbidden.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
/games/{game_id}:
get:
operationId: getGame
- summary: Get a game
parameters:
- - $ref: '#/components/parameters/path_game_id'
+ - name: game_id
+ in: path
+ required: true
+ schema:
+ type: integer
responses:
'200':
- description: A game
+ description: The request has succeeded.
content:
application/json:
schema:
@@ -105,41 +56,53 @@ paths:
required:
- game
'401':
- $ref: '#/components/responses/Unauthorized'
+ description: Access is unauthorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'403':
- $ref: '#/components/responses/Forbidden'
+ description: Access is forbidden.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'404':
- $ref: '#/components/responses/NotFound'
- /games/{game_id}/play/latest_state:
- get:
- operationId: getGamePlayLatestState
- summary: Get the latest execution result for player
+ description: The server cannot find the requested resource.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ /games/{game_id}/play/code:
+ post:
+ operationId: postGamePlayCode
parameters:
- - $ref: '#/components/parameters/path_game_id'
+ - name: game_id
+ in: path
+ required: true
+ schema:
+ type: integer
responses:
'200':
- description: Your latest game state
+ description: The request has succeeded.
+ '401':
+ description: Access is unauthorized.
content:
application/json:
schema:
- type: object
- properties:
- state:
- $ref: '#/components/schemas/LatestGameState'
- required:
- - state
- '401':
- $ref: '#/components/responses/Unauthorized'
+ $ref: '#/components/schemas/Error'
'403':
- $ref: '#/components/responses/Forbidden'
+ description: Access is forbidden.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'404':
- $ref: '#/components/responses/NotFound'
- /games/{game_id}/play/code:
- post:
- operationId: postGamePlayCode
- summary: Post the latest code
- parameters:
- - $ref: '#/components/parameters/path_game_id'
+ description: The server cannot find the requested resource.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
requestBody:
required: true
content:
@@ -149,24 +112,77 @@ paths:
properties:
code:
type: string
- example: "echo 'hello world';"
required:
- code
+ /games/{game_id}/play/latest_state:
+ get:
+ operationId: getGamePlayLatestState
+ parameters:
+ - name: game_id
+ in: path
+ required: true
+ schema:
+ type: integer
responses:
'200':
- description: Successfully updated
+ description: The request has succeeded.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ state:
+ $ref: '#/components/schemas/LatestGameState'
+ required:
+ - state
'401':
- $ref: '#/components/responses/Unauthorized'
+ description: Access is unauthorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'403':
- $ref: '#/components/responses/Forbidden'
+ description: Access is forbidden.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'404':
- $ref: '#/components/responses/NotFound'
+ description: The server cannot find the requested resource.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
/games/{game_id}/play/submit:
post:
operationId: postGamePlaySubmit
- summary: Submit the answer
parameters:
- - $ref: '#/components/parameters/path_game_id'
+ - name: game_id
+ in: path
+ required: true
+ schema:
+ type: integer
+ responses:
+ '200':
+ description: The request has succeeded.
+ '401':
+ description: Access is unauthorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ '403':
+ description: Access is forbidden.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ '404':
+ description: The server cannot find the requested resource.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
requestBody:
required: true
content:
@@ -176,27 +192,61 @@ paths:
properties:
code:
type: string
- example: "echo 'hello world';"
required:
- code
+ /games/{game_id}/watch/latest_states:
+ get:
+ operationId: getGameWatchLatestStates
+ parameters:
+ - name: game_id
+ in: path
+ required: true
+ schema:
+ type: integer
responses:
'200':
- description: Successfully submitted
+ description: The request has succeeded.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ states:
+ type: object
+ additionalProperties:
+ $ref: '#/components/schemas/LatestGameState'
+ required:
+ - states
'401':
- $ref: '#/components/responses/Unauthorized'
+ description: Access is unauthorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'403':
- $ref: '#/components/responses/Forbidden'
+ description: Access is forbidden.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'404':
- $ref: '#/components/responses/NotFound'
+ description: The server cannot find the requested resource.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
/games/{game_id}/watch/ranking:
get:
operationId: getGameWatchRanking
- summary: Get the latest player ranking
parameters:
- - $ref: '#/components/parameters/path_game_id'
+ - name: game_id
+ in: path
+ required: true
+ schema:
+ type: integer
responses:
'200':
- description: Player ranking
+ description: The request has succeeded.
content:
application/json:
schema:
@@ -209,70 +259,131 @@ paths:
required:
- ranking
'401':
- $ref: '#/components/responses/Unauthorized'
+ description: Access is unauthorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'403':
- $ref: '#/components/responses/Forbidden'
+ description: Access is forbidden.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'404':
- $ref: '#/components/responses/NotFound'
- /games/{game_id}/watch/latest_states:
+ description: The server cannot find the requested resource.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ /login:
+ post:
+ operationId: postLogin
+ parameters: []
+ responses:
+ '200':
+ description: The request has succeeded.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ user:
+ $ref: '#/components/schemas/User'
+ required:
+ - user
+ '401':
+ description: Access is unauthorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ username:
+ type: string
+ password:
+ type: string
+ required:
+ - username
+ - password
+ /logout:
+ post:
+ operationId: postLogout
+ parameters: []
+ responses:
+ '200':
+ description: The request has succeeded.
+ '401':
+ description: Access is unauthorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ /me:
get:
- operationId: getGameWatchLatestStates
- summary: Get all the latest game states of the main players
- parameters:
- - $ref: '#/components/parameters/path_game_id'
+ operationId: getMe
+ parameters: []
responses:
'200':
- description: All the latest game states of the main players
+ description: The request has succeeded.
content:
application/json:
schema:
type: object
properties:
- states:
- type: object
- additionalProperties:
- $ref: '#/components/schemas/LatestGameState'
+ user:
+ $ref: '#/components/schemas/User'
required:
- - states
+ - user
'401':
- $ref: '#/components/responses/Unauthorized'
- '403':
- $ref: '#/components/responses/Forbidden'
- '404':
- $ref: '#/components/responses/NotFound'
+ description: Access is unauthorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
/tournament:
get:
operationId: getTournament
- summary: Get tournament bracket data
parameters:
- - in: query
- name: game1
+ - name: game1
+ in: query
+ required: true
schema:
type: integer
+ explode: false
+ - name: game2
+ in: query
required: true
- - in: query
- name: game2
schema:
type: integer
+ explode: false
+ - name: game3
+ in: query
required: true
- - in: query
- name: game3
schema:
type: integer
+ explode: false
+ - name: game4
+ in: query
required: true
- - in: query
- name: game4
schema:
type: integer
+ explode: false
+ - name: game5
+ in: query
required: true
- - in: query
- name: game5
schema:
type: integer
- required: true
+ explode: false
responses:
'200':
- description: Tournament data
+ description: The request has succeeded.
content:
application/json:
schema:
@@ -283,105 +394,66 @@ paths:
required:
- tournament
'401':
- $ref: '#/components/responses/Unauthorized'
+ description: Access is unauthorized.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'403':
- $ref: '#/components/responses/Forbidden'
+ description: Access is forbidden.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
'404':
- $ref: '#/components/responses/NotFound'
+ description: The server cannot find the requested resource.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
components:
- parameters:
- path_game_id:
- in: path
- name: game_id
- schema:
- type: integer
- required: true
- responses:
- BadRequest:
- description: Bad request
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Error'
- Unauthorized:
- description: Unauthorized
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Error'
- Forbidden:
- description: Forbidden
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Error'
- NotFound:
- description: Not found
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Error'
schemas:
Error:
type: object
- properties:
- message:
- type: string
- example: "Invalid request"
required:
- message
- User:
- type: object
properties:
- user_id:
- type: integer
- example: 123
- username:
- type: string
- example: "john"
- display_name:
- type: string
- example: "John Doe"
- icon_path:
- type: string
- example: "/images/john.jpg"
- is_admin:
- type: boolean
- example: false
- label:
+ message:
type: string
- nullable: true
- example: "staff"
- required:
- - user_id
- - username
- - display_name
- - is_admin
- - label
+ ExecutionStatus:
+ type: string
+ enum:
+ - none
+ - running
+ - success
+ - wrong_answer
+ - timeout
+ - compile_error
+ - runtime_error
+ - internal_error
Game:
type: object
+ required:
+ - game_id
+ - game_type
+ - is_public
+ - display_name
+ - duration_seconds
+ - problem
+ - main_players
properties:
game_id:
type: integer
- example: 1
game_type:
- type: string
- example: "1v1"
- enum:
- - 1v1
- - multiplayer
+ $ref: '#/components/schemas/GameType'
is_public:
type: boolean
- example: true
display_name:
type: string
- example: "Game 1"
duration_seconds:
type: integer
- example: 360
started_at:
type: integer
- example: 946684800
x-go-type: int64
problem:
$ref: '#/components/schemas/Problem'
@@ -389,123 +461,117 @@ components:
type: array
items:
$ref: '#/components/schemas/User'
- required:
- - game_id
- - game_type
- - is_public
- - display_name
- - duration_seconds
- - problem
- - main_players
- Problem:
- type: object
- properties:
- problem_id:
- type: integer
- example: 1
- title:
- type: string
- example: "Problem 1"
- description:
- type: string
- example: "This is a problem"
- language:
- type: string
- example: "php"
- enum:
- - php
- - swift
- sample_code:
- type: string
- example: "echo 'hello world';"
- required:
- - problem_id
- - title
- - description
- - language
- - sample_code
- ExecutionStatus:
+ GameType:
type: string
- example: "success"
enum:
- - none
- - running
- - success
- - wrong_answer
- - timeout
- - compile_error
- - runtime_error
- - internal_error
+ - 1v1
+ - multiplayer
LatestGameState:
type: object
+ required:
+ - code
+ - score
+ - best_score_submitted_at
+ - status
properties:
code:
type: string
- example: "echo 'hello world';"
score:
type: integer
nullable: true
- example: 100
best_score_submitted_at:
type: integer
nullable: true
- example: 946684800
x-go-type: int64
status:
$ref: '#/components/schemas/ExecutionStatus'
+ Problem:
+ type: object
required:
- - code
- - score
- - best_score_submitted_at
- - status
+ - problem_id
+ - title
+ - description
+ - language
+ - sample_code
+ properties:
+ problem_id:
+ type: integer
+ title:
+ type: string
+ description:
+ type: string
+ language:
+ $ref: '#/components/schemas/ProblemLanguage'
+ sample_code:
+ type: string
+ ProblemLanguage:
+ type: string
+ enum:
+ - php
+ - swift
RankingEntry:
type: object
+ required:
+ - player
+ - score
+ - submitted_at
+ - code
properties:
player:
$ref: '#/components/schemas/User'
score:
type: integer
- example: 100
submitted_at:
type: integer
- example: 946684800
x-go-type: int64
code:
type: string
nullable: true
- example: "echo 'hello world';"
- required:
- - player
- - score
- - submitted_at
- - code
Tournament:
type: object
+ required:
+ - matches
properties:
matches:
type: array
items:
$ref: '#/components/schemas/TournamentMatch'
- required:
- - matches
TournamentMatch:
type: object
+ required:
+ - game_id
properties:
game_id:
type: integer
- example: 1
player1:
$ref: '#/components/schemas/User'
player2:
$ref: '#/components/schemas/User'
player1_score:
type: integer
- example: 1
player2_score:
type: integer
- example: 1
winner:
type: integer
- example: 1
+ User:
+ type: object
required:
- - game_id
+ - user_id
+ - username
+ - display_name
+ - is_admin
+ - label
+ properties:
+ user_id:
+ type: integer
+ username:
+ type: string
+ display_name:
+ type: string
+ icon_path:
+ type: string
+ is_admin:
+ type: boolean
+ label:
+ type: string
+ nullable: true
diff --git a/openapi/fortee.yaml b/openapi/fortee.yaml
index 7e27f30..89a1842 100644
--- a/openapi/fortee.yaml
+++ b/openapi/fortee.yaml
@@ -2,30 +2,15 @@ openapi: 3.0.0
info:
title: fortee API
version: 0.1.0
+tags: []
paths:
/api/user/login:
post:
operationId: postLogin
- summary: User login
- requestBody:
- required: true
- content:
- application/x-www-form-urlencoded:
- schema:
- type: object
- properties:
- username:
- type: string
- example: "john"
- password:
- type: string
- example: "password123"
- required:
- - username
- - password
+ parameters: []
responses:
'200':
- description: Successfully authenticated
+ description: The request has succeeded.
content:
application/json:
schema:
@@ -33,30 +18,41 @@ paths:
properties:
loggedIn:
type: boolean
- example: true
user:
type: object
properties:
username:
type: string
- example: "john"
required:
- username
required:
- loggedIn
+ requestBody:
+ required: true
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ username:
+ type: string
+ password:
+ type: string
+ required:
+ - username
+ - password
/api/user/view/{username}:
get:
operationId: getUser
- summary: Get a user
parameters:
- - in: path
- name: username
+ - name: username
+ in: path
+ required: true
schema:
type: string
- required: true
responses:
'200':
- description: User found
+ description: The request has succeeded.
content:
application/json:
schema:
@@ -64,16 +60,14 @@ paths:
properties:
uuid:
type: string
- example: "11111111-1111-1111-1111-111111111111"
username:
type: string
- example: "john"
avatar_url:
type: string
- example: "/files/_user/11111111-1111-1111-1111-111111111111.jpg"
required:
- uuid
- username
- avatar_url
'404':
- description: User not found
+ description: The server cannot find the requested resource.
+components: {}
diff --git a/typespec/api-server/main.tsp b/typespec/api-server/main.tsp
new file mode 100644
index 0000000..00d1816
--- /dev/null
+++ b/typespec/api-server/main.tsp
@@ -0,0 +1,17 @@
+import "@typespec/http";
+import "@typespec/openapi";
+import "@typespec/openapi3";
+
+import "./models.tsp";
+import "./routes.tsp";
+
+using TypeSpec.Http;
+using TypeSpec.OpenAPI;
+
+@service(#{
+ title: "Albatross internal web API",
+})
+@info(#{
+ version: "0.3.0",
+})
+namespace AlbatrossApi;
diff --git a/typespec/api-server/models.tsp b/typespec/api-server/models.tsp
new file mode 100644
index 0000000..47519be
--- /dev/null
+++ b/typespec/api-server/models.tsp
@@ -0,0 +1,119 @@
+using TypeSpec.Http;
+using TypeSpec.OpenAPI;
+
+namespace AlbatrossApi;
+
+// ---------- Error ----------
+
+model Error {
+ message: string;
+}
+
+// ---------- Error Responses ----------
+
+@error
+model UnauthorizedError {
+ @statusCode statusCode: 401;
+ @body body: Error;
+}
+
+@error
+model ForbiddenError {
+ @statusCode statusCode: 403;
+ @body body: Error;
+}
+
+@error
+model NotFoundError {
+ @statusCode statusCode: 404;
+ @body body: Error;
+}
+
+// ---------- Enums ----------
+
+enum GameType {
+ `1v1`,
+ multiplayer,
+}
+
+enum ProblemLanguage {
+ php,
+ swift,
+}
+
+enum ExecutionStatus {
+ none,
+ running,
+ success,
+ wrong_answer,
+ timeout,
+ compile_error,
+ runtime_error,
+ internal_error,
+}
+
+// ---------- Models ----------
+
+model User {
+ user_id: integer;
+ username: string;
+ display_name: string;
+ icon_path?: string;
+ is_admin: boolean;
+ label: string | null;
+}
+
+model Problem {
+ problem_id: integer;
+ title: string;
+ description: string;
+ language: ProblemLanguage;
+ sample_code: string;
+}
+
+model Game {
+ game_id: integer;
+ game_type: GameType;
+ is_public: boolean;
+ display_name: string;
+ duration_seconds: integer;
+
+ @extension("x-go-type", "int64")
+ started_at?: integer;
+
+ problem: Problem;
+ main_players: User[];
+}
+
+model LatestGameState {
+ code: string;
+ score: integer | null;
+
+ @extension("x-go-type", "int64")
+ best_score_submitted_at: integer | null;
+
+ status: ExecutionStatus;
+}
+
+model RankingEntry {
+ player: User;
+ score: integer;
+
+ @extension("x-go-type", "int64")
+ submitted_at: integer;
+
+ code: string | null;
+}
+
+model Tournament {
+ matches: TournamentMatch[];
+}
+
+model TournamentMatch {
+ game_id: integer;
+ player1?: User;
+ player2?: User;
+ player1_score?: integer;
+ player2_score?: integer;
+ winner?: integer;
+}
diff --git a/typespec/api-server/routes.tsp b/typespec/api-server/routes.tsp
new file mode 100644
index 0000000..3409cea
--- /dev/null
+++ b/typespec/api-server/routes.tsp
@@ -0,0 +1,126 @@
+using TypeSpec.Http;
+using TypeSpec.OpenAPI;
+
+namespace AlbatrossApi;
+
+// ---------- Auth ----------
+
+@route("/login")
+@post
+@operationId("postLogin")
+op postLogin(@body body: {
+ username: string;
+ password: string;
+}): {
+ @body body: {
+ user: User;
+ };
+} | UnauthorizedError;
+
+@route("/logout")
+@post
+@operationId("postLogout")
+op postLogout(): {
+ @statusCode statusCode: 200;
+} | UnauthorizedError;
+
+@route("/me")
+@get
+@operationId("getMe")
+op getMe(): {
+ @body body: {
+ user: User;
+ };
+} | UnauthorizedError;
+
+// ---------- Games ----------
+
+@route("/games")
+@get
+@operationId("getGames")
+op getGames(): {
+ @body body: {
+ games: Game[];
+ };
+} | UnauthorizedError | ForbiddenError;
+
+@route("/games/{game_id}")
+@get
+@operationId("getGame")
+op getGame(@path game_id: integer): {
+ @body body: {
+ game: Game;
+ };
+} | UnauthorizedError | ForbiddenError | NotFoundError;
+
+// ---------- Play ----------
+
+@route("/games/{game_id}/play/latest_state")
+@get
+@operationId("getGamePlayLatestState")
+op getGamePlayLatestState(@path game_id: integer): {
+ @body body: {
+ state: LatestGameState;
+ };
+} | UnauthorizedError | ForbiddenError | NotFoundError;
+
+@route("/games/{game_id}/play/code")
+@post
+@operationId("postGamePlayCode")
+op postGamePlayCode(
+ @path game_id: integer,
+ @body body: {
+ code: string;
+ },
+): {
+ @statusCode statusCode: 200;
+} | UnauthorizedError | ForbiddenError | NotFoundError;
+
+@route("/games/{game_id}/play/submit")
+@post
+@operationId("postGamePlaySubmit")
+op postGamePlaySubmit(
+ @path game_id: integer,
+ @body body: {
+ code: string;
+ },
+): {
+ @statusCode statusCode: 200;
+} | UnauthorizedError | ForbiddenError | NotFoundError;
+
+// ---------- Watch ----------
+
+@route("/games/{game_id}/watch/ranking")
+@get
+@operationId("getGameWatchRanking")
+op getGameWatchRanking(@path game_id: integer): {
+ @body body: {
+ ranking: RankingEntry[];
+ };
+} | UnauthorizedError | ForbiddenError | NotFoundError;
+
+@route("/games/{game_id}/watch/latest_states")
+@get
+@operationId("getGameWatchLatestStates")
+op getGameWatchLatestStates(@path game_id: integer): {
+ @body body: {
+ states: Record<LatestGameState>;
+ };
+} | UnauthorizedError | ForbiddenError | NotFoundError;
+
+// ---------- Tournament ----------
+
+@route("/tournament")
+@get
+@operationId("getTournament")
+op getTournament(
+ @query game1: integer,
+ @query game2: integer,
+ @query game3: integer,
+ @query game4: integer,
+ @query game5: integer,
+): {
+ @body body: {
+ tournament: Tournament;
+ };
+} | UnauthorizedError | ForbiddenError | NotFoundError;
diff --git a/typespec/api-server/tspconfig.yaml b/typespec/api-server/tspconfig.yaml
new file mode 100644
index 0000000..78ba744
--- /dev/null
+++ b/typespec/api-server/tspconfig.yaml
@@ -0,0 +1,8 @@
+output-dir: "{project-root}/tsp-output"
+emit:
+ - "@typespec/openapi3"
+options:
+ "@typespec/openapi3":
+ openapi-versions:
+ - "3.0.0"
+ output-file: openapi.yaml
diff --git a/typespec/fortee/main.tsp b/typespec/fortee/main.tsp
new file mode 100644
index 0000000..03683a7
--- /dev/null
+++ b/typespec/fortee/main.tsp
@@ -0,0 +1,45 @@
+import "@typespec/http";
+import "@typespec/openapi";
+import "@typespec/openapi3";
+
+using TypeSpec.Http;
+using TypeSpec.OpenAPI;
+
+@service(#{
+ title: "fortee API",
+})
+@info(#{
+ version: "0.1.0",
+})
+namespace ForteeApi;
+
+@route("/api/user/login")
+@post
+@operationId("postLogin")
+op postLogin(
+ @header contentType: "application/x-www-form-urlencoded",
+ @body body: {
+ username: string;
+ password: string;
+ },
+): {
+ @body body: {
+ loggedIn: boolean;
+ user?: {
+ username: string;
+ };
+ };
+};
+
+@route("/api/user/view/{username}")
+@get
+@operationId("getUser")
+op getUser(@path username: string): {
+ @body body: {
+ uuid: string;
+ username: string;
+ avatar_url: string;
+ };
+} | {
+ @statusCode statusCode: 404;
+};
diff --git a/typespec/fortee/tspconfig.yaml b/typespec/fortee/tspconfig.yaml
new file mode 100644
index 0000000..78ba744
--- /dev/null
+++ b/typespec/fortee/tspconfig.yaml
@@ -0,0 +1,8 @@
+output-dir: "{project-root}/tsp-output"
+emit:
+ - "@typespec/openapi3"
+options:
+ "@typespec/openapi3":
+ openapi-versions:
+ - "3.0.0"
+ output-file: openapi.yaml
diff --git a/typespec/package-lock.json b/typespec/package-lock.json
new file mode 100644
index 0000000..f343eee
--- /dev/null
+++ b/typespec/package-lock.json
@@ -0,0 +1,1435 @@
+{
+ "name": "albatross-typespec",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "albatross-typespec",
+ "dependencies": {
+ "@typespec/compiler": "^1.9.0",
+ "@typespec/http": "^1.9.0",
+ "@typespec/openapi": "^1.9.0",
+ "@typespec/openapi3": "^1.9.0",
+ "@typespec/rest": "^0.79.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz",
+ "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@inquirer/ansi": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.3.tgz",
+ "integrity": "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ }
+ },
+ "node_modules/@inquirer/checkbox": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.0.4.tgz",
+ "integrity": "sha512-DrAMU3YBGMUAp6ArwTIp/25CNDtDbxk7UjIrrtM25JVVrlVYlVzHh5HR1BDFu9JMyUoZ4ZanzeaHqNDttf3gVg==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.3",
+ "@inquirer/core": "^11.1.1",
+ "@inquirer/figures": "^2.0.3",
+ "@inquirer/type": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/confirm": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.4.tgz",
+ "integrity": "sha512-WdaPe7foUnoGYvXzH4jp4wH/3l+dBhZ3uwhKjXjwdrq5tEIFaANxj6zrGHxLdsIA0yKM0kFPVcEalOZXBB5ISA==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^11.1.1",
+ "@inquirer/type": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/core": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.1.tgz",
+ "integrity": "sha512-hV9o15UxX46OyQAtaoMqAOxGR8RVl1aZtDx1jHbCtSJy1tBdTfKxLPKf7utsE4cRy4tcmCQ4+vdV+ca+oNxqNA==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.3",
+ "@inquirer/figures": "^2.0.3",
+ "@inquirer/type": "^4.0.3",
+ "cli-width": "^4.1.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0",
+ "wrap-ansi": "^9.0.2"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/editor": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.0.4.tgz",
+ "integrity": "sha512-QI3Jfqcv6UO2/VJaEFONH8Im1ll++Xn/AJTBn9Xf+qx2M+H8KZAdQ5sAe2vtYlo+mLW+d7JaMJB4qWtK4BG3pw==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^11.1.1",
+ "@inquirer/external-editor": "^2.0.3",
+ "@inquirer/type": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/expand": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.0.4.tgz",
+ "integrity": "sha512-0I/16YwPPP0Co7a5MsomlZLpch48NzYfToyqYAOWtBmaXSB80RiNQ1J+0xx2eG+Wfxt0nHtpEWSRr6CzNVnOGg==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^11.1.1",
+ "@inquirer/type": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/external-editor": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-2.0.3.tgz",
+ "integrity": "sha512-LgyI7Agbda74/cL5MvA88iDpvdXI2KuMBCGRkbCl2Dg1vzHeOgs+s0SDcXV7b+WZJrv2+ERpWSM65Fpi9VfY3w==",
+ "license": "MIT",
+ "dependencies": {
+ "chardet": "^2.1.1",
+ "iconv-lite": "^0.7.2"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/figures": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.3.tgz",
+ "integrity": "sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ }
+ },
+ "node_modules/@inquirer/input": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.0.4.tgz",
+ "integrity": "sha512-4B3s3jvTREDFvXWit92Yc6jF1RJMDy2VpSqKtm4We2oVU65YOh2szY5/G14h4fHlyQdpUmazU5MPCFZPRJ0AOw==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^11.1.1",
+ "@inquirer/type": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/number": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.0.4.tgz",
+ "integrity": "sha512-CmMp9LF5HwE+G/xWsC333TlCzYYbXMkcADkKzcawh49fg2a1ryLc7JL1NJYYt1lJ+8f4slikNjJM9TEL/AljYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^11.1.1",
+ "@inquirer/type": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/password": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.0.4.tgz",
+ "integrity": "sha512-ZCEPyVYvHK4W4p2Gy6sTp9nqsdHQCfiPXIP9LbJVW4yCinnxL/dDDmPaEZVysGrj8vxVReRnpfS2fOeODe9zjg==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.3",
+ "@inquirer/core": "^11.1.1",
+ "@inquirer/type": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/prompts": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.2.0.tgz",
+ "integrity": "sha512-rqTzOprAj55a27jctS3vhvDDJzYXsr33WXTjODgVOru21NvBo9yIgLIAf7SBdSV0WERVly3dR6TWyp7ZHkvKFA==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/checkbox": "^5.0.4",
+ "@inquirer/confirm": "^6.0.4",
+ "@inquirer/editor": "^5.0.4",
+ "@inquirer/expand": "^5.0.4",
+ "@inquirer/input": "^5.0.4",
+ "@inquirer/number": "^4.0.4",
+ "@inquirer/password": "^5.0.4",
+ "@inquirer/rawlist": "^5.2.0",
+ "@inquirer/search": "^4.1.0",
+ "@inquirer/select": "^5.0.4"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/rawlist": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.2.0.tgz",
+ "integrity": "sha512-CciqGoOUMrFo6HxvOtU5uL8fkjCmzyeB6fG7O1vdVAZVSopUBYECOwevDBlqNLyyYmzpm2Gsn/7nLrpruy9RFg==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^11.1.1",
+ "@inquirer/type": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/search": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.1.0.tgz",
+ "integrity": "sha512-EAzemfiP4IFvIuWnrHpgZs9lAhWDA0GM3l9F4t4mTQ22IFtzfrk8xbkMLcAN7gmVML9O/i+Hzu8yOUyAaL6BKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^11.1.1",
+ "@inquirer/figures": "^2.0.3",
+ "@inquirer/type": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/select": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.0.4.tgz",
+ "integrity": "sha512-s8KoGpPYMEQ6WXc0dT9blX2NtIulMdLOO3LA1UKOiv7KFWzlJ6eLkEYTDBIi+JkyKXyn8t/CD6TinxGjyLt57g==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^2.0.3",
+ "@inquirer/core": "^11.1.1",
+ "@inquirer/figures": "^2.0.3",
+ "@inquirer/type": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/type": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.3.tgz",
+ "integrity": "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@scalar/helpers": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/@scalar/helpers/-/helpers-0.2.11.tgz",
+ "integrity": "sha512-Y7DLt1bIZF9dvHzJwSJTcC1lpSr1Tbf4VBhHOCRIHu23Rr7/lhQnddRxFmPV1tZXwEQKz7F7yRrubwCfKPCucw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@scalar/json-magic": {
+ "version": "0.9.6",
+ "resolved": "https://registry.npmjs.org/@scalar/json-magic/-/json-magic-0.9.6.tgz",
+ "integrity": "sha512-2TKoqkAophHti1nH+rvQlR4lhD6X9tqQpuNeAE0cytHSX/yndkSOE0yA7cep5T9tFjGN4Km0gMnelvY3LgWs4A==",
+ "license": "MIT",
+ "dependencies": {
+ "@scalar/helpers": "0.2.11",
+ "yaml": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@scalar/openapi-parser": {
+ "version": "0.24.9",
+ "resolved": "https://registry.npmjs.org/@scalar/openapi-parser/-/openapi-parser-0.24.9.tgz",
+ "integrity": "sha512-uqpwt6ZQJQu4c3CvMsJiXMUj32113yrclsDC31hlL33vEUS5JU9dCYfY27oLSCVoKl8R8KihlnEcbfRnH/O/GA==",
+ "license": "MIT",
+ "dependencies": {
+ "@scalar/helpers": "0.2.11",
+ "@scalar/json-magic": "0.11.0",
+ "@scalar/openapi-types": "0.5.3",
+ "@scalar/openapi-upgrader": "0.1.8",
+ "ajv": "^8.17.1",
+ "ajv-draft-04": "^1.0.0",
+ "ajv-formats": "^3.0.1",
+ "jsonpointer": "^5.0.1",
+ "leven": "^4.0.0",
+ "yaml": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@scalar/openapi-parser/node_modules/@scalar/json-magic": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@scalar/json-magic/-/json-magic-0.11.0.tgz",
+ "integrity": "sha512-1zBseDDEPkKlAVd9lT1HlK9Nefeh0YEE+pcmyDL3J5derIZn9UYXAFecdkeXMdjDtWDgcrkmWCrHhpoT7zVKdQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@scalar/helpers": "0.2.11",
+ "yaml": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@scalar/openapi-types": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/@scalar/openapi-types/-/openapi-types-0.5.3.tgz",
+ "integrity": "sha512-m4n/Su3K01d15dmdWO1LlqecdSPKuNjuokrJLdiQ485kW/hRHbXW1QP6tJL75myhw/XhX5YhYAR+jrwnGjXiMw==",
+ "license": "MIT",
+ "dependencies": {
+ "zod": "^4.1.11"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@scalar/openapi-upgrader": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/@scalar/openapi-upgrader/-/openapi-upgrader-0.1.8.tgz",
+ "integrity": "sha512-2xuYLLs0fBadLIk4I1ObjMiCnOyLPEMPf24A1HtHQvhKGDnGlvT63F2rU2Xw8lxCjgHnzveMPnOJEbwIy64RCg==",
+ "license": "MIT",
+ "dependencies": {
+ "@scalar/openapi-types": "0.5.3"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@sindresorhus/merge-streams": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
+ "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@typespec/asset-emitter": {
+ "version": "0.79.0",
+ "resolved": "https://registry.npmjs.org/@typespec/asset-emitter/-/asset-emitter-0.79.0.tgz",
+ "integrity": "sha512-pNMtfSSwgmTQ2ex6bd1l6BUW2RLjSFnWQO5C5bNSleV62YEH5jMLn3THWDU9oUB0JoiBjgomV8cPqNRTJ+iV9w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@typespec/compiler": "^1.9.0"
+ }
+ },
+ "node_modules/@typespec/compiler": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.9.0.tgz",
+ "integrity": "sha512-Rz9fFWQSTJSnhBfZvtA/bDIuO82fknYdtyMsL9lZNJE82rquC6JByHPFsnbGH1VXA0HhMj9L7Oqyp3f0m/BTOA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "~7.28.6",
+ "@inquirer/prompts": "^8.0.1",
+ "ajv": "~8.17.1",
+ "change-case": "~5.4.4",
+ "env-paths": "^3.0.0",
+ "globby": "~16.1.0",
+ "is-unicode-supported": "^2.1.0",
+ "mustache": "~4.2.0",
+ "picocolors": "~1.1.1",
+ "prettier": "~3.8.0",
+ "semver": "^7.7.1",
+ "tar": "^7.5.2",
+ "temporal-polyfill": "^0.3.0",
+ "vscode-languageserver": "~9.0.1",
+ "vscode-languageserver-textdocument": "~1.0.12",
+ "yaml": "~2.8.2",
+ "yargs": "~18.0.0"
+ },
+ "bin": {
+ "tsp": "cmd/tsp.js",
+ "tsp-server": "cmd/tsp-server.js"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@typespec/http": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.9.0.tgz",
+ "integrity": "sha512-JzlZZsgCo71f2KhWbf4BLOz5e+dVLj7gJJ4kvXvrmuG9QHoT41VaGPpCQamYgpZLMz2LQbsOtw34AmpovhuJSw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@typespec/compiler": "^1.9.0",
+ "@typespec/streams": "^0.79.0"
+ },
+ "peerDependenciesMeta": {
+ "@typespec/streams": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typespec/openapi": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.9.0.tgz",
+ "integrity": "sha512-5ieXCWRLcyFLv3IFk26ena/RW/NxvT5KiHaoNVFRd79J0XZjFcE0Od6Lxxqj4dWmCo3C8oKtOwFoQuie18G3lQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@typespec/compiler": "^1.9.0",
+ "@typespec/http": "^1.9.0"
+ }
+ },
+ "node_modules/@typespec/openapi3": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-1.9.0.tgz",
+ "integrity": "sha512-htwhrGHQxuoNwAljeJE8CBt5yfKOv48T9Ugv91Y+4yNnlevJfDT29yrfD2mXYMujVOr3Kte1qilazClafkUIgg==",
+ "license": "MIT",
+ "dependencies": {
+ "@scalar/json-magic": "^0.9.1",
+ "@scalar/openapi-parser": "^0.24.1",
+ "@scalar/openapi-types": "^0.5.0",
+ "@typespec/asset-emitter": "^0.79.0",
+ "yaml": "~2.8.2"
+ },
+ "bin": {
+ "tsp-openapi3": "cmd/tsp-openapi3.js"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@typespec/compiler": "^1.9.0",
+ "@typespec/events": "^0.79.0",
+ "@typespec/http": "^1.9.0",
+ "@typespec/json-schema": "^1.9.0",
+ "@typespec/openapi": "^1.9.0",
+ "@typespec/sse": "^0.79.0",
+ "@typespec/streams": "^0.79.0",
+ "@typespec/versioning": "^0.79.0"
+ },
+ "peerDependenciesMeta": {
+ "@typespec/events": {
+ "optional": true
+ },
+ "@typespec/json-schema": {
+ "optional": true
+ },
+ "@typespec/sse": {
+ "optional": true
+ },
+ "@typespec/streams": {
+ "optional": true
+ },
+ "@typespec/versioning": {
+ "optional": true
+ },
+ "@typespec/xml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typespec/rest": {
+ "version": "0.79.0",
+ "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.79.0.tgz",
+ "integrity": "sha512-6QIX7oaUGy/z4rseUrC86LjHxZn8rAAY4fXvGnlPRce6GhEdTb9S9OQPmlPeWngXwCx/07P2+FCR915APqmZxg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@typespec/compiler": "^1.9.0",
+ "@typespec/http": "^1.9.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-draft-04": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
+ "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "ajv": "^8.5.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/change-case": {
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz",
+ "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==",
+ "license": "MIT"
+ },
+ "node_modules/chardet": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz",
+ "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==",
+ "license": "MIT"
+ },
+ "node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/cli-width": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
+ "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "license": "MIT"
+ },
+ "node_modules/env-paths": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz",
+ "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz",
+ "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/globby": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-16.1.0.tgz",
+ "integrity": "sha512-+A4Hq7m7Ze592k9gZRy4gJ27DrXRNnC1vPjxTt1qQxEY8RxagBkBxivkCwg7FxSTG0iLLEMaUx13oOr0R2/qcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/merge-streams": "^4.0.0",
+ "fast-glob": "^3.3.3",
+ "ignore": "^7.0.5",
+ "is-path-inside": "^4.0.0",
+ "slash": "^5.1.0",
+ "unicorn-magic": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz",
+ "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+ "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/jsonpointer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz",
+ "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/leven": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-4.1.0.tgz",
+ "integrity": "sha512-KZ9W9nWDT7rF7Dazg8xyLHGLrmpgq2nVNFUckhqdW3szVP6YhCpp/RAnpmVExA9JvrMynjwSLVrEj3AepHR6ew==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
+ "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/mustache": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
+ "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
+ "license": "MIT",
+ "bin": {
+ "mustache": "bin/mustache"
+ }
+ },
+ "node_modules/mute-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz",
+ "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==",
+ "license": "ISC",
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
+ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/slash": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
+ "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/tar": {
+ "version": "7.5.7",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
+ "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.1.0",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/temporal-polyfill": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.3.0.tgz",
+ "integrity": "sha512-qNsTkX9K8hi+FHDfHmf22e/OGuXmfBm9RqNismxBrnSmZVJKegQ+HYYXT+R7Ha8F/YSm2Y34vmzD4cxMu2u95g==",
+ "license": "MIT",
+ "dependencies": {
+ "temporal-spec": "0.3.0"
+ }
+ },
+ "node_modules/temporal-spec": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.3.0.tgz",
+ "integrity": "sha512-n+noVpIqz4hYgFSMOSiINNOUOMFtV5cZQNCmmszA6GiVFVRt3G7AqVyhXjhCSmowvQn+NsGn+jMDMKJYHd3bSQ==",
+ "license": "ISC"
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/unicorn-magic": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz",
+ "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/vscode-jsonrpc": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
+ "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/vscode-languageserver": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz",
+ "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==",
+ "license": "MIT",
+ "dependencies": {
+ "vscode-languageserver-protocol": "3.17.5"
+ },
+ "bin": {
+ "installServerIntoExtension": "bin/installServerIntoExtension"
+ }
+ },
+ "node_modules/vscode-languageserver-protocol": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
+ "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
+ "license": "MIT",
+ "dependencies": {
+ "vscode-jsonrpc": "8.2.0",
+ "vscode-languageserver-types": "3.17.5"
+ }
+ },
+ "node_modules/vscode-languageserver-textdocument": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz",
+ "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==",
+ "license": "MIT"
+ },
+ "node_modules/vscode-languageserver-types": {
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
+ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/yaml": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
+ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^7.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
+ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/typespec/package.json b/typespec/package.json
new file mode 100644
index 0000000..5f77beb
--- /dev/null
+++ b/typespec/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "albatross-typespec",
+ "private": true,
+ "scripts": {
+ "build": "tsp compile api-server/ && tsp compile fortee/ && npm run copy-output",
+ "copy-output": "cp api-server/tsp-output/@typespec/openapi3/openapi.yaml ../openapi/api-server.yaml && cp fortee/tsp-output/@typespec/openapi3/openapi.yaml ../openapi/fortee.yaml"
+ },
+ "dependencies": {
+ "@typespec/compiler": "^1.9.0",
+ "@typespec/http": "^1.9.0",
+ "@typespec/openapi": "^1.9.0",
+ "@typespec/openapi3": "^1.9.0",
+ "@typespec/rest": "^0.79.0"
+ }
+}