2 Commits

Author SHA1 Message Date
84299675ff later 2025-03-31 18:24:02 -07:00
dd698528be validator: refactor everything 2025-03-31 18:24:02 -07:00
159 changed files with 2668 additions and 44320 deletions
.drone.ymlCargo.lockContainerfileREADME.mdcompose.yamlopenapi-internal.yamlopenapi.yaml
pkg
validation
web

@ -25,8 +25,6 @@ steps:
branch:
- master
- staging
event:
- push
- name: frontend
image: plugins/docker
@ -46,8 +44,6 @@ steps:
branch:
- master
- staging
event:
- push
- name: validator
image: plugins/docker
@ -67,8 +63,6 @@ steps:
branch:
- master
- staging
event:
- push
- name: deploy
image: argoproj/argocd:latest
@ -90,68 +84,8 @@ steps:
branch:
- master
- staging
event:
- push
# pr dry run
- name: api-pr
image: plugins/docker
settings:
registry: registry.itzana.me
repo: registry.itzana.me/strafesnet/maptest-validator
tags:
- ${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}
- ${DRONE_BRANCH}
dockerfile: Containerfile
context: .
dry_run: true
when:
event:
- pull_request
- name: frontend-pr
image: plugins/docker
settings:
registry: registry.itzana.me
repo: registry.itzana.me/strafesnet/maptest-validator
tags:
- ${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}
- ${DRONE_BRANCH}
dockerfile: web/Containerfile
context: web
dry_run: true
when:
event:
- pull_request
- name: validator-pr
image: plugins/docker
settings:
registry: registry.itzana.me
repo: registry.itzana.me/strafesnet/maptest-validator
tags:
- ${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}
- ${DRONE_BRANCH}
dockerfile: validation/Containerfile
context: validation
dry_run: true
when:
event:
- pull_request
- name: build-pr
image: alpine
commands:
- echo "Success!"
depends_on:
- api-pr
- frontend-pr
- validator-pr
when:
event:
- pull_request
---
kind: signature
hmac: 11e6d7f1eb839d3798fdcb642ca5523c011bd14c1f3a0343a9c3106bab9ef142
hmac: 1162b329a9cad12b4c5db0ccf8b8998072b0de9279326f76a493fd0af6794095
...

739
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -1,5 +1,5 @@
# Stage 1: Build
FROM registry.itzana.me/docker-proxy/golang:1.24 AS builder
FROM docker.io/golang:1.23 AS builder
# Set the working directory in the container
WORKDIR /app
@ -14,7 +14,7 @@ COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o service ./cmd/maps-service/service.go
# Stage 2: Run
FROM registry.itzana.me/docker-proxy/alpine:3.21
FROM alpine
# Set up a non-root user for security
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

@ -26,11 +26,10 @@ Prerequisite: golang installed
Prerequisite: bun installed
The environment variables `API_HOST` and `AUTH_HOST` will need to be set for the middleware.
The environment variable `API_HOST` will need to be set for the middleware.
Example `.env` in web's root:
```
API_HOST="http://localhost:8082/"
AUTH_HOST="http://localhost:8083/"
API_HOST="http://localhost:8082/v1/"
```
1. `cd web`

@ -50,7 +50,6 @@ services:
- "3000:3000"
environment:
- API_HOST=http://submissions:8082/v1
- AUTH_HOST=http://localhost:8080/
validation:
image:

@ -4,251 +4,9 @@ info:
description: Internal operations inaccessible from the public internet.
version: 0.1.0
tags:
- name: Mapfixes
description: Mapfix operations
- name: Submissions
description: Submission operations
paths:
/mapfixes:
post:
summary: Create a mapfix
operationId: createMapfix
tags:
- Mapfixes
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MapfixCreate'
responses:
"201":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/MapfixID"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/validated-model:
post:
summary: Update validated model
operationId: updateMapfixValidatedModel
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ValidatedModelID
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
- name: ValidatedModelVersion
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/error:
post:
summary: Validator posts an error to the audit log
operationId: createMapfixAuditError
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-submitted:
post:
summary: (Internal endpoint) Role Validator changes status from Submitting -> Submitted
operationId: actionMapfixSubmitted
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
- name: ModelVersion
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
- name: DisplayName
in: query
required: true
schema:
type: string
maxLength: 128
- name: Creator
in: query
required: true
schema:
type: string
maxLength: 128
- name: GameID
in: query
required: true
schema:
type: integer
format: int32
minimum: 0
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-request-changes:
post:
summary: (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested
operationId: actionMapfixRequestChanges
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-validated:
post:
summary: (Internal endpoint) Role Validator changes status from Validating -> Validated
operationId: actionMapfixValidated
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-failed:
post:
summary: (Internal endpoint) Role Validator changes status from Validating -> Accepted
operationId: actionMapfixAccepted
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/mapfixes/{MapfixID}/status/validator-uploaded:
post:
summary: (Internal endpoint) Role Validator changes status from Uploading -> Uploaded
operationId: actionMapfixUploaded
tags:
- Mapfixes
parameters:
- $ref: '#/components/parameters/MapfixID'
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/operations/{OperationID}/status/operation-failed:
post:
summary: (Internal endpoint) Fail an operation and write a StatusMessage
operationId: actionOperationFailed
tags:
- Operations
parameters:
- $ref: '#/components/parameters/OperationID'
- name: StatusMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions:
post:
summary: Create a new submission
operationId: createSubmission
tags:
- Submissions
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SubmissionCreate'
responses:
"201":
description: Successful response
content:
application/json:
schema:
$ref: "#/components/schemas/SubmissionID"
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/validated-model:
post:
summary: Update validated model
@ -263,98 +21,12 @@ paths:
schema:
type: integer
format: int64
minimum: 0
- name: ValidatedModelVersion
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/error:
post:
summary: Validator posts an error to the audit log
operationId: createSubmissionAuditError
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ErrorMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validator-submitted:
post:
summary: (Internal endpoint) Role Validator changes status from Submitting -> Submitted
operationId: actionSubmissionSubmitted
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: ModelVersion
in: query
required: true
schema:
type: integer
format: int64
minimum: 0
- name: DisplayName
in: query
required: true
schema:
type: string
maxLength: 128
- name: Creator
in: query
required: true
schema:
type: string
maxLength: 128
- name: GameID
in: query
required: true
schema:
type: integer
format: int32
minimum: 0
responses:
"204":
description: Successful response
default:
description: General Error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/submissions/{SubmissionID}/status/validator-request-changes:
post:
summary: (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested
operationId: actionSubmissionRequestChanges
tags:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
responses:
"204":
description: Successful response
@ -389,6 +61,13 @@ paths:
- Submissions
parameters:
- $ref: '#/components/parameters/SubmissionID'
- name: StatusMessage
in: query
required: true
schema:
type: string
minLength: 0
maxLength: 4096
responses:
"204":
description: Successful response
@ -412,7 +91,6 @@ paths:
schema:
type: integer
format: int64
minimum: 0
responses:
"204":
description: Successful response
@ -442,13 +120,11 @@ paths:
schema:
type: integer
format: int64
minimum: 0
- name: Policy
in: query
schema:
type: integer
format: int32
minimum: 0
responses:
"200":
description: Successful response
@ -481,7 +157,7 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/ScriptPolicyID"
$ref: "#/components/schemas/Id"
default:
description: General Error
content:
@ -513,18 +189,11 @@ paths:
schema:
type: string
maxLength: 1048576
- name: ResourceType
in: query
schema:
type: integer
format: int32
minimum: 0
- name: ResourceID
- name: SubmissionID
in: query
schema:
type: integer
format: int64
minimum: 0
responses:
"200":
description: Successful response
@ -557,7 +226,7 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/ScriptID"
$ref: "#/components/schemas/Id"
default:
description: General Error
content:
@ -587,24 +256,6 @@ paths:
$ref: "#/components/schemas/Error"
components:
parameters:
MapfixID:
name: MapfixID
in: path
required: true
description: The unique identifier for a submission.
schema:
type: integer
format: int64
minimum: 0
OperationID:
name: OperationID
in: path
required: true
description: The unique identifier for a long-running operation.
schema:
type: integer
format: int32
minimum: 0
SubmissionID:
name: SubmissionID
in: path
@ -613,7 +264,6 @@ components:
schema:
type: integer
format: int64
minimum: 0
ScriptID:
name: ScriptID
in: path
@ -622,7 +272,6 @@ components:
schema:
type: integer
format: int64
minimum: 0
Page:
name: Page
in: query
@ -641,135 +290,14 @@ components:
minimum: 1
maximum: 100
schemas:
MapfixID:
Id:
required:
- MapfixID
- ID
type: object
properties:
MapfixID:
ID:
type: integer
format: int64
minimum: 0
SubmissionID:
required:
- SubmissionID
type: object
properties:
SubmissionID:
type: integer
format: int64
minimum: 0
ScriptID:
required:
- ScriptID
type: object
properties:
ScriptID:
type: integer
format: int64
minimum: 0
ScriptPolicyID:
required:
- ScriptPolicyID
type: object
properties:
ScriptPolicyID:
type: integer
format: int64
minimum: 0
MapfixCreate:
required:
- OperationID
- AssetOwner
- DisplayName
- Creator
- GameID
- AssetID
- AssetVersion
- TargetAssetID
- Description
type: object
properties:
OperationID:
type: integer
format: int32
minimum: 0
AssetOwner:
type: integer
format: int64
minimum: 0
DisplayName:
type: string
maxLength: 128
Creator:
type: string
maxLength: 128
GameID:
type: integer
format: int32
minimum: 0
AssetID:
type: integer
format: int64
minimum: 0
AssetVersion:
type: integer
format: int64
minimum: 0
TargetAssetID:
type: integer
format: int64
minimum: 0
Description:
type: string
maxLength: 256
SubmissionCreate:
required:
- OperationID
- AssetOwner
- DisplayName
- Creator
- GameID
- AssetID
- AssetVersion
- Status
- Roles
type: object
properties:
OperationID:
type: integer
format: int32
minimum: 0
AssetOwner:
type: integer
format: int64
minimum: 0
DisplayName:
type: string
maxLength: 128
Creator:
type: string
maxLength: 128
GameID:
type: integer
format: int32
minimum: 0
AssetID:
type: integer
format: int64
minimum: 0
AssetVersion:
type: integer
format: int64
minimum: 0
Status:
type: integer
format: uint32
minimum: 0
maximum: 9
Roles:
type: integer
format: uint32
Script:
required:
- ID
@ -783,7 +311,6 @@ components:
ID:
type: integer
format: int64
minimum: 0
Name:
type: string
maxLength: 128
@ -797,11 +324,9 @@ components:
ResourceType:
type: integer
format: int32
minimum: 0
ResourceID:
type: integer
format: int64
minimum: 0
ScriptCreate:
required:
- Name
@ -819,11 +344,9 @@ components:
ResourceType:
type: integer
format: int32
minimum: 0
ResourceID:
type: integer
format: int64
minimum: 0
ScriptPolicy:
required:
- ID
@ -835,7 +358,6 @@ components:
ID:
type: integer
format: int64
minimum: 0
FromScriptHash:
type: string
minLength: 16
@ -843,11 +365,9 @@ components:
ToScriptID:
type: integer
format: int64
minimum: 0
Policy:
type: integer
format: int32
minimum: 0
ScriptPolicyCreate:
required:
- FromScriptID
@ -858,15 +378,12 @@ components:
FromScriptID:
type: integer
format: int64
minimum: 0
ToScriptID:
type: integer
format: int64
minimum: 0
Policy:
type: integer
format: int32
minimum: 0
Error:
description: Represents error object
type: object
@ -874,7 +391,6 @@ components:
code:
type: integer
format: int64
minimum: 0
message:
type: string
required:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -6,58 +6,32 @@ package api
type OperationName = string
const (
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixRejectOperation OperationName = "ActionMapfixReject"
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
ActionMapfixResetSubmittingOperation OperationName = "ActionMapfixResetSubmitting"
ActionMapfixRetryValidateOperation OperationName = "ActionMapfixRetryValidate"
ActionMapfixRevokeOperation OperationName = "ActionMapfixRevoke"
ActionMapfixTriggerSubmitOperation OperationName = "ActionMapfixTriggerSubmit"
ActionMapfixTriggerSubmitUncheckedOperation OperationName = "ActionMapfixTriggerSubmitUnchecked"
ActionMapfixTriggerUploadOperation OperationName = "ActionMapfixTriggerUpload"
ActionMapfixTriggerValidateOperation OperationName = "ActionMapfixTriggerValidate"
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionResetSubmittingOperation OperationName = "ActionSubmissionResetSubmitting"
ActionSubmissionRetryValidateOperation OperationName = "ActionSubmissionRetryValidate"
ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke"
ActionSubmissionTriggerSubmitOperation OperationName = "ActionSubmissionTriggerSubmit"
ActionSubmissionTriggerSubmitUncheckedOperation OperationName = "ActionSubmissionTriggerSubmitUnchecked"
ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateMapfixOperation OperationName = "CreateMapfix"
CreateMapfixAuditCommentOperation OperationName = "CreateMapfixAuditComment"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
CreateSubmissionAdminOperation OperationName = "CreateSubmissionAdmin"
CreateSubmissionAuditCommentOperation OperationName = "CreateSubmissionAuditComment"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
GetMapOperation OperationName = "GetMap"
GetMapfixOperation OperationName = "GetMapfix"
GetOperationOperation OperationName = "GetOperation"
GetScriptOperation OperationName = "GetScript"
GetScriptPolicyOperation OperationName = "GetScriptPolicy"
GetSubmissionOperation OperationName = "GetSubmission"
ListMapfixAuditEventsOperation OperationName = "ListMapfixAuditEvents"
ListMapfixesOperation OperationName = "ListMapfixes"
ListMapsOperation OperationName = "ListMaps"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
ListSubmissionAuditEventsOperation OperationName = "ListSubmissionAuditEvents"
ListSubmissionsOperation OperationName = "ListSubmissions"
ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions"
SessionRolesOperation OperationName = "SessionRoles"
SessionUserOperation OperationName = "SessionUser"
SessionValidateOperation OperationName = "SessionValidate"
SetMapfixCompletedOperation OperationName = "SetMapfixCompleted"
SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
UpdateMapfixModelOperation OperationName = "UpdateMapfixModel"
UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionRejectOperation OperationName = "ActionSubmissionReject"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionRetryValidateOperation OperationName = "ActionSubmissionRetryValidate"
ActionSubmissionRevokeOperation OperationName = "ActionSubmissionRevoke"
ActionSubmissionSubmitOperation OperationName = "ActionSubmissionSubmit"
ActionSubmissionTriggerUploadOperation OperationName = "ActionSubmissionTriggerUpload"
ActionSubmissionTriggerValidateOperation OperationName = "ActionSubmissionTriggerValidate"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
DeleteScriptOperation OperationName = "DeleteScript"
DeleteScriptPolicyOperation OperationName = "DeleteScriptPolicy"
GetScriptOperation OperationName = "GetScript"
GetScriptPolicyOperation OperationName = "GetScriptPolicy"
GetSubmissionOperation OperationName = "GetSubmission"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
ListSubmissionsOperation OperationName = "ListSubmissions"
ReleaseSubmissionsOperation OperationName = "ReleaseSubmissions"
SessionRolesOperation OperationName = "SessionRoles"
SessionUserOperation OperationName = "SessionUser"
SessionValidateOperation OperationName = "SessionValidate"
SetSubmissionCompletedOperation OperationName = "SetSubmissionCompleted"
UpdateScriptOperation OperationName = "UpdateScript"
UpdateScriptPolicyOperation OperationName = "UpdateScriptPolicy"
UpdateSubmissionModelOperation OperationName = "UpdateSubmissionModel"
)

File diff suppressed because it is too large Load Diff

@ -3,123 +3,18 @@
package api
import (
"fmt"
"io"
"mime"
"net/http"
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"go.uber.org/multierr"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/ogen-go/ogen/validate"
)
func (s *Server) decodeCreateMapfixRequest(r *http.Request) (
req *MapfixTriggerCreate,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request MapfixTriggerCreate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateMapfixAuditCommentRequest(r *http.Request) (
req CreateMapfixAuditCommentReq,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "text/plain":
reader := r.Body
request := CreateMapfixAuditCommentReq{Data: reader}
return request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateScriptRequest(r *http.Request) (
req *ScriptCreate,
close func() error,
@ -131,13 +26,13 @@ func (s *Server) decodeCreateScriptRequest(r *http.Request) (
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
rerr = multierr.Append(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
@ -202,13 +97,13 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
rerr = multierr.Append(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
@ -248,14 +143,6 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
@ -263,7 +150,7 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
}
func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
req *SubmissionTriggerCreate,
req *SubmissionCreate,
close func() error,
rerr error,
) {
@ -273,13 +160,13 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
rerr = multierr.Append(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
@ -302,7 +189,7 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
d := jx.DecodeBytes(buf)
var request SubmissionTriggerCreate
var request SubmissionCreate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
@ -333,111 +220,6 @@ func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
}
}
func (s *Server) decodeCreateSubmissionAdminRequest(r *http.Request) (
req *SubmissionTriggerCreate,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request SubmissionTriggerCreate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateSubmissionAuditCommentRequest(r *http.Request) (
req CreateSubmissionAuditCommentReq,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "text/plain":
reader := r.Body
request := CreateSubmissionAuditCommentReq{Data: reader}
return request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeReleaseSubmissionsRequest(r *http.Request) (
req []ReleaseInfo,
close func() error,
@ -449,13 +231,13 @@ func (s *Server) decodeReleaseSubmissionsRequest(r *http.Request) (
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
rerr = multierr.Append(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
@ -515,23 +297,6 @@ func (s *Server) decodeReleaseSubmissionsRequest(r *http.Request) (
}).ValidateLength(len(request)); err != nil {
return errors.Wrap(err, "array")
}
var failures []validate.FieldError
for i, elem := range request {
if err := func() error {
if err := elem.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: fmt.Sprintf("[%d]", i),
Error: err,
})
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
@ -553,13 +318,13 @@ func (s *Server) decodeUpdateScriptRequest(r *http.Request) (
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
rerr = multierr.Append(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
@ -624,13 +389,13 @@ func (s *Server) decodeUpdateScriptPolicyRequest(r *http.Request) (
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
rerr = multierr.Append(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
@ -670,14 +435,6 @@ func (s *Server) decodeUpdateScriptPolicyRequest(r *http.Request) (
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)

@ -11,30 +11,6 @@ import (
ht "github.com/ogen-go/ogen/http"
)
func encodeCreateMapfixRequest(
req *MapfixTriggerCreate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateMapfixAuditCommentRequest(
req CreateMapfixAuditCommentReq,
r *http.Request,
) error {
const contentType = "text/plain"
body := req
ht.SetBody(r, body, contentType)
return nil
}
func encodeCreateScriptRequest(
req *ScriptCreate,
r *http.Request,
@ -64,7 +40,7 @@ func encodeCreateScriptPolicyRequest(
}
func encodeCreateSubmissionRequest(
req *SubmissionTriggerCreate,
req *SubmissionCreate,
r *http.Request,
) error {
const contentType = "application/json"
@ -77,30 +53,6 @@ func encodeCreateSubmissionRequest(
return nil
}
func encodeCreateSubmissionAdminRequest(
req *SubmissionTriggerCreate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateSubmissionAuditCommentRequest(
req CreateSubmissionAuditCommentReq,
r *http.Request,
) error {
const contentType = "text/plain"
body := req
ht.SetBody(r, body, contentType)
return nil
}
func encodeReleaseSubmissionsRequest(
req []ReleaseInfo,
r *http.Request,

File diff suppressed because it is too large Load Diff

@ -13,83 +13,6 @@ import (
ht "github.com/ogen-go/ogen/http"
)
func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRejectResponse(response *ActionMapfixRejectNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRequestChangesResponse(response *ActionMapfixRequestChangesNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixResetSubmittingResponse(response *ActionMapfixResetSubmittingNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRetryValidateResponse(response *ActionMapfixRetryValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRevokeResponse(response *ActionMapfixRevokeNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixTriggerSubmitResponse(response *ActionMapfixTriggerSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixTriggerSubmitUncheckedResponse(response *ActionMapfixTriggerSubmitUncheckedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixTriggerUploadResponse(response *ActionMapfixTriggerUploadNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixTriggerValidateResponse(response *ActionMapfixTriggerValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixValidatedResponse(response *ActionMapfixValidatedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -111,13 +34,6 @@ func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequ
return nil
}
func encodeActionSubmissionResetSubmittingResponse(response *ActionSubmissionResetSubmittingNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionRetryValidateResponse(response *ActionSubmissionRetryValidateNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -132,14 +48,7 @@ func encodeActionSubmissionRevokeResponse(response *ActionSubmissionRevokeNoCont
return nil
}
func encodeActionSubmissionTriggerSubmitResponse(response *ActionSubmissionTriggerSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionTriggerSubmitUncheckedResponse(response *ActionSubmissionTriggerSubmitUncheckedNoContent, w http.ResponseWriter, span trace.Span) error {
func encodeActionSubmissionSubmitResponse(response *ActionSubmissionSubmitNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -167,7 +76,7 @@ func encodeActionSubmissionValidatedResponse(response *ActionSubmissionValidated
return nil
}
func encodeCreateMapfixResponse(response *OperationID, w http.ResponseWriter, span trace.Span) error {
func encodeCreateScriptResponse(response *ID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
@ -181,14 +90,7 @@ func encodeCreateMapfixResponse(response *OperationID, w http.ResponseWriter, sp
return nil
}
func encodeCreateMapfixAuditCommentResponse(response *CreateMapfixAuditCommentNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeCreateScriptResponse(response *ScriptID, w http.ResponseWriter, span trace.Span) error {
func encodeCreateScriptPolicyResponse(response *ID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
@ -202,7 +104,7 @@ func encodeCreateScriptResponse(response *ScriptID, w http.ResponseWriter, span
return nil
}
func encodeCreateScriptPolicyResponse(response *ScriptPolicyID, w http.ResponseWriter, span trace.Span) error {
func encodeCreateSubmissionResponse(response *ID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
@ -216,41 +118,6 @@ func encodeCreateScriptPolicyResponse(response *ScriptPolicyID, w http.ResponseW
return nil
}
func encodeCreateSubmissionResponse(response *OperationID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeCreateSubmissionAdminResponse(response *OperationID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeCreateSubmissionAuditCommentResponse(response *CreateSubmissionAuditCommentNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeDeleteScriptResponse(response *DeleteScriptNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -265,48 +132,6 @@ func encodeDeleteScriptPolicyResponse(response *DeleteScriptPolicyNoContent, w h
return nil
}
func encodeGetMapResponse(response *Map, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeGetMapfixResponse(response *Mapfix, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeGetOperationResponse(response *Operation, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeGetScriptResponse(response *Script, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
@ -349,56 +174,6 @@ func encodeGetSubmissionResponse(response *Submission, w http.ResponseWriter, sp
return nil
}
func encodeListMapfixAuditEventsResponse(response []AuditEvent, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
e.ArrStart()
for _, elem := range response {
elem.Encode(e)
}
e.ArrEnd()
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeListMapfixesResponse(response *Mapfixes, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeListMapsResponse(response []Map, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
e.ArrStart()
for _, elem := range response {
elem.Encode(e)
}
e.ArrEnd()
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeListScriptPolicyResponse(response []ScriptPolicy, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
@ -435,7 +210,7 @@ func encodeListScriptsResponse(response []Script, w http.ResponseWriter, span tr
return nil
}
func encodeListSubmissionAuditEventsResponse(response []AuditEvent, w http.ResponseWriter, span trace.Span) error {
func encodeListSubmissionsResponse(response []Submission, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
@ -453,20 +228,6 @@ func encodeListSubmissionAuditEventsResponse(response []AuditEvent, w http.Respo
return nil
}
func encodeListSubmissionsResponse(response *Submissions, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
span.SetStatus(codes.Ok, http.StatusText(200))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeReleaseSubmissionsResponse(response *ReleaseSubmissionsCreated, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
@ -516,13 +277,6 @@ func encodeSessionValidateResponse(response bool, w http.ResponseWriter, span tr
return nil
}
func encodeSetMapfixCompletedResponse(response *SetMapfixCompletedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeSetSubmissionCompletedResponse(response *SetSubmissionCompletedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -530,13 +284,6 @@ func encodeSetSubmissionCompletedResponse(response *SetSubmissionCompletedNoCont
return nil
}
func encodeUpdateMapfixModelResponse(response *UpdateMapfixModelNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeUpdateScriptResponse(response *UpdateScriptNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))

File diff suppressed because it is too large Load Diff

@ -4,49 +4,13 @@ package api
import (
"fmt"
"io"
"time"
"github.com/go-faster/jx"
)
func (s *ErrorStatusCode) Error() string {
return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response)
}
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
type ActionMapfixAcceptedNoContent struct{}
// ActionMapfixRejectNoContent is response for ActionMapfixReject operation.
type ActionMapfixRejectNoContent struct{}
// ActionMapfixRequestChangesNoContent is response for ActionMapfixRequestChanges operation.
type ActionMapfixRequestChangesNoContent struct{}
// ActionMapfixResetSubmittingNoContent is response for ActionMapfixResetSubmitting operation.
type ActionMapfixResetSubmittingNoContent struct{}
// ActionMapfixRetryValidateNoContent is response for ActionMapfixRetryValidate operation.
type ActionMapfixRetryValidateNoContent struct{}
// ActionMapfixRevokeNoContent is response for ActionMapfixRevoke operation.
type ActionMapfixRevokeNoContent struct{}
// ActionMapfixTriggerSubmitNoContent is response for ActionMapfixTriggerSubmit operation.
type ActionMapfixTriggerSubmitNoContent struct{}
// ActionMapfixTriggerSubmitUncheckedNoContent is response for ActionMapfixTriggerSubmitUnchecked operation.
type ActionMapfixTriggerSubmitUncheckedNoContent struct{}
// ActionMapfixTriggerUploadNoContent is response for ActionMapfixTriggerUpload operation.
type ActionMapfixTriggerUploadNoContent struct{}
// ActionMapfixTriggerValidateNoContent is response for ActionMapfixTriggerValidate operation.
type ActionMapfixTriggerValidateNoContent struct{}
// ActionMapfixValidatedNoContent is response for ActionMapfixValidated operation.
type ActionMapfixValidatedNoContent struct{}
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
type ActionSubmissionAcceptedNoContent struct{}
@ -56,20 +20,14 @@ type ActionSubmissionRejectNoContent struct{}
// ActionSubmissionRequestChangesNoContent is response for ActionSubmissionRequestChanges operation.
type ActionSubmissionRequestChangesNoContent struct{}
// ActionSubmissionResetSubmittingNoContent is response for ActionSubmissionResetSubmitting operation.
type ActionSubmissionResetSubmittingNoContent struct{}
// ActionSubmissionRetryValidateNoContent is response for ActionSubmissionRetryValidate operation.
type ActionSubmissionRetryValidateNoContent struct{}
// ActionSubmissionRevokeNoContent is response for ActionSubmissionRevoke operation.
type ActionSubmissionRevokeNoContent struct{}
// ActionSubmissionTriggerSubmitNoContent is response for ActionSubmissionTriggerSubmit operation.
type ActionSubmissionTriggerSubmitNoContent struct{}
// ActionSubmissionTriggerSubmitUncheckedNoContent is response for ActionSubmissionTriggerSubmitUnchecked operation.
type ActionSubmissionTriggerSubmitUncheckedNoContent struct{}
// ActionSubmissionSubmitNoContent is response for ActionSubmissionSubmit operation.
type ActionSubmissionSubmitNoContent struct{}
// ActionSubmissionTriggerUploadNoContent is response for ActionSubmissionTriggerUpload operation.
type ActionSubmissionTriggerUploadNoContent struct{}
@ -80,115 +38,8 @@ type ActionSubmissionTriggerValidateNoContent struct{}
// ActionSubmissionValidatedNoContent is response for ActionSubmissionValidated operation.
type ActionSubmissionValidatedNoContent struct{}
// Ref: #/components/schemas/AuditEvent
type AuditEvent struct {
ID int64 `json:"ID"`
Date int64 `json:"Date"`
User int64 `json:"User"`
Username string `json:"Username"`
// Is this a submission or is it a mapfix.
ResourceType int32 `json:"ResourceType"`
ResourceID int64 `json:"ResourceID"`
EventType int32 `json:"EventType"`
// Arbitrary event data.
EventData AuditEventEventData `json:"EventData"`
}
// GetID returns the value of ID.
func (s *AuditEvent) GetID() int64 {
return s.ID
}
// GetDate returns the value of Date.
func (s *AuditEvent) GetDate() int64 {
return s.Date
}
// GetUser returns the value of User.
func (s *AuditEvent) GetUser() int64 {
return s.User
}
// GetUsername returns the value of Username.
func (s *AuditEvent) GetUsername() string {
return s.Username
}
// GetResourceType returns the value of ResourceType.
func (s *AuditEvent) GetResourceType() int32 {
return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *AuditEvent) GetResourceID() int64 {
return s.ResourceID
}
// GetEventType returns the value of EventType.
func (s *AuditEvent) GetEventType() int32 {
return s.EventType
}
// GetEventData returns the value of EventData.
func (s *AuditEvent) GetEventData() AuditEventEventData {
return s.EventData
}
// SetID sets the value of ID.
func (s *AuditEvent) SetID(val int64) {
s.ID = val
}
// SetDate sets the value of Date.
func (s *AuditEvent) SetDate(val int64) {
s.Date = val
}
// SetUser sets the value of User.
func (s *AuditEvent) SetUser(val int64) {
s.User = val
}
// SetUsername sets the value of Username.
func (s *AuditEvent) SetUsername(val string) {
s.Username = val
}
// SetResourceType sets the value of ResourceType.
func (s *AuditEvent) SetResourceType(val int32) {
s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *AuditEvent) SetResourceID(val int64) {
s.ResourceID = val
}
// SetEventType sets the value of EventType.
func (s *AuditEvent) SetEventType(val int32) {
s.EventType = val
}
// SetEventData sets the value of EventData.
func (s *AuditEvent) SetEventData(val AuditEventEventData) {
s.EventData = val
}
// Arbitrary event data.
type AuditEventEventData map[string]jx.Raw
func (s *AuditEventEventData) init() AuditEventEventData {
m := *s
if m == nil {
m = map[string]jx.Raw{}
*s = m
}
return m
}
type CookieAuth struct {
APIKey string
Roles []string
}
// GetAPIKey returns the value of APIKey.
@ -196,55 +47,11 @@ func (s *CookieAuth) GetAPIKey() string {
return s.APIKey
}
// GetRoles returns the value of Roles.
func (s *CookieAuth) GetRoles() []string {
return s.Roles
}
// SetAPIKey sets the value of APIKey.
func (s *CookieAuth) SetAPIKey(val string) {
s.APIKey = val
}
// SetRoles sets the value of Roles.
func (s *CookieAuth) SetRoles(val []string) {
s.Roles = val
}
// CreateMapfixAuditCommentNoContent is response for CreateMapfixAuditComment operation.
type CreateMapfixAuditCommentNoContent struct{}
type CreateMapfixAuditCommentReq struct {
Data io.Reader
}
// Read reads data from the Data reader.
//
// Kept to satisfy the io.Reader interface.
func (s CreateMapfixAuditCommentReq) Read(p []byte) (n int, err error) {
if s.Data == nil {
return 0, io.EOF
}
return s.Data.Read(p)
}
// CreateSubmissionAuditCommentNoContent is response for CreateSubmissionAuditComment operation.
type CreateSubmissionAuditCommentNoContent struct{}
type CreateSubmissionAuditCommentReq struct {
Data io.Reader
}
// Read reads data from the Data reader.
//
// Kept to satisfy the io.Reader interface.
func (s CreateSubmissionAuditCommentReq) Read(p []byte) (n int, err error) {
if s.Data == nil {
return 0, io.EOF
}
return s.Data.Read(p)
}
// DeleteScriptNoContent is response for DeleteScript operation.
type DeleteScriptNoContent struct{}
@ -304,360 +111,21 @@ func (s *ErrorStatusCode) SetResponse(val Error) {
s.Response = val
}
// Ref: #/components/schemas/Map
type Map struct {
ID int64 `json:"ID"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
Date int64 `json:"Date"`
// Ref: #/components/schemas/Id
type ID struct {
ID int64 `json:"ID"`
}
// GetID returns the value of ID.
func (s *Map) GetID() int64 {
func (s *ID) GetID() int64 {
return s.ID
}
// GetDisplayName returns the value of DisplayName.
func (s *Map) GetDisplayName() string {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *Map) GetCreator() string {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *Map) GetGameID() int32 {
return s.GameID
}
// GetDate returns the value of Date.
func (s *Map) GetDate() int64 {
return s.Date
}
// SetID sets the value of ID.
func (s *Map) SetID(val int64) {
func (s *ID) SetID(val int64) {
s.ID = val
}
// SetDisplayName sets the value of DisplayName.
func (s *Map) SetDisplayName(val string) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *Map) SetCreator(val string) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *Map) SetGameID(val int32) {
s.GameID = val
}
// SetDate sets the value of Date.
func (s *Map) SetDate(val int64) {
s.Date = val
}
// Ref: #/components/schemas/Mapfix
type Mapfix struct {
ID int64 `json:"ID"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
CreatedAt int64 `json:"CreatedAt"`
UpdatedAt int64 `json:"UpdatedAt"`
Submitter int64 `json:"Submitter"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
Completed bool `json:"Completed"`
TargetAssetID int64 `json:"TargetAssetID"`
StatusID int32 `json:"StatusID"`
Description string `json:"Description"`
}
// GetID returns the value of ID.
func (s *Mapfix) GetID() int64 {
return s.ID
}
// GetDisplayName returns the value of DisplayName.
func (s *Mapfix) GetDisplayName() string {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *Mapfix) GetCreator() string {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *Mapfix) GetGameID() int32 {
return s.GameID
}
// GetCreatedAt returns the value of CreatedAt.
func (s *Mapfix) GetCreatedAt() int64 {
return s.CreatedAt
}
// GetUpdatedAt returns the value of UpdatedAt.
func (s *Mapfix) GetUpdatedAt() int64 {
return s.UpdatedAt
}
// GetSubmitter returns the value of Submitter.
func (s *Mapfix) GetSubmitter() int64 {
return s.Submitter
}
// GetAssetID returns the value of AssetID.
func (s *Mapfix) GetAssetID() int64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *Mapfix) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetCompleted returns the value of Completed.
func (s *Mapfix) GetCompleted() bool {
return s.Completed
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *Mapfix) GetTargetAssetID() int64 {
return s.TargetAssetID
}
// GetStatusID returns the value of StatusID.
func (s *Mapfix) GetStatusID() int32 {
return s.StatusID
}
// GetDescription returns the value of Description.
func (s *Mapfix) GetDescription() string {
return s.Description
}
// SetID sets the value of ID.
func (s *Mapfix) SetID(val int64) {
s.ID = val
}
// SetDisplayName sets the value of DisplayName.
func (s *Mapfix) SetDisplayName(val string) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *Mapfix) SetCreator(val string) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *Mapfix) SetGameID(val int32) {
s.GameID = val
}
// SetCreatedAt sets the value of CreatedAt.
func (s *Mapfix) SetCreatedAt(val int64) {
s.CreatedAt = val
}
// SetUpdatedAt sets the value of UpdatedAt.
func (s *Mapfix) SetUpdatedAt(val int64) {
s.UpdatedAt = val
}
// SetSubmitter sets the value of Submitter.
func (s *Mapfix) SetSubmitter(val int64) {
s.Submitter = val
}
// SetAssetID sets the value of AssetID.
func (s *Mapfix) SetAssetID(val int64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *Mapfix) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetCompleted sets the value of Completed.
func (s *Mapfix) SetCompleted(val bool) {
s.Completed = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *Mapfix) SetTargetAssetID(val int64) {
s.TargetAssetID = val
}
// SetStatusID sets the value of StatusID.
func (s *Mapfix) SetStatusID(val int32) {
s.StatusID = val
}
// SetDescription sets the value of Description.
func (s *Mapfix) SetDescription(val string) {
s.Description = val
}
// Ref: #/components/schemas/MapfixTriggerCreate
type MapfixTriggerCreate struct {
AssetID int64 `json:"AssetID"`
TargetAssetID int64 `json:"TargetAssetID"`
Description string `json:"Description"`
}
// GetAssetID returns the value of AssetID.
func (s *MapfixTriggerCreate) GetAssetID() int64 {
return s.AssetID
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *MapfixTriggerCreate) GetTargetAssetID() int64 {
return s.TargetAssetID
}
// GetDescription returns the value of Description.
func (s *MapfixTriggerCreate) GetDescription() string {
return s.Description
}
// SetAssetID sets the value of AssetID.
func (s *MapfixTriggerCreate) SetAssetID(val int64) {
s.AssetID = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *MapfixTriggerCreate) SetTargetAssetID(val int64) {
s.TargetAssetID = val
}
// SetDescription sets the value of Description.
func (s *MapfixTriggerCreate) SetDescription(val string) {
s.Description = val
}
// Ref: #/components/schemas/Mapfixes
type Mapfixes struct {
Total int64 `json:"Total"`
Mapfixes []Mapfix `json:"Mapfixes"`
}
// GetTotal returns the value of Total.
func (s *Mapfixes) GetTotal() int64 {
return s.Total
}
// GetMapfixes returns the value of Mapfixes.
func (s *Mapfixes) GetMapfixes() []Mapfix {
return s.Mapfixes
}
// SetTotal sets the value of Total.
func (s *Mapfixes) SetTotal(val int64) {
s.Total = val
}
// SetMapfixes sets the value of Mapfixes.
func (s *Mapfixes) SetMapfixes(val []Mapfix) {
s.Mapfixes = val
}
// Ref: #/components/schemas/Operation
type Operation struct {
OperationID int32 `json:"OperationID"`
Date int64 `json:"Date"`
Owner int64 `json:"Owner"`
Status int32 `json:"Status"`
StatusMessage string `json:"StatusMessage"`
Path string `json:"Path"`
}
// GetOperationID returns the value of OperationID.
func (s *Operation) GetOperationID() int32 {
return s.OperationID
}
// GetDate returns the value of Date.
func (s *Operation) GetDate() int64 {
return s.Date
}
// GetOwner returns the value of Owner.
func (s *Operation) GetOwner() int64 {
return s.Owner
}
// GetStatus returns the value of Status.
func (s *Operation) GetStatus() int32 {
return s.Status
}
// GetStatusMessage returns the value of StatusMessage.
func (s *Operation) GetStatusMessage() string {
return s.StatusMessage
}
// GetPath returns the value of Path.
func (s *Operation) GetPath() string {
return s.Path
}
// SetOperationID sets the value of OperationID.
func (s *Operation) SetOperationID(val int32) {
s.OperationID = val
}
// SetDate sets the value of Date.
func (s *Operation) SetDate(val int64) {
s.Date = val
}
// SetOwner sets the value of Owner.
func (s *Operation) SetOwner(val int64) {
s.Owner = val
}
// SetStatus sets the value of Status.
func (s *Operation) SetStatus(val int32) {
s.Status = val
}
// SetStatusMessage sets the value of StatusMessage.
func (s *Operation) SetStatusMessage(val string) {
s.StatusMessage = val
}
// SetPath sets the value of Path.
func (s *Operation) SetPath(val string) {
s.Path = val
}
// Ref: #/components/schemas/OperationID
type OperationID struct {
OperationID int32 `json:"OperationID"`
}
// GetOperationID returns the value of OperationID.
func (s *OperationID) GetOperationID() int32 {
return s.OperationID
}
// SetOperationID sets the value of OperationID.
func (s *OperationID) SetOperationID(val int32) {
s.OperationID = val
}
// NewOptInt32 returns new OptInt32 with value set to v.
func NewOptInt32(v int32) OptInt32 {
return OptInt32{
@ -958,21 +426,6 @@ func (s *ScriptCreate) SetResourceID(val OptInt64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptID
type ScriptID struct {
ScriptID int64 `json:"ScriptID"`
}
// GetScriptID returns the value of ScriptID.
func (s *ScriptID) GetScriptID() int64 {
return s.ScriptID
}
// SetScriptID sets the value of ScriptID.
func (s *ScriptID) SetScriptID(val int64) {
s.ScriptID = val
}
// Ref: #/components/schemas/ScriptPolicy
type ScriptPolicy struct {
ID int64 `json:"ID"`
@ -1058,21 +511,6 @@ func (s *ScriptPolicyCreate) SetPolicy(val int32) {
s.Policy = val
}
// Ref: #/components/schemas/ScriptPolicyID
type ScriptPolicyID struct {
ScriptPolicyID int64 `json:"ScriptPolicyID"`
}
// GetScriptPolicyID returns the value of ScriptPolicyID.
func (s *ScriptPolicyID) GetScriptPolicyID() int64 {
return s.ScriptPolicyID
}
// SetScriptPolicyID sets the value of ScriptPolicyID.
func (s *ScriptPolicyID) SetScriptPolicyID(val int64) {
s.ScriptPolicyID = val
}
// Ref: #/components/schemas/ScriptPolicyUpdate
type ScriptPolicyUpdate struct {
ID int64 `json:"ID"`
@ -1180,28 +618,24 @@ func (s *ScriptUpdate) SetResourceID(val OptInt64) {
s.ResourceID = val
}
// SetMapfixCompletedNoContent is response for SetMapfixCompleted operation.
type SetMapfixCompletedNoContent struct{}
// SetSubmissionCompletedNoContent is response for SetSubmissionCompleted operation.
type SetSubmissionCompletedNoContent struct{}
// Ref: #/components/schemas/Submission
type Submission struct {
ID int64 `json:"ID"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
CreatedAt int64 `json:"CreatedAt"`
UpdatedAt int64 `json:"UpdatedAt"`
Submitter int64 `json:"Submitter"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
ValidatedAssetID OptInt64 `json:"ValidatedAssetID"`
ValidatedAssetVersion OptInt64 `json:"ValidatedAssetVersion"`
Completed bool `json:"Completed"`
UploadedAssetID OptInt64 `json:"UploadedAssetID"`
StatusID int32 `json:"StatusID"`
ID int64 `json:"ID"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
CreatedAt int64 `json:"CreatedAt"`
UpdatedAt int64 `json:"UpdatedAt"`
Submitter int64 `json:"Submitter"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
Completed bool `json:"Completed"`
UploadedAssetID OptInt64 `json:"UploadedAssetID"`
StatusID int32 `json:"StatusID"`
StatusMessage string `json:"StatusMessage"`
}
// GetID returns the value of ID.
@ -1249,16 +683,6 @@ func (s *Submission) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetValidatedAssetID returns the value of ValidatedAssetID.
func (s *Submission) GetValidatedAssetID() OptInt64 {
return s.ValidatedAssetID
}
// GetValidatedAssetVersion returns the value of ValidatedAssetVersion.
func (s *Submission) GetValidatedAssetVersion() OptInt64 {
return s.ValidatedAssetVersion
}
// GetCompleted returns the value of Completed.
func (s *Submission) GetCompleted() bool {
return s.Completed
@ -1274,6 +698,11 @@ func (s *Submission) GetStatusID() int32 {
return s.StatusID
}
// GetStatusMessage returns the value of StatusMessage.
func (s *Submission) GetStatusMessage() string {
return s.StatusMessage
}
// SetID sets the value of ID.
func (s *Submission) SetID(val int64) {
s.ID = val
@ -1319,16 +748,6 @@ func (s *Submission) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetValidatedAssetID sets the value of ValidatedAssetID.
func (s *Submission) SetValidatedAssetID(val OptInt64) {
s.ValidatedAssetID = val
}
// SetValidatedAssetVersion sets the value of ValidatedAssetVersion.
func (s *Submission) SetValidatedAssetVersion(val OptInt64) {
s.ValidatedAssetVersion = val
}
// SetCompleted sets the value of Completed.
func (s *Submission) SetCompleted(val bool) {
s.Completed = val
@ -1344,83 +763,70 @@ func (s *Submission) SetStatusID(val int32) {
s.StatusID = val
}
// Ref: #/components/schemas/SubmissionTriggerCreate
type SubmissionTriggerCreate struct {
AssetID int64 `json:"AssetID"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
// SetStatusMessage sets the value of StatusMessage.
func (s *Submission) SetStatusMessage(val string) {
s.StatusMessage = val
}
// GetAssetID returns the value of AssetID.
func (s *SubmissionTriggerCreate) GetAssetID() int64 {
return s.AssetID
// Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct {
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
}
// GetDisplayName returns the value of DisplayName.
func (s *SubmissionTriggerCreate) GetDisplayName() string {
func (s *SubmissionCreate) GetDisplayName() string {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *SubmissionTriggerCreate) GetCreator() string {
func (s *SubmissionCreate) GetCreator() string {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *SubmissionTriggerCreate) GetGameID() int32 {
func (s *SubmissionCreate) GetGameID() int32 {
return s.GameID
}
// SetAssetID sets the value of AssetID.
func (s *SubmissionTriggerCreate) SetAssetID(val int64) {
s.AssetID = val
// GetAssetID returns the value of AssetID.
func (s *SubmissionCreate) GetAssetID() int64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *SubmissionCreate) GetAssetVersion() int64 {
return s.AssetVersion
}
// SetDisplayName sets the value of DisplayName.
func (s *SubmissionTriggerCreate) SetDisplayName(val string) {
func (s *SubmissionCreate) SetDisplayName(val string) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *SubmissionTriggerCreate) SetCreator(val string) {
func (s *SubmissionCreate) SetCreator(val string) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *SubmissionTriggerCreate) SetGameID(val int32) {
func (s *SubmissionCreate) SetGameID(val int32) {
s.GameID = val
}
// Ref: #/components/schemas/Submissions
type Submissions struct {
Total int64 `json:"Total"`
Submissions []Submission `json:"Submissions"`
// SetAssetID sets the value of AssetID.
func (s *SubmissionCreate) SetAssetID(val int64) {
s.AssetID = val
}
// GetTotal returns the value of Total.
func (s *Submissions) GetTotal() int64 {
return s.Total
// SetAssetVersion sets the value of AssetVersion.
func (s *SubmissionCreate) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// GetSubmissions returns the value of Submissions.
func (s *Submissions) GetSubmissions() []Submission {
return s.Submissions
}
// SetTotal sets the value of Total.
func (s *Submissions) SetTotal(val int64) {
s.Total = val
}
// SetSubmissions sets the value of Submissions.
func (s *Submissions) SetSubmissions(val []Submission) {
s.Submissions = val
}
// UpdateMapfixModelNoContent is response for UpdateMapfixModel operation.
type UpdateMapfixModelNoContent struct{}
// UpdateScriptNoContent is response for UpdateScript operation.
type UpdateScriptNoContent struct{}

@ -33,51 +33,6 @@ func findAuthorization(h http.Header, prefix string) (string, bool) {
return "", false
}
var operationRolesCookieAuth = map[string][]string{
ActionMapfixAcceptedOperation: []string{},
ActionMapfixRejectOperation: []string{},
ActionMapfixRequestChangesOperation: []string{},
ActionMapfixResetSubmittingOperation: []string{},
ActionMapfixRetryValidateOperation: []string{},
ActionMapfixRevokeOperation: []string{},
ActionMapfixTriggerSubmitOperation: []string{},
ActionMapfixTriggerSubmitUncheckedOperation: []string{},
ActionMapfixTriggerUploadOperation: []string{},
ActionMapfixTriggerValidateOperation: []string{},
ActionMapfixValidatedOperation: []string{},
ActionSubmissionAcceptedOperation: []string{},
ActionSubmissionRejectOperation: []string{},
ActionSubmissionRequestChangesOperation: []string{},
ActionSubmissionResetSubmittingOperation: []string{},
ActionSubmissionRetryValidateOperation: []string{},
ActionSubmissionRevokeOperation: []string{},
ActionSubmissionTriggerSubmitOperation: []string{},
ActionSubmissionTriggerSubmitUncheckedOperation: []string{},
ActionSubmissionTriggerUploadOperation: []string{},
ActionSubmissionTriggerValidateOperation: []string{},
ActionSubmissionValidatedOperation: []string{},
CreateMapfixOperation: []string{},
CreateMapfixAuditCommentOperation: []string{},
CreateScriptOperation: []string{},
CreateScriptPolicyOperation: []string{},
CreateSubmissionOperation: []string{},
CreateSubmissionAdminOperation: []string{},
CreateSubmissionAuditCommentOperation: []string{},
DeleteScriptOperation: []string{},
DeleteScriptPolicyOperation: []string{},
GetOperationOperation: []string{},
ReleaseSubmissionsOperation: []string{},
SessionRolesOperation: []string{},
SessionUserOperation: []string{},
SessionValidateOperation: []string{},
SetMapfixCompletedOperation: []string{},
SetSubmissionCompletedOperation: []string{},
UpdateMapfixModelOperation: []string{},
UpdateScriptOperation: []string{},
UpdateScriptPolicyOperation: []string{},
UpdateSubmissionModelOperation: []string{},
}
func (s *Server) securityCookieAuth(ctx context.Context, operationName OperationName, req *http.Request) (context.Context, bool, error) {
var t CookieAuth
const parameterName = "session_id"
@ -91,7 +46,6 @@ func (s *Server) securityCookieAuth(ctx context.Context, operationName Operation
return nil, false, errors.Wrap(err, "get cookie value")
}
t.APIKey = value
t.Roles = operationRolesCookieAuth[operationName]
rctx, err := s.sec.HandleCookieAuth(ctx, operationName, t)
if errors.Is(err, ogenerrors.ErrSkipServerSecurity) {
return nil, false, nil

@ -8,73 +8,6 @@ import (
// Handler handles operations described by OpenAPI v3 specification.
type Handler interface {
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/reset-validating
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixReject implements actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
//
// POST /mapfixes/{MapfixID}/status/reject
ActionMapfixReject(ctx context.Context, params ActionMapfixRejectParams) error
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/request-changes
ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error
// ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation.
//
// Role Submitter manually resets submitting softlock and changes status from Submitting ->
// UnderConstruction.
//
// POST /mapfixes/{MapfixID}/status/reset-submitting
ActionMapfixResetSubmitting(ctx context.Context, params ActionMapfixResetSubmittingParams) error
// ActionMapfixRetryValidate implements actionMapfixRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /mapfixes/{MapfixID}/status/retry-validate
ActionMapfixRetryValidate(ctx context.Context, params ActionMapfixRetryValidateParams) error
// ActionMapfixRevoke implements actionMapfixRevoke operation.
//
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
//
// POST /mapfixes/{MapfixID}/status/revoke
ActionMapfixRevoke(ctx context.Context, params ActionMapfixRevokeParams) error
// ActionMapfixTriggerSubmit implements actionMapfixTriggerSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit
ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error
// ActionMapfixTriggerSubmitUnchecked implements actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) error
// ActionMapfixTriggerUpload implements actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
//
// POST /mapfixes/{MapfixID}/status/trigger-upload
ActionMapfixTriggerUpload(ctx context.Context, params ActionMapfixTriggerUploadParams) error
// ActionMapfixTriggerValidate implements actionMapfixTriggerValidate operation.
//
// Role Reviewer triggers validation and changes status from Submitted -> Validating.
//
// POST /mapfixes/{MapfixID}/status/trigger-validate
ActionMapfixTriggerValidate(ctx context.Context, params ActionMapfixTriggerValidateParams) error
// ActionMapfixValidated implements actionMapfixValidated operation.
//
// Role Admin manually resets uploading softlock and changes status from Uploading -> Validated.
//
// POST /mapfixes/{MapfixID}/status/reset-uploading
ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted.
@ -93,13 +26,6 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/request-changes
ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error
// ActionSubmissionResetSubmitting implements actionSubmissionResetSubmitting operation.
//
// Role Submitter manually resets submitting softlock and changes status from Submitting ->
// UnderConstruction.
//
// POST /submissions/{SubmissionID}/status/reset-submitting
ActionSubmissionResetSubmitting(ctx context.Context, params ActionSubmissionResetSubmittingParams) error
// ActionSubmissionRetryValidate implements actionSubmissionRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
@ -112,18 +38,12 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/revoke
ActionSubmissionRevoke(ctx context.Context, params ActionSubmissionRevokeParams) error
// ActionSubmissionTriggerSubmit implements actionSubmissionTriggerSubmit operation.
// ActionSubmissionSubmit implements actionSubmissionSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/trigger-submit
ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error
// ActionSubmissionTriggerSubmitUnchecked implements actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) error
// POST /submissions/{SubmissionID}/status/submit
ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error
// ActionSubmissionTriggerUpload implements actionSubmissionTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
@ -142,48 +62,24 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/reset-uploading
ActionSubmissionValidated(ctx context.Context, params ActionSubmissionValidatedParams) error
// CreateMapfix implements createMapfix operation.
//
// Trigger the validator to create a mapfix.
//
// POST /mapfixes
CreateMapfix(ctx context.Context, req *MapfixTriggerCreate) (*OperationID, error)
// CreateMapfixAuditComment implements createMapfixAuditComment operation.
//
// Post a comment to the audit log.
//
// POST /mapfixes/{MapfixID}/comment
CreateMapfixAuditComment(ctx context.Context, req CreateMapfixAuditCommentReq, params CreateMapfixAuditCommentParams) error
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
CreateScript(ctx context.Context, req *ScriptCreate) (*ScriptID, error)
CreateScript(ctx context.Context, req *ScriptCreate) (*ID, error)
// CreateScriptPolicy implements createScriptPolicy operation.
//
// Create a new script policy.
//
// POST /script-policy
CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (*ScriptPolicyID, error)
CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (*ID, error)
// CreateSubmission implements createSubmission operation.
//
// Trigger the validator to create a new submission.
// Create new submission.
//
// POST /submissions
CreateSubmission(ctx context.Context, req *SubmissionTriggerCreate) (*OperationID, error)
// CreateSubmissionAdmin implements createSubmissionAdmin operation.
//
// Trigger the validator to create a new submission.
//
// POST /submissions-admin
CreateSubmissionAdmin(ctx context.Context, req *SubmissionTriggerCreate) (*OperationID, error)
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
//
// Post a comment to the audit log.
//
// POST /submissions/{SubmissionID}/comment
CreateSubmissionAuditComment(ctx context.Context, req CreateSubmissionAuditCommentReq, params CreateSubmissionAuditCommentParams) error
CreateSubmission(ctx context.Context, req *SubmissionCreate) (*ID, error)
// DeleteScript implements deleteScript operation.
//
// Delete the specified script by ID.
@ -196,24 +92,6 @@ type Handler interface {
//
// DELETE /script-policy/{ScriptPolicyID}
DeleteScriptPolicy(ctx context.Context, params DeleteScriptPolicyParams) error
// GetMap implements getMap operation.
//
// Retrieve map with ID.
//
// GET /maps/{MapID}
GetMap(ctx context.Context, params GetMapParams) (*Map, error)
// GetMapfix implements getMapfix operation.
//
// Retrieve map with ID.
//
// GET /mapfixes/{MapfixID}
GetMapfix(ctx context.Context, params GetMapfixParams) (*Mapfix, error)
// GetOperation implements getOperation operation.
//
// Retrieve operation with ID.
//
// GET /operations/{OperationID}
GetOperation(ctx context.Context, params GetOperationParams) (*Operation, error)
// GetScript implements getScript operation.
//
// Get the specified script by ID.
@ -232,24 +110,6 @@ type Handler interface {
//
// GET /submissions/{SubmissionID}
GetSubmission(ctx context.Context, params GetSubmissionParams) (*Submission, error)
// ListMapfixAuditEvents implements listMapfixAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /mapfixes/{MapfixID}/audit-events
ListMapfixAuditEvents(ctx context.Context, params ListMapfixAuditEventsParams) ([]AuditEvent, error)
// ListMapfixes implements listMapfixes operation.
//
// Get list of mapfixes.
//
// GET /mapfixes
ListMapfixes(ctx context.Context, params ListMapfixesParams) (*Mapfixes, error)
// ListMaps implements listMaps operation.
//
// Get list of maps.
//
// GET /maps
ListMaps(ctx context.Context, params ListMapsParams) ([]Map, error)
// ListScriptPolicy implements listScriptPolicy operation.
//
// Get list of script policies.
@ -262,18 +122,12 @@ type Handler interface {
//
// GET /scripts
ListScripts(ctx context.Context, params ListScriptsParams) ([]Script, error)
// ListSubmissionAuditEvents implements listSubmissionAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /submissions/{SubmissionID}/audit-events
ListSubmissionAuditEvents(ctx context.Context, params ListSubmissionAuditEventsParams) ([]AuditEvent, error)
// ListSubmissions implements listSubmissions operation.
//
// Get list of submissions.
//
// GET /submissions
ListSubmissions(ctx context.Context, params ListSubmissionsParams) (*Submissions, error)
ListSubmissions(ctx context.Context, params ListSubmissionsParams) ([]Submission, error)
// ReleaseSubmissions implements releaseSubmissions operation.
//
// Release a set of uploaded maps.
@ -298,24 +152,12 @@ type Handler interface {
//
// GET /session/validate
SessionValidate(ctx context.Context) (bool, error)
// SetMapfixCompleted implements setMapfixCompleted operation.
//
// Called by maptest when a player completes the map.
//
// POST /mapfixes/{MapfixID}/completed
SetMapfixCompleted(ctx context.Context, params SetMapfixCompletedParams) error
// SetSubmissionCompleted implements setSubmissionCompleted operation.
//
// Called by maptest when a player completes the map.
//
// POST /submissions/{SubmissionID}/completed
SetSubmissionCompleted(ctx context.Context, params SetSubmissionCompletedParams) error
// UpdateMapfixModel implements updateMapfixModel operation.
//
// Update model following role restrictions.
//
// POST /mapfixes/{MapfixID}/model
UpdateMapfixModel(ctx context.Context, params UpdateMapfixModelParams) error
// UpdateScript implements updateScript operation.
//
// Update the specified script by ID.

@ -13,106 +13,6 @@ type UnimplementedHandler struct{}
var _ Handler = UnimplementedHandler{}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/reset-validating
func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixReject implements actionMapfixReject operation.
//
// Role Reviewer changes status from Submitted -> Rejected.
//
// POST /mapfixes/{MapfixID}/status/reject
func (UnimplementedHandler) ActionMapfixReject(ctx context.Context, params ActionMapfixRejectParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/request-changes
func (UnimplementedHandler) ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation.
//
// Role Submitter manually resets submitting softlock and changes status from Submitting ->
// UnderConstruction.
//
// POST /mapfixes/{MapfixID}/status/reset-submitting
func (UnimplementedHandler) ActionMapfixResetSubmitting(ctx context.Context, params ActionMapfixResetSubmittingParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixRetryValidate implements actionMapfixRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
//
// POST /mapfixes/{MapfixID}/status/retry-validate
func (UnimplementedHandler) ActionMapfixRetryValidate(ctx context.Context, params ActionMapfixRetryValidateParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixRevoke implements actionMapfixRevoke operation.
//
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
//
// POST /mapfixes/{MapfixID}/status/revoke
func (UnimplementedHandler) ActionMapfixRevoke(ctx context.Context, params ActionMapfixRevokeParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixTriggerSubmit implements actionMapfixTriggerSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit
func (UnimplementedHandler) ActionMapfixTriggerSubmit(ctx context.Context, params ActionMapfixTriggerSubmitParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixTriggerSubmitUnchecked implements actionMapfixTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /mapfixes/{MapfixID}/status/trigger-submit-unchecked
func (UnimplementedHandler) ActionMapfixTriggerSubmitUnchecked(ctx context.Context, params ActionMapfixTriggerSubmitUncheckedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixTriggerUpload implements actionMapfixTriggerUpload operation.
//
// Role Admin changes status from Validated -> Uploading.
//
// POST /mapfixes/{MapfixID}/status/trigger-upload
func (UnimplementedHandler) ActionMapfixTriggerUpload(ctx context.Context, params ActionMapfixTriggerUploadParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixTriggerValidate implements actionMapfixTriggerValidate operation.
//
// Role Reviewer triggers validation and changes status from Submitted -> Validating.
//
// POST /mapfixes/{MapfixID}/status/trigger-validate
func (UnimplementedHandler) ActionMapfixTriggerValidate(ctx context.Context, params ActionMapfixTriggerValidateParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixValidated implements actionMapfixValidated operation.
//
// Role Admin manually resets uploading softlock and changes status from Uploading -> Validated.
//
// POST /mapfixes/{MapfixID}/status/reset-uploading
func (UnimplementedHandler) ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// Role Reviewer manually resets validating softlock and changes status from Validating -> Accepted.
@ -140,16 +40,6 @@ func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context,
return ht.ErrNotImplemented
}
// ActionSubmissionResetSubmitting implements actionSubmissionResetSubmitting operation.
//
// Role Submitter manually resets submitting softlock and changes status from Submitting ->
// UnderConstruction.
//
// POST /submissions/{SubmissionID}/status/reset-submitting
func (UnimplementedHandler) ActionSubmissionResetSubmitting(ctx context.Context, params ActionSubmissionResetSubmittingParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionRetryValidate implements actionSubmissionRetryValidate operation.
//
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
@ -168,21 +58,12 @@ func (UnimplementedHandler) ActionSubmissionRevoke(ctx context.Context, params A
return ht.ErrNotImplemented
}
// ActionSubmissionTriggerSubmit implements actionSubmissionTriggerSubmit operation.
// ActionSubmissionSubmit implements actionSubmissionSubmit operation.
//
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
//
// POST /submissions/{SubmissionID}/status/trigger-submit
func (UnimplementedHandler) ActionSubmissionTriggerSubmit(ctx context.Context, params ActionSubmissionTriggerSubmitParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionTriggerSubmitUnchecked implements actionSubmissionTriggerSubmitUnchecked operation.
//
// Role Reviewer changes status from ChangesRequested -> Submitting.
//
// POST /submissions/{SubmissionID}/status/trigger-submit-unchecked
func (UnimplementedHandler) ActionSubmissionTriggerSubmitUnchecked(ctx context.Context, params ActionSubmissionTriggerSubmitUncheckedParams) error {
// POST /submissions/{SubmissionID}/status/submit
func (UnimplementedHandler) ActionSubmissionSubmit(ctx context.Context, params ActionSubmissionSubmitParams) error {
return ht.ErrNotImplemented
}
@ -213,30 +94,12 @@ func (UnimplementedHandler) ActionSubmissionValidated(ctx context.Context, param
return ht.ErrNotImplemented
}
// CreateMapfix implements createMapfix operation.
//
// Trigger the validator to create a mapfix.
//
// POST /mapfixes
func (UnimplementedHandler) CreateMapfix(ctx context.Context, req *MapfixTriggerCreate) (r *OperationID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateMapfixAuditComment implements createMapfixAuditComment operation.
//
// Post a comment to the audit log.
//
// POST /mapfixes/{MapfixID}/comment
func (UnimplementedHandler) CreateMapfixAuditComment(ctx context.Context, req CreateMapfixAuditCommentReq, params CreateMapfixAuditCommentParams) error {
return ht.ErrNotImplemented
}
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
func (UnimplementedHandler) CreateScript(ctx context.Context, req *ScriptCreate) (r *ScriptID, _ error) {
func (UnimplementedHandler) CreateScript(ctx context.Context, req *ScriptCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented
}
@ -245,37 +108,19 @@ func (UnimplementedHandler) CreateScript(ctx context.Context, req *ScriptCreate)
// Create a new script policy.
//
// POST /script-policy
func (UnimplementedHandler) CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (r *ScriptPolicyID, _ error) {
func (UnimplementedHandler) CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateSubmission implements createSubmission operation.
//
// Trigger the validator to create a new submission.
// Create new submission.
//
// POST /submissions
func (UnimplementedHandler) CreateSubmission(ctx context.Context, req *SubmissionTriggerCreate) (r *OperationID, _ error) {
func (UnimplementedHandler) CreateSubmission(ctx context.Context, req *SubmissionCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateSubmissionAdmin implements createSubmissionAdmin operation.
//
// Trigger the validator to create a new submission.
//
// POST /submissions-admin
func (UnimplementedHandler) CreateSubmissionAdmin(ctx context.Context, req *SubmissionTriggerCreate) (r *OperationID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateSubmissionAuditComment implements createSubmissionAuditComment operation.
//
// Post a comment to the audit log.
//
// POST /submissions/{SubmissionID}/comment
func (UnimplementedHandler) CreateSubmissionAuditComment(ctx context.Context, req CreateSubmissionAuditCommentReq, params CreateSubmissionAuditCommentParams) error {
return ht.ErrNotImplemented
}
// DeleteScript implements deleteScript operation.
//
// Delete the specified script by ID.
@ -294,33 +139,6 @@ func (UnimplementedHandler) DeleteScriptPolicy(ctx context.Context, params Delet
return ht.ErrNotImplemented
}
// GetMap implements getMap operation.
//
// Retrieve map with ID.
//
// GET /maps/{MapID}
func (UnimplementedHandler) GetMap(ctx context.Context, params GetMapParams) (r *Map, _ error) {
return r, ht.ErrNotImplemented
}
// GetMapfix implements getMapfix operation.
//
// Retrieve map with ID.
//
// GET /mapfixes/{MapfixID}
func (UnimplementedHandler) GetMapfix(ctx context.Context, params GetMapfixParams) (r *Mapfix, _ error) {
return r, ht.ErrNotImplemented
}
// GetOperation implements getOperation operation.
//
// Retrieve operation with ID.
//
// GET /operations/{OperationID}
func (UnimplementedHandler) GetOperation(ctx context.Context, params GetOperationParams) (r *Operation, _ error) {
return r, ht.ErrNotImplemented
}
// GetScript implements getScript operation.
//
// Get the specified script by ID.
@ -348,33 +166,6 @@ func (UnimplementedHandler) GetSubmission(ctx context.Context, params GetSubmiss
return r, ht.ErrNotImplemented
}
// ListMapfixAuditEvents implements listMapfixAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /mapfixes/{MapfixID}/audit-events
func (UnimplementedHandler) ListMapfixAuditEvents(ctx context.Context, params ListMapfixAuditEventsParams) (r []AuditEvent, _ error) {
return r, ht.ErrNotImplemented
}
// ListMapfixes implements listMapfixes operation.
//
// Get list of mapfixes.
//
// GET /mapfixes
func (UnimplementedHandler) ListMapfixes(ctx context.Context, params ListMapfixesParams) (r *Mapfixes, _ error) {
return r, ht.ErrNotImplemented
}
// ListMaps implements listMaps operation.
//
// Get list of maps.
//
// GET /maps
func (UnimplementedHandler) ListMaps(ctx context.Context, params ListMapsParams) (r []Map, _ error) {
return r, ht.ErrNotImplemented
}
// ListScriptPolicy implements listScriptPolicy operation.
//
// Get list of script policies.
@ -393,21 +184,12 @@ func (UnimplementedHandler) ListScripts(ctx context.Context, params ListScriptsP
return r, ht.ErrNotImplemented
}
// ListSubmissionAuditEvents implements listSubmissionAuditEvents operation.
//
// Retrieve a list of audit events.
//
// GET /submissions/{SubmissionID}/audit-events
func (UnimplementedHandler) ListSubmissionAuditEvents(ctx context.Context, params ListSubmissionAuditEventsParams) (r []AuditEvent, _ error) {
return r, ht.ErrNotImplemented
}
// ListSubmissions implements listSubmissions operation.
//
// Get list of submissions.
//
// GET /submissions
func (UnimplementedHandler) ListSubmissions(ctx context.Context, params ListSubmissionsParams) (r *Submissions, _ error) {
func (UnimplementedHandler) ListSubmissions(ctx context.Context, params ListSubmissionsParams) (r []Submission, _ error) {
return r, ht.ErrNotImplemented
}
@ -447,15 +229,6 @@ func (UnimplementedHandler) SessionValidate(ctx context.Context) (r bool, _ erro
return r, ht.ErrNotImplemented
}
// SetMapfixCompleted implements setMapfixCompleted operation.
//
// Called by maptest when a player completes the map.
//
// POST /mapfixes/{MapfixID}/completed
func (UnimplementedHandler) SetMapfixCompleted(ctx context.Context, params SetMapfixCompletedParams) error {
return ht.ErrNotImplemented
}
// SetSubmissionCompleted implements setSubmissionCompleted operation.
//
// Called by maptest when a player completes the map.
@ -465,15 +238,6 @@ func (UnimplementedHandler) SetSubmissionCompleted(ctx context.Context, params S
return ht.ErrNotImplemented
}
// UpdateMapfixModel implements updateMapfixModel operation.
//
// Update model following role restrictions.
//
// POST /mapfixes/{MapfixID}/model
func (UnimplementedHandler) UpdateMapfixModel(ctx context.Context, params UpdateMapfixModelParams) error {
return ht.ErrNotImplemented
}
// UpdateScript implements updateScript operation.
//
// Update the specified script by ID.

File diff suppressed because it is too large Load Diff

@ -6,7 +6,6 @@ import (
"git.itzana.me/strafesnet/go-grpc/auth"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/go-grpc/users"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
@ -126,8 +125,7 @@ func serve(ctx *cli.Context) error {
svc := &service.Service{
DB: db,
Nats: js,
Maps: maps.NewMapsServiceClient(conn),
Users: users.NewUsersServiceClient(conn),
Client: maps.NewMapsServiceClient(conn),
}
conn, err = grpc.Dial(ctx.String("auth-rpc-host"), grpc.WithTransportCredentials(insecure.NewCredentials()))

@ -3,8 +3,6 @@ package datastore
import (
"context"
"errors"
"time"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
@ -24,42 +22,11 @@ const (
)
type Datastore interface {
AuditEvents() AuditEvents
Mapfixes() Mapfixes
Operations() Operations
Submissions() Submissions
Scripts() Scripts
ScriptPolicy() ScriptPolicy
}
type AuditEvents interface {
Get(ctx context.Context, id int64) (model.AuditEvent, error)
Create(ctx context.Context, smap model.AuditEvent) (model.AuditEvent, error)
Update(ctx context.Context, id int64, values OptionalMap) error
Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page) ([]model.AuditEvent, error)
}
type Mapfixes interface {
Get(ctx context.Context, id int64) (model.Mapfix, error)
GetList(ctx context.Context, id []int64) ([]model.Mapfix, error)
Create(ctx context.Context, smap model.Mapfix) (model.Mapfix, error)
Update(ctx context.Context, id int64, values OptionalMap) error
IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.MapfixStatus, values OptionalMap) error
IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.MapfixStatus, values OptionalMap) (model.Mapfix, error)
Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) ([]model.Mapfix, error)
ListWithTotal(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) (int64, []model.Mapfix, error)
}
type Operations interface {
Get(ctx context.Context, id int32) (model.Operation, error)
Create(ctx context.Context, smap model.Operation) (model.Operation, error)
Update(ctx context.Context, id int32, values OptionalMap) error
Delete(ctx context.Context, id int32) error
CountSince(ctx context.Context, owner int64, since time.Time) (int64, error)
}
type Submissions interface {
Get(ctx context.Context, id int64) (model.Submission, error)
GetList(ctx context.Context, id []int64) ([]model.Submission, error)
@ -69,7 +36,6 @@ type Submissions interface {
IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.SubmissionStatus, values OptionalMap) (model.Submission, error)
Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) ([]model.Submission, error)
ListWithTotal(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) (int64, []model.Submission, error)
}
type Scripts interface {

@ -1,64 +0,0 @@
package gormstore
import (
"context"
"errors"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"gorm.io/gorm"
)
type AuditEvents struct {
db *gorm.DB
}
func (env *AuditEvents) Get(ctx context.Context, id int64) (model.AuditEvent, error) {
var mdl model.AuditEvent
if err := env.db.First(&mdl, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return mdl, datastore.ErrNotExist
}
return mdl, err
}
return mdl, nil
}
func (env *AuditEvents) Create(ctx context.Context, smap model.AuditEvent) (model.AuditEvent, error) {
if err := env.db.Create(&smap).Error; err != nil {
return smap, err
}
return smap, nil
}
func (env *AuditEvents) Update(ctx context.Context, id int64, values datastore.OptionalMap) error {
if err := env.db.Model(&model.AuditEvent{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *AuditEvents) Delete(ctx context.Context, id int64) error {
if err := env.db.Delete(&model.AuditEvent{}, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *AuditEvents) List(ctx context.Context, filters datastore.OptionalMap, page model.Page) ([]model.AuditEvent, error) {
var events []model.AuditEvent
if err := env.db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&events).Error; err != nil {
return nil, err
}
return events, nil
}

@ -31,9 +31,6 @@ func New(ctx *cli.Context) (datastore.Datastore, error) {
if ctx.Bool("migrate") {
if err := db.AutoMigrate(
&model.AuditEvent{},
&model.Mapfix{},
&model.Operation{},
&model.Submission{},
&model.Script{},
&model.ScriptPolicy{},

@ -9,18 +9,6 @@ type Gormstore struct {
db *gorm.DB
}
func (g Gormstore) AuditEvents() datastore.AuditEvents {
return &AuditEvents{db: g.db}
}
func (g Gormstore) Mapfixes() datastore.Mapfixes {
return &Mapfixes{db: g.db}
}
func (g Gormstore) Operations() datastore.Operations {
return &Operations{db: g.db}
}
func (g Gormstore) Submissions() datastore.Submissions {
return &Submissions{db: g.db}
}

@ -1,148 +0,0 @@
package gormstore
import (
"context"
"errors"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type Mapfixes struct {
db *gorm.DB
}
func (env *Mapfixes) Get(ctx context.Context, id int64) (model.Mapfix, error) {
var mapfix model.Mapfix
if err := env.db.First(&mapfix, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return mapfix, datastore.ErrNotExist
}
return mapfix, err
}
return mapfix, nil
}
func (env *Mapfixes) GetList(ctx context.Context, id []int64) ([]model.Mapfix, error) {
var mapList []model.Mapfix
if err := env.db.Find(&mapList, "id IN ?", id).Error; err != nil {
return mapList, err
}
return mapList, nil
}
func (env *Mapfixes) Create(ctx context.Context, smap model.Mapfix) (model.Mapfix, error) {
if err := env.db.Create(&smap).Error; err != nil {
return smap, err
}
return smap, nil
}
func (env *Mapfixes) Update(ctx context.Context, id int64, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Mapfix{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
// the update can only occur if the status matches one of the provided values.
func (env *Mapfixes) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.MapfixStatus, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Mapfix{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
// the update can only occur if the status matches one of the provided values.
// returns the updated value
func (env *Mapfixes) IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.MapfixStatus, values datastore.OptionalMap) (model.Mapfix, error) {
var mapfix model.Mapfix
result := env.db.Model(&mapfix).
Clauses(clause.Returning{}).
Where("id = ?", id).
Where("status_id IN ?", statuses).
Updates(values.Map())
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return mapfix, datastore.ErrNotExist
}
return mapfix, result.Error
}
if result.RowsAffected == 0 {
return mapfix, datastore.ErroNoRowsAffected
}
return mapfix, nil
}
func (env *Mapfixes) Delete(ctx context.Context, id int64) error {
if err := env.db.Delete(&model.Mapfix{}, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *Mapfixes) List(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort datastore.ListSort) ([]model.Mapfix, error) {
var maps []model.Mapfix
db := env.db
switch sort {
case datastore.ListSortDisabled:
// No sort
break
case datastore.ListSortDisplayNameAscending:
db=db.Order("display_name ASC")
break
case datastore.ListSortDisplayNameDescending:
db=db.Order("display_name DESC")
break
case datastore.ListSortDateAscending:
db=db.Order("created_at ASC")
break
case datastore.ListSortDateDescending:
db=db.Order("created_at DESC")
break
default:
return nil, datastore.ErrInvalidListSort
}
if err := db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&maps).Error; err != nil {
return nil, err
}
return maps, nil
}
func (env *Mapfixes) ListWithTotal(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort datastore.ListSort) (int64, []model.Mapfix, error) {
// grab page items
maps, err := env.List(ctx, filters, page, sort)
if err != nil{
return 0, nil, err
}
// count total with filters
var total int64
if err := env.db.Model(&model.Mapfix{}).Where(filters.Map()).Count(&total).Error; err != nil {
return 0, nil, err
}
return total, maps, nil
}

@ -1,65 +0,0 @@
package gormstore
import (
"context"
"errors"
"time"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"gorm.io/gorm"
)
type Operations struct {
db *gorm.DB
}
func (env *Operations) Get(ctx context.Context, id int32) (model.Operation, error) {
var operation model.Operation
if err := env.db.First(&operation, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return operation, datastore.ErrNotExist
}
return operation, err
}
return operation, nil
}
func (env *Operations) Create(ctx context.Context, smap model.Operation) (model.Operation, error) {
if err := env.db.Create(&smap).Error; err != nil {
return smap, err
}
return smap, nil
}
func (env *Operations) Update(ctx context.Context, id int32, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Operation{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *Operations) Delete(ctx context.Context, id int32) error {
if err := env.db.Delete(&model.Operation{}, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *Operations) CountSince(ctx context.Context, owner int64, since time.Time) (int64, error) {
var count int64
if err := env.db.Model(&model.Operation{}).Where("owner = ? AND created_at > ?",owner,since).Count(&count).Error; err != nil {
return count, err
}
return count, nil
}

@ -130,19 +130,3 @@ func (env *Submissions) List(ctx context.Context, filters datastore.OptionalMap,
return maps, nil
}
func (env *Submissions) ListWithTotal(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort datastore.ListSort) (int64, []model.Submission, error) {
// grab page items
maps, err := env.List(ctx, filters, page, sort)
if err != nil{
return 0, nil, err
}
// count total with filters
var total int64
if err := env.db.Model(&model.Submission{}).Where(filters.Map()).Count(&total).Error; err != nil {
return 0, nil, err
}
return total, maps, nil
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -126,284 +126,51 @@ func (s *Error) UnmarshalJSON(data []byte) error {
}
// Encode implements json.Marshaler.
func (s *MapfixCreate) Encode(e *jx.Encoder) {
func (s *ID) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *MapfixCreate) encodeFields(e *jx.Encoder) {
func (s *ID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("OperationID")
e.Int32(s.OperationID)
}
{
e.FieldStart("AssetOwner")
e.Int64(s.AssetOwner)
}
{
e.FieldStart("DisplayName")
e.Str(s.DisplayName)
}
{
e.FieldStart("Creator")
e.Str(s.Creator)
}
{
e.FieldStart("GameID")
e.Int32(s.GameID)
}
{
e.FieldStart("AssetID")
e.Int64(s.AssetID)
}
{
e.FieldStart("AssetVersion")
e.Int64(s.AssetVersion)
}
{
e.FieldStart("TargetAssetID")
e.Int64(s.TargetAssetID)
}
{
e.FieldStart("Description")
e.Str(s.Description)
e.FieldStart("ID")
e.Int64(s.ID)
}
}
var jsonFieldsNameOfMapfixCreate = [9]string{
0: "OperationID",
1: "AssetOwner",
2: "DisplayName",
3: "Creator",
4: "GameID",
5: "AssetID",
6: "AssetVersion",
7: "TargetAssetID",
8: "Description",
var jsonFieldsNameOfID = [1]string{
0: "ID",
}
// Decode decodes MapfixCreate from json.
func (s *MapfixCreate) Decode(d *jx.Decoder) error {
// Decode decodes ID from json.
func (s *ID) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode MapfixCreate to nil")
}
var requiredBitSet [2]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "OperationID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int32()
s.OperationID = int32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"OperationID\"")
}
case "AssetOwner":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.Int64()
s.AssetOwner = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"AssetOwner\"")
}
case "DisplayName":
requiredBitSet[0] |= 1 << 2
if err := func() error {
v, err := d.Str()
s.DisplayName = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"DisplayName\"")
}
case "Creator":
requiredBitSet[0] |= 1 << 3
if err := func() error {
v, err := d.Str()
s.Creator = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Creator\"")
}
case "GameID":
requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.Int32()
s.GameID = int32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"GameID\"")
}
case "AssetID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.Int64()
s.AssetID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"AssetID\"")
}
case "AssetVersion":
requiredBitSet[0] |= 1 << 6
if err := func() error {
v, err := d.Int64()
s.AssetVersion = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"AssetVersion\"")
}
case "TargetAssetID":
requiredBitSet[0] |= 1 << 7
if err := func() error {
v, err := d.Int64()
s.TargetAssetID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"TargetAssetID\"")
}
case "Description":
requiredBitSet[1] |= 1 << 0
if err := func() error {
v, err := d.Str()
s.Description = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Description\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode MapfixCreate")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [2]uint8{
0b11111111,
0b00000001,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfMapfixCreate) {
name = jsonFieldsNameOfMapfixCreate[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *MapfixCreate) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *MapfixCreate) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *MapfixID) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *MapfixID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("MapfixID")
e.Int64(s.MapfixID)
}
}
var jsonFieldsNameOfMapfixID = [1]string{
0: "MapfixID",
}
// Decode decodes MapfixID from json.
func (s *MapfixID) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode MapfixID to nil")
return errors.New("invalid: unable to decode ID to nil")
}
var requiredBitSet [1]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "MapfixID":
case "ID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.MapfixID = int64(v)
s.ID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"MapfixID\"")
return errors.Wrap(err, "decode field \"ID\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode MapfixID")
return errors.Wrap(err, "decode ID")
}
// Validate required fields.
var failures []validate.FieldError
@ -420,8 +187,8 @@ func (s *MapfixID) Decode(d *jx.Decoder) error {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfMapfixID) {
name = jsonFieldsNameOfMapfixID[fieldIdx]
if fieldIdx < len(jsonFieldsNameOfID) {
name = jsonFieldsNameOfID[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
@ -442,14 +209,14 @@ func (s *MapfixID) Decode(d *jx.Decoder) error {
}
// MarshalJSON implements stdjson.Marshaler.
func (s *MapfixID) MarshalJSON() ([]byte, error) {
func (s *ID) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *MapfixID) UnmarshalJSON(data []byte) error {
func (s *ID) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
@ -817,102 +584,6 @@ func (s *ScriptCreate) UnmarshalJSON(data []byte) error {
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *ScriptID) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *ScriptID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("ScriptID")
e.Int64(s.ScriptID)
}
}
var jsonFieldsNameOfScriptID = [1]string{
0: "ScriptID",
}
// Decode decodes ScriptID from json.
func (s *ScriptID) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode ScriptID to nil")
}
var requiredBitSet [1]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "ScriptID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.ScriptID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ScriptID\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode ScriptID")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000001,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfScriptID) {
name = jsonFieldsNameOfScriptID[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *ScriptID) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *ScriptID) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *ScriptPolicy) Encode(e *jx.Encoder) {
e.ObjStart()
@ -1189,428 +860,3 @@ func (s *ScriptPolicyCreate) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *ScriptPolicyID) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *ScriptPolicyID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("ScriptPolicyID")
e.Int64(s.ScriptPolicyID)
}
}
var jsonFieldsNameOfScriptPolicyID = [1]string{
0: "ScriptPolicyID",
}
// Decode decodes ScriptPolicyID from json.
func (s *ScriptPolicyID) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode ScriptPolicyID to nil")
}
var requiredBitSet [1]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "ScriptPolicyID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.ScriptPolicyID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ScriptPolicyID\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode ScriptPolicyID")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000001,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfScriptPolicyID) {
name = jsonFieldsNameOfScriptPolicyID[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *ScriptPolicyID) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *ScriptPolicyID) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *SubmissionCreate) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *SubmissionCreate) encodeFields(e *jx.Encoder) {
{
e.FieldStart("OperationID")
e.Int32(s.OperationID)
}
{
e.FieldStart("AssetOwner")
e.Int64(s.AssetOwner)
}
{
e.FieldStart("DisplayName")
e.Str(s.DisplayName)
}
{
e.FieldStart("Creator")
e.Str(s.Creator)
}
{
e.FieldStart("GameID")
e.Int32(s.GameID)
}
{
e.FieldStart("AssetID")
e.Int64(s.AssetID)
}
{
e.FieldStart("AssetVersion")
e.Int64(s.AssetVersion)
}
{
e.FieldStart("Status")
e.UInt32(s.Status)
}
{
e.FieldStart("Roles")
e.UInt32(s.Roles)
}
}
var jsonFieldsNameOfSubmissionCreate = [9]string{
0: "OperationID",
1: "AssetOwner",
2: "DisplayName",
3: "Creator",
4: "GameID",
5: "AssetID",
6: "AssetVersion",
7: "Status",
8: "Roles",
}
// Decode decodes SubmissionCreate from json.
func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode SubmissionCreate to nil")
}
var requiredBitSet [2]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "OperationID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int32()
s.OperationID = int32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"OperationID\"")
}
case "AssetOwner":
requiredBitSet[0] |= 1 << 1
if err := func() error {
v, err := d.Int64()
s.AssetOwner = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"AssetOwner\"")
}
case "DisplayName":
requiredBitSet[0] |= 1 << 2
if err := func() error {
v, err := d.Str()
s.DisplayName = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"DisplayName\"")
}
case "Creator":
requiredBitSet[0] |= 1 << 3
if err := func() error {
v, err := d.Str()
s.Creator = string(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Creator\"")
}
case "GameID":
requiredBitSet[0] |= 1 << 4
if err := func() error {
v, err := d.Int32()
s.GameID = int32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"GameID\"")
}
case "AssetID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.Int64()
s.AssetID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"AssetID\"")
}
case "AssetVersion":
requiredBitSet[0] |= 1 << 6
if err := func() error {
v, err := d.Int64()
s.AssetVersion = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"AssetVersion\"")
}
case "Status":
requiredBitSet[0] |= 1 << 7
if err := func() error {
v, err := d.UInt32()
s.Status = uint32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Status\"")
}
case "Roles":
requiredBitSet[1] |= 1 << 0
if err := func() error {
v, err := d.UInt32()
s.Roles = uint32(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"Roles\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode SubmissionCreate")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [2]uint8{
0b11111111,
0b00000001,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfSubmissionCreate) {
name = jsonFieldsNameOfSubmissionCreate[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *SubmissionCreate) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *SubmissionCreate) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}
// Encode implements json.Marshaler.
func (s *SubmissionID) Encode(e *jx.Encoder) {
e.ObjStart()
s.encodeFields(e)
e.ObjEnd()
}
// encodeFields encodes fields.
func (s *SubmissionID) encodeFields(e *jx.Encoder) {
{
e.FieldStart("SubmissionID")
e.Int64(s.SubmissionID)
}
}
var jsonFieldsNameOfSubmissionID = [1]string{
0: "SubmissionID",
}
// Decode decodes SubmissionID from json.
func (s *SubmissionID) Decode(d *jx.Decoder) error {
if s == nil {
return errors.New("invalid: unable to decode SubmissionID to nil")
}
var requiredBitSet [1]uint8
if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {
switch string(k) {
case "SubmissionID":
requiredBitSet[0] |= 1 << 0
if err := func() error {
v, err := d.Int64()
s.SubmissionID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionID\"")
}
default:
return d.Skip()
}
return nil
}); err != nil {
return errors.Wrap(err, "decode SubmissionID")
}
// Validate required fields.
var failures []validate.FieldError
for i, mask := range [1]uint8{
0b00000001,
} {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR.
//
// If XOR result is not zero, result is not equal to expected, so some fields are missed.
// Bits of fields which would be set are actually bits of missed fields.
missed := bits.OnesCount8(result)
for bitN := 0; bitN < missed; bitN++ {
bitIdx := bits.TrailingZeros8(result)
fieldIdx := i*8 + bitIdx
var name string
if fieldIdx < len(jsonFieldsNameOfSubmissionID) {
name = jsonFieldsNameOfSubmissionID[fieldIdx]
} else {
name = strconv.Itoa(fieldIdx)
}
failures = append(failures, validate.FieldError{
Name: name,
Error: validate.ErrFieldRequired,
})
// Reset bit.
result &^= 1 << bitIdx
}
}
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
// MarshalJSON implements stdjson.Marshaler.
func (s *SubmissionID) MarshalJSON() ([]byte, error) {
e := jx.Encoder{}
s.Encode(&e)
return e.Bytes(), nil
}
// UnmarshalJSON implements stdjson.Unmarshaler.
func (s *SubmissionID) UnmarshalJSON(data []byte) error {
d := jx.DecodeBytes(data)
return s.Decode(d)
}

@ -6,26 +6,13 @@ package api
type OperationName = string
const (
ActionMapfixAcceptedOperation OperationName = "ActionMapfixAccepted"
ActionMapfixRequestChangesOperation OperationName = "ActionMapfixRequestChanges"
ActionMapfixSubmittedOperation OperationName = "ActionMapfixSubmitted"
ActionMapfixUploadedOperation OperationName = "ActionMapfixUploaded"
ActionMapfixValidatedOperation OperationName = "ActionMapfixValidated"
ActionOperationFailedOperation OperationName = "ActionOperationFailed"
ActionSubmissionAcceptedOperation OperationName = "ActionSubmissionAccepted"
ActionSubmissionRequestChangesOperation OperationName = "ActionSubmissionRequestChanges"
ActionSubmissionSubmittedOperation OperationName = "ActionSubmissionSubmitted"
ActionSubmissionUploadedOperation OperationName = "ActionSubmissionUploaded"
ActionSubmissionValidatedOperation OperationName = "ActionSubmissionValidated"
CreateMapfixOperation OperationName = "CreateMapfix"
CreateMapfixAuditErrorOperation OperationName = "CreateMapfixAuditError"
CreateScriptOperation OperationName = "CreateScript"
CreateScriptPolicyOperation OperationName = "CreateScriptPolicy"
CreateSubmissionOperation OperationName = "CreateSubmission"
CreateSubmissionAuditErrorOperation OperationName = "CreateSubmissionAuditError"
GetScriptOperation OperationName = "GetScript"
ListScriptPolicyOperation OperationName = "ListScriptPolicy"
ListScriptsOperation OperationName = "ListScripts"
UpdateMapfixValidatedModelOperation OperationName = "UpdateMapfixValidatedModel"
UpdateSubmissionValidatedModelOperation OperationName = "UpdateSubmissionValidatedModel"
)

File diff suppressed because it is too large Load Diff

@ -9,82 +9,12 @@ import (
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"go.uber.org/multierr"
"github.com/ogen-go/ogen/ogenerrors"
"github.com/ogen-go/ogen/validate"
)
func (s *Server) decodeCreateMapfixRequest(r *http.Request) (
req *MapfixCreate,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request MapfixCreate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateScriptRequest(r *http.Request) (
req *ScriptCreate,
close func() error,
@ -96,13 +26,13 @@ func (s *Server) decodeCreateScriptRequest(r *http.Request) (
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
rerr = multierr.Append(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
@ -167,13 +97,13 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
merr = multierr.Append(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
rerr = multierr.Append(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
@ -213,85 +143,6 @@ func (s *Server) decodeCreateScriptPolicyRequest(r *http.Request) (
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)
}
}
func (s *Server) decodeCreateSubmissionRequest(r *http.Request) (
req *SubmissionCreate,
close func() error,
rerr error,
) {
var closers []func() error
close = func() error {
var merr error
// Close in reverse order, to match defer behavior.
for i := len(closers) - 1; i >= 0; i-- {
c := closers[i]
merr = errors.Join(merr, c())
}
return merr
}
defer func() {
if rerr != nil {
rerr = errors.Join(rerr, close())
}
}()
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return req, close, errors.Wrap(err, "parse media type")
}
switch {
case ct == "application/json":
if r.ContentLength == 0 {
return req, close, validate.ErrBodyRequired
}
buf, err := io.ReadAll(r.Body)
if err != nil {
return req, close, err
}
if len(buf) == 0 {
return req, close, validate.ErrBodyRequired
}
d := jx.DecodeBytes(buf)
var request SubmissionCreate
if err := func() error {
if err := request.Decode(d); err != nil {
return err
}
if err := d.Skip(); err != io.EOF {
return errors.New("unexpected trailing data")
}
return nil
}(); err != nil {
err = &ogenerrors.DecodeBodyError{
ContentType: ct,
Body: buf,
Err: err,
}
return req, close, err
}
if err := func() error {
if err := request.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
return req, close, errors.Wrap(err, "validate")
}
return &request, close, nil
default:
return req, close, validate.InvalidContentType(ct)

@ -11,20 +11,6 @@ import (
ht "github.com/ogen-go/ogen/http"
)
func encodeCreateMapfixRequest(
req *MapfixCreate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateScriptRequest(
req *ScriptCreate,
r *http.Request,
@ -52,17 +38,3 @@ func encodeCreateScriptPolicyRequest(
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}
func encodeCreateSubmissionRequest(
req *SubmissionCreate,
r *http.Request,
) error {
const contentType = "application/json"
e := new(jx.Encoder)
{
req.Encode(e)
}
encoded := e.Bytes()
ht.SetBody(r, bytes.NewReader(encoded), contentType)
return nil
}

File diff suppressed because it is too large Load Diff

@ -13,48 +13,6 @@ import (
ht "github.com/ogen-go/ogen/http"
)
func encodeActionMapfixAcceptedResponse(response *ActionMapfixAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixRequestChangesResponse(response *ActionMapfixRequestChangesNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixSubmittedResponse(response *ActionMapfixSubmittedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixUploadedResponse(response *ActionMapfixUploadedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionMapfixValidatedResponse(response *ActionMapfixValidatedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionOperationFailedResponse(response *ActionOperationFailedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -62,20 +20,6 @@ func encodeActionSubmissionAcceptedResponse(response *ActionSubmissionAcceptedNo
return nil
}
func encodeActionSubmissionRequestChangesResponse(response *ActionSubmissionRequestChangesNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionSubmittedResponse(response *ActionSubmissionSubmittedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeActionSubmissionUploadedResponse(response *ActionSubmissionUploadedNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
@ -90,7 +34,7 @@ func encodeActionSubmissionValidatedResponse(response *ActionSubmissionValidated
return nil
}
func encodeCreateMapfixResponse(response *MapfixID, w http.ResponseWriter, span trace.Span) error {
func encodeCreateScriptResponse(response *ID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
@ -104,14 +48,7 @@ func encodeCreateMapfixResponse(response *MapfixID, w http.ResponseWriter, span
return nil
}
func encodeCreateMapfixAuditErrorResponse(response *CreateMapfixAuditErrorNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeCreateScriptResponse(response *ScriptID, w http.ResponseWriter, span trace.Span) error {
func encodeCreateScriptPolicyResponse(response *ID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
@ -125,41 +62,6 @@ func encodeCreateScriptResponse(response *ScriptID, w http.ResponseWriter, span
return nil
}
func encodeCreateScriptPolicyResponse(response *ScriptPolicyID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeCreateSubmissionResponse(response *SubmissionID, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(201)
span.SetStatus(codes.Ok, http.StatusText(201))
e := new(jx.Encoder)
response.Encode(e)
if _, err := e.WriteTo(w); err != nil {
return errors.Wrap(err, "write")
}
return nil
}
func encodeCreateSubmissionAuditErrorResponse(response *CreateSubmissionAuditErrorNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeGetScriptResponse(response *Script, w http.ResponseWriter, span trace.Span) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
@ -210,13 +112,6 @@ func encodeListScriptsResponse(response []Script, w http.ResponseWriter, span tr
return nil
}
func encodeUpdateMapfixValidatedModelResponse(response *UpdateMapfixValidatedModelNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))
return nil
}
func encodeUpdateSubmissionValidatedModelResponse(response *UpdateSubmissionValidatedModelNoContent, w http.ResponseWriter, span trace.Span) error {
w.WriteHeader(204)
span.SetStatus(codes.Ok, http.StatusText(204))

File diff suppressed because it is too large Load Diff

@ -10,45 +10,15 @@ func (s *ErrorStatusCode) Error() string {
return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response)
}
// ActionMapfixAcceptedNoContent is response for ActionMapfixAccepted operation.
type ActionMapfixAcceptedNoContent struct{}
// ActionMapfixRequestChangesNoContent is response for ActionMapfixRequestChanges operation.
type ActionMapfixRequestChangesNoContent struct{}
// ActionMapfixSubmittedNoContent is response for ActionMapfixSubmitted operation.
type ActionMapfixSubmittedNoContent struct{}
// ActionMapfixUploadedNoContent is response for ActionMapfixUploaded operation.
type ActionMapfixUploadedNoContent struct{}
// ActionMapfixValidatedNoContent is response for ActionMapfixValidated operation.
type ActionMapfixValidatedNoContent struct{}
// ActionOperationFailedNoContent is response for ActionOperationFailed operation.
type ActionOperationFailedNoContent struct{}
// ActionSubmissionAcceptedNoContent is response for ActionSubmissionAccepted operation.
type ActionSubmissionAcceptedNoContent struct{}
// ActionSubmissionRequestChangesNoContent is response for ActionSubmissionRequestChanges operation.
type ActionSubmissionRequestChangesNoContent struct{}
// ActionSubmissionSubmittedNoContent is response for ActionSubmissionSubmitted operation.
type ActionSubmissionSubmittedNoContent struct{}
// ActionSubmissionUploadedNoContent is response for ActionSubmissionUploaded operation.
type ActionSubmissionUploadedNoContent struct{}
// ActionSubmissionValidatedNoContent is response for ActionSubmissionValidated operation.
type ActionSubmissionValidatedNoContent struct{}
// CreateMapfixAuditErrorNoContent is response for CreateMapfixAuditError operation.
type CreateMapfixAuditErrorNoContent struct{}
// CreateSubmissionAuditErrorNoContent is response for CreateSubmissionAuditError operation.
type CreateSubmissionAuditErrorNoContent struct{}
// Represents error object.
// Ref: #/components/schemas/Error
type Error struct {
@ -102,122 +72,19 @@ func (s *ErrorStatusCode) SetResponse(val Error) {
s.Response = val
}
// Ref: #/components/schemas/MapfixCreate
type MapfixCreate struct {
OperationID int32 `json:"OperationID"`
AssetOwner int64 `json:"AssetOwner"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
TargetAssetID int64 `json:"TargetAssetID"`
Description string `json:"Description"`
// Ref: #/components/schemas/Id
type ID struct {
ID int64 `json:"ID"`
}
// GetOperationID returns the value of OperationID.
func (s *MapfixCreate) GetOperationID() int32 {
return s.OperationID
// GetID returns the value of ID.
func (s *ID) GetID() int64 {
return s.ID
}
// GetAssetOwner returns the value of AssetOwner.
func (s *MapfixCreate) GetAssetOwner() int64 {
return s.AssetOwner
}
// GetDisplayName returns the value of DisplayName.
func (s *MapfixCreate) GetDisplayName() string {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *MapfixCreate) GetCreator() string {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *MapfixCreate) GetGameID() int32 {
return s.GameID
}
// GetAssetID returns the value of AssetID.
func (s *MapfixCreate) GetAssetID() int64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *MapfixCreate) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *MapfixCreate) GetTargetAssetID() int64 {
return s.TargetAssetID
}
// GetDescription returns the value of Description.
func (s *MapfixCreate) GetDescription() string {
return s.Description
}
// SetOperationID sets the value of OperationID.
func (s *MapfixCreate) SetOperationID(val int32) {
s.OperationID = val
}
// SetAssetOwner sets the value of AssetOwner.
func (s *MapfixCreate) SetAssetOwner(val int64) {
s.AssetOwner = val
}
// SetDisplayName sets the value of DisplayName.
func (s *MapfixCreate) SetDisplayName(val string) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *MapfixCreate) SetCreator(val string) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *MapfixCreate) SetGameID(val int32) {
s.GameID = val
}
// SetAssetID sets the value of AssetID.
func (s *MapfixCreate) SetAssetID(val int64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *MapfixCreate) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *MapfixCreate) SetTargetAssetID(val int64) {
s.TargetAssetID = val
}
// SetDescription sets the value of Description.
func (s *MapfixCreate) SetDescription(val string) {
s.Description = val
}
// Ref: #/components/schemas/MapfixID
type MapfixID struct {
MapfixID int64 `json:"MapfixID"`
}
// GetMapfixID returns the value of MapfixID.
func (s *MapfixID) GetMapfixID() int64 {
return s.MapfixID
}
// SetMapfixID sets the value of MapfixID.
func (s *MapfixID) SetMapfixID(val int64) {
s.MapfixID = val
// SetID sets the value of ID.
func (s *ID) SetID(val int64) {
s.ID = val
}
// NewOptInt32 returns new OptInt32 with value set to v.
@ -476,21 +343,6 @@ func (s *ScriptCreate) SetResourceID(val OptInt64) {
s.ResourceID = val
}
// Ref: #/components/schemas/ScriptID
type ScriptID struct {
ScriptID int64 `json:"ScriptID"`
}
// GetScriptID returns the value of ScriptID.
func (s *ScriptID) GetScriptID() int64 {
return s.ScriptID
}
// SetScriptID sets the value of ScriptID.
func (s *ScriptID) SetScriptID(val int64) {
s.ScriptID = val
}
// Ref: #/components/schemas/ScriptPolicy
type ScriptPolicy struct {
ID int64 `json:"ID"`
@ -576,141 +428,5 @@ func (s *ScriptPolicyCreate) SetPolicy(val int32) {
s.Policy = val
}
// Ref: #/components/schemas/ScriptPolicyID
type ScriptPolicyID struct {
ScriptPolicyID int64 `json:"ScriptPolicyID"`
}
// GetScriptPolicyID returns the value of ScriptPolicyID.
func (s *ScriptPolicyID) GetScriptPolicyID() int64 {
return s.ScriptPolicyID
}
// SetScriptPolicyID sets the value of ScriptPolicyID.
func (s *ScriptPolicyID) SetScriptPolicyID(val int64) {
s.ScriptPolicyID = val
}
// Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct {
OperationID int32 `json:"OperationID"`
AssetOwner int64 `json:"AssetOwner"`
DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"`
GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"`
Status uint32 `json:"Status"`
Roles uint32 `json:"Roles"`
}
// GetOperationID returns the value of OperationID.
func (s *SubmissionCreate) GetOperationID() int32 {
return s.OperationID
}
// GetAssetOwner returns the value of AssetOwner.
func (s *SubmissionCreate) GetAssetOwner() int64 {
return s.AssetOwner
}
// GetDisplayName returns the value of DisplayName.
func (s *SubmissionCreate) GetDisplayName() string {
return s.DisplayName
}
// GetCreator returns the value of Creator.
func (s *SubmissionCreate) GetCreator() string {
return s.Creator
}
// GetGameID returns the value of GameID.
func (s *SubmissionCreate) GetGameID() int32 {
return s.GameID
}
// GetAssetID returns the value of AssetID.
func (s *SubmissionCreate) GetAssetID() int64 {
return s.AssetID
}
// GetAssetVersion returns the value of AssetVersion.
func (s *SubmissionCreate) GetAssetVersion() int64 {
return s.AssetVersion
}
// GetStatus returns the value of Status.
func (s *SubmissionCreate) GetStatus() uint32 {
return s.Status
}
// GetRoles returns the value of Roles.
func (s *SubmissionCreate) GetRoles() uint32 {
return s.Roles
}
// SetOperationID sets the value of OperationID.
func (s *SubmissionCreate) SetOperationID(val int32) {
s.OperationID = val
}
// SetAssetOwner sets the value of AssetOwner.
func (s *SubmissionCreate) SetAssetOwner(val int64) {
s.AssetOwner = val
}
// SetDisplayName sets the value of DisplayName.
func (s *SubmissionCreate) SetDisplayName(val string) {
s.DisplayName = val
}
// SetCreator sets the value of Creator.
func (s *SubmissionCreate) SetCreator(val string) {
s.Creator = val
}
// SetGameID sets the value of GameID.
func (s *SubmissionCreate) SetGameID(val int32) {
s.GameID = val
}
// SetAssetID sets the value of AssetID.
func (s *SubmissionCreate) SetAssetID(val int64) {
s.AssetID = val
}
// SetAssetVersion sets the value of AssetVersion.
func (s *SubmissionCreate) SetAssetVersion(val int64) {
s.AssetVersion = val
}
// SetStatus sets the value of Status.
func (s *SubmissionCreate) SetStatus(val uint32) {
s.Status = val
}
// SetRoles sets the value of Roles.
func (s *SubmissionCreate) SetRoles(val uint32) {
s.Roles = val
}
// Ref: #/components/schemas/SubmissionID
type SubmissionID struct {
SubmissionID int64 `json:"SubmissionID"`
}
// GetSubmissionID returns the value of SubmissionID.
func (s *SubmissionID) GetSubmissionID() int64 {
return s.SubmissionID
}
// SetSubmissionID sets the value of SubmissionID.
func (s *SubmissionID) SetSubmissionID(val int64) {
s.SubmissionID = val
}
// UpdateMapfixValidatedModelNoContent is response for UpdateMapfixValidatedModel operation.
type UpdateMapfixValidatedModelNoContent struct{}
// UpdateSubmissionValidatedModelNoContent is response for UpdateSubmissionValidatedModel operation.
type UpdateSubmissionValidatedModelNoContent struct{}

@ -8,60 +8,12 @@ import (
// Handler handles operations described by OpenAPI v3 specification.
type Handler interface {
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/validator-request-changes
ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error
// ActionMapfixSubmitted implements actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
ActionMapfixUploaded(ctx context.Context, params ActionMapfixUploadedParams) error
// ActionMapfixValidated implements actionMapfixValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error
// ActionOperationFailed implements actionOperationFailed operation.
//
// (Internal endpoint) Fail an operation and write a StatusMessage.
//
// POST /operations/{OperationID}/status/operation-failed
ActionOperationFailed(ctx context.Context, params ActionOperationFailedParams) error
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /submissions/{SubmissionID}/status/validator-failed
ActionSubmissionAccepted(ctx context.Context, params ActionSubmissionAcceptedParams) error
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /submissions/{SubmissionID}/status/validator-request-changes
ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error
// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@ -74,42 +26,18 @@ type Handler interface {
//
// POST /submissions/{SubmissionID}/status/validator-validated
ActionSubmissionValidated(ctx context.Context, params ActionSubmissionValidatedParams) error
// CreateMapfix implements createMapfix operation.
//
// Create a mapfix.
//
// POST /mapfixes
CreateMapfix(ctx context.Context, req *MapfixCreate) (*MapfixID, error)
// CreateMapfixAuditError implements createMapfixAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /mapfixes/{MapfixID}/error
CreateMapfixAuditError(ctx context.Context, params CreateMapfixAuditErrorParams) error
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
CreateScript(ctx context.Context, req *ScriptCreate) (*ScriptID, error)
CreateScript(ctx context.Context, req *ScriptCreate) (*ID, error)
// CreateScriptPolicy implements createScriptPolicy operation.
//
// Create a new script policy.
//
// POST /script-policy
CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (*ScriptPolicyID, error)
// CreateSubmission implements createSubmission operation.
//
// Create a new submission.
//
// POST /submissions
CreateSubmission(ctx context.Context, req *SubmissionCreate) (*SubmissionID, error)
// CreateSubmissionAuditError implements createSubmissionAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /submissions/{SubmissionID}/error
CreateSubmissionAuditError(ctx context.Context, params CreateSubmissionAuditErrorParams) error
CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (*ID, error)
// GetScript implements getScript operation.
//
// Get the specified script by ID.
@ -128,12 +56,6 @@ type Handler interface {
//
// GET /scripts
ListScripts(ctx context.Context, params ListScriptsParams) ([]Script, error)
// UpdateMapfixValidatedModel implements updateMapfixValidatedModel operation.
//
// Update validated model.
//
// POST /mapfixes/{MapfixID}/validated-model
UpdateMapfixValidatedModel(ctx context.Context, params UpdateMapfixValidatedModelParams) error
// UpdateSubmissionValidatedModel implements updateSubmissionValidatedModel operation.
//
// Update validated model.

@ -13,60 +13,6 @@ type UnimplementedHandler struct{}
var _ Handler = UnimplementedHandler{}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
func (UnimplementedHandler) ActionMapfixAccepted(ctx context.Context, params ActionMapfixAcceptedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /mapfixes/{MapfixID}/status/validator-request-changes
func (UnimplementedHandler) ActionMapfixRequestChanges(ctx context.Context, params ActionMapfixRequestChangesParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixSubmitted implements actionMapfixSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
func (UnimplementedHandler) ActionMapfixSubmitted(ctx context.Context, params ActionMapfixSubmittedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (UnimplementedHandler) ActionMapfixUploaded(ctx context.Context, params ActionMapfixUploadedParams) error {
return ht.ErrNotImplemented
}
// ActionMapfixValidated implements actionMapfixValidated operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
func (UnimplementedHandler) ActionMapfixValidated(ctx context.Context, params ActionMapfixValidatedParams) error {
return ht.ErrNotImplemented
}
// ActionOperationFailed implements actionOperationFailed operation.
//
// (Internal endpoint) Fail an operation and write a StatusMessage.
//
// POST /operations/{OperationID}/status/operation-failed
func (UnimplementedHandler) ActionOperationFailed(ctx context.Context, params ActionOperationFailedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
@ -76,24 +22,6 @@ func (UnimplementedHandler) ActionSubmissionAccepted(ctx context.Context, params
return ht.ErrNotImplemented
}
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> ChangesRequested.
//
// POST /submissions/{SubmissionID}/status/validator-request-changes
func (UnimplementedHandler) ActionSubmissionRequestChanges(ctx context.Context, params ActionSubmissionRequestChangesParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionSubmitted implements actionSubmissionSubmitted operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
func (UnimplementedHandler) ActionSubmissionSubmitted(ctx context.Context, params ActionSubmissionSubmittedParams) error {
return ht.ErrNotImplemented
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
@ -112,30 +40,12 @@ func (UnimplementedHandler) ActionSubmissionValidated(ctx context.Context, param
return ht.ErrNotImplemented
}
// CreateMapfix implements createMapfix operation.
//
// Create a mapfix.
//
// POST /mapfixes
func (UnimplementedHandler) CreateMapfix(ctx context.Context, req *MapfixCreate) (r *MapfixID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateMapfixAuditError implements createMapfixAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /mapfixes/{MapfixID}/error
func (UnimplementedHandler) CreateMapfixAuditError(ctx context.Context, params CreateMapfixAuditErrorParams) error {
return ht.ErrNotImplemented
}
// CreateScript implements createScript operation.
//
// Create a new script.
//
// POST /scripts
func (UnimplementedHandler) CreateScript(ctx context.Context, req *ScriptCreate) (r *ScriptID, _ error) {
func (UnimplementedHandler) CreateScript(ctx context.Context, req *ScriptCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented
}
@ -144,28 +54,10 @@ func (UnimplementedHandler) CreateScript(ctx context.Context, req *ScriptCreate)
// Create a new script policy.
//
// POST /script-policy
func (UnimplementedHandler) CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (r *ScriptPolicyID, _ error) {
func (UnimplementedHandler) CreateScriptPolicy(ctx context.Context, req *ScriptPolicyCreate) (r *ID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateSubmission implements createSubmission operation.
//
// Create a new submission.
//
// POST /submissions
func (UnimplementedHandler) CreateSubmission(ctx context.Context, req *SubmissionCreate) (r *SubmissionID, _ error) {
return r, ht.ErrNotImplemented
}
// CreateSubmissionAuditError implements createSubmissionAuditError operation.
//
// Validator posts an error to the audit log.
//
// POST /submissions/{SubmissionID}/error
func (UnimplementedHandler) CreateSubmissionAuditError(ctx context.Context, params CreateSubmissionAuditErrorParams) error {
return ht.ErrNotImplemented
}
// GetScript implements getScript operation.
//
// Get the specified script by ID.
@ -193,15 +85,6 @@ func (UnimplementedHandler) ListScripts(ctx context.Context, params ListScriptsP
return r, ht.ErrNotImplemented
}
// UpdateMapfixValidatedModel implements updateMapfixValidatedModel operation.
//
// Update validated model.
//
// POST /mapfixes/{MapfixID}/validated-model
func (UnimplementedHandler) UpdateMapfixValidatedModel(ctx context.Context, params UpdateMapfixValidatedModelParams) error {
return ht.ErrNotImplemented
}
// UpdateSubmissionValidatedModel implements updateSubmissionValidatedModel operation.
//
// Update validated model.

@ -8,308 +8,12 @@ import (
"github.com/ogen-go/ogen/validate"
)
func (s *Error) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.Code)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "code",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ErrorStatusCode) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := s.Response.Validate(); err != nil {
return err
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Response",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *MapfixCreate) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.OperationID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "OperationID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetOwner)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetOwner",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.DisplayName)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "DisplayName",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Creator)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Creator",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.GameID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "GameID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetVersion)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetVersion",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.TargetAssetID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "TargetAssetID",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 256,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Description)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Description",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *MapfixID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.MapfixID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "MapfixID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *Script) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ID",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
@ -367,46 +71,6 @@ func (s *Script) Validate() error {
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ResourceType)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceType",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ResourceID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
@ -457,85 +121,6 @@ func (s *ScriptCreate) Validate() error {
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ResourceType)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceType",
Error: err,
})
}
if err := func() error {
if value, ok := s.ResourceID.Get(); ok {
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(value)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
return err
}
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ResourceID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ScriptID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
@ -548,26 +133,6 @@ func (s *ScriptPolicy) Validate() error {
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ID",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 16,
@ -587,352 +152,6 @@ func (s *ScriptPolicy) Validate() error {
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ToScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ToScriptID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.Policy)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Policy",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptPolicyCreate) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.FromScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "FromScriptID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ToScriptID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ToScriptID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.Policy)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Policy",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *ScriptPolicyID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.ScriptPolicyID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "ScriptPolicyID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *SubmissionCreate) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.OperationID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "OperationID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetOwner)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetOwner",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.DisplayName)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "DisplayName",
Error: err,
})
}
if err := func() error {
if err := (validate.String{
MinLength: 0,
MinLengthSet: false,
MaxLength: 128,
MaxLengthSet: true,
Email: false,
Hostname: false,
Regex: nil,
}).Validate(string(s.Creator)); err != nil {
return errors.Wrap(err, "string")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Creator",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.GameID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "GameID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetID",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.AssetVersion)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "AssetVersion",
Error: err,
})
}
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: true,
Max: 9,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.Status)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "Status",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}
return nil
}
func (s *SubmissionID) Validate() error {
if s == nil {
return validate.ErrNilPointer
}
var failures []validate.FieldError
if err := func() error {
if err := (validate.Int{
MinSet: true,
Min: 0,
MaxSet: false,
Max: 0,
MinExclusive: false,
MaxExclusive: false,
MultipleOfSet: false,
MultipleOf: 0,
}).Validate(int64(s.SubmissionID)); err != nil {
return errors.Wrap(err, "int")
}
return nil
}(); err != nil {
failures = append(failures, validate.FieldError{
Name: "SubmissionID",
Error: err,
})
}
if len(failures) > 0 {
return &validate.Error{Fields: failures}
}

@ -1,59 +0,0 @@
package model
import (
"encoding/json"
"time"
)
type AuditEventType int32
// User clicked "Submit", "Accept" etc
const AuditEventTypeAction AuditEventType = 0
type AuditEventDataAction struct {
TargetStatus uint32 `json:"target_status"`
}
// User wrote a comment
const AuditEventTypeComment AuditEventType = 1
type AuditEventDataComment struct {
Comment string `json:"comment"`
}
// User changed Model
const AuditEventTypeChangeModel AuditEventType = 2
type AuditEventDataChangeModel struct {
OldModelID uint64 `json:"old_model_id"`
OldModelVersion uint64 `json:"old_model_version"`
NewModelID uint64 `json:"new_model_id"`
NewModelVersion uint64 `json:"new_model_version"`
}
// Validator validates model
const AuditEventTypeChangeValidatedModel AuditEventType = 3
type AuditEventDataChangeValidatedModel struct {
ValidatedModelID uint64 `json:"validated_model_id"`
ValidatedModelVersion uint64 `json:"validated_model_version"`
}
// User changed DisplayName / Creator
const AuditEventTypeChangeDisplayName AuditEventType = 4
const AuditEventTypeChangeCreator AuditEventType = 5
type AuditEventDataChangeName struct {
OldName string `json:"old_name"`
NewName string `json:"new_name"`
}
// Validator had an error
const AuditEventTypeError AuditEventType = 6
type AuditEventDataError struct {
Error string `json:"error"`
}
type AuditEvent struct {
ID int64 `gorm:"primaryKey"`
CreatedAt time.Time
User uint64
ResourceType ResourceType // is this a submission or is it a mapfix
ResourceID int64 // submission / mapfix / map ID
EventType AuditEventType
EventData json.RawMessage `gorm:"type:jsonb"`
}

@ -1,43 +0,0 @@
package model
import "time"
type MapfixStatus int32
const (
// Phase: Creation
MapfixStatusUnderConstruction MapfixStatus = 0
MapfixStatusChangesRequested MapfixStatus = 1
// Phase: Review
MapfixStatusSubmitting MapfixStatus = 2
MapfixStatusSubmitted MapfixStatus = 3
// Phase: Testing
MapfixStatusAcceptedUnvalidated MapfixStatus = 4 // pending script review, can re-trigger validation
MapfixStatusValidating MapfixStatus = 5
MapfixStatusValidated MapfixStatus = 6
MapfixStatusUploading MapfixStatus = 7
// Phase: Final MapfixStatus
MapfixStatusUploaded MapfixStatus = 8 // uploaded to the group, but pending release
MapfixStatusRejected MapfixStatus = 9
)
type Mapfix struct {
ID int64 `gorm:"primaryKey"`
DisplayName string
Creator string
GameID uint32
CreatedAt time.Time
UpdatedAt time.Time
Submitter uint64 // UserID
AssetID uint64
AssetVersion uint64
ValidatedAssetID uint64
ValidatedAssetVersion uint64
Completed bool // Has this version of the map been completed at least once on maptest
TargetAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID MapfixStatus
Description string // mapfix description
}

@ -5,63 +5,25 @@ package model
// Requests are sent from maps-service to validator
type CreateSubmissionRequest struct {
// operation_id is passed back in the response message
OperationID int32
ModelID uint64
DisplayName string
Creator string
GameID uint32
Status uint32
Roles uint32
}
type CreateMapfixRequest struct {
OperationID int32
ModelID uint64
TargetAssetID uint64
Description string
}
type CheckSubmissionRequest struct{
SubmissionID int64
ModelID uint64
SkipChecks bool
}
type CheckMapfixRequest struct{
MapfixID int64
ModelID uint64
SkipChecks bool
}
type ValidateSubmissionRequest struct {
type ValidateRequest struct {
// submission_id is passed back in the response message
SubmissionID int64
ModelID uint64
ModelVersion uint64
ValidatedModelID *uint64 // optional value
}
type ValidateMapfixRequest struct {
MapfixID int64
ModelID uint64
ModelVersion uint64
ValidatedModelID *uint64 // optional value
SubmissionID int64
ModelID int64
ModelVersion int64
ValidatedModelID *int64 // optional value
}
// Create a new map
type UploadSubmissionRequest struct {
SubmissionID int64
ModelID uint64
ModelVersion uint64
type UploadNewRequest struct {
SubmissionID int64
ModelID int64
ModelVersion int64
ModelName string
}
type UploadMapfixRequest struct {
MapfixID int64
ModelID uint64
ModelVersion uint64
TargetAssetID uint64
type UploadFixRequest struct {
SubmissionID int64
ModelID int64
ModelVersion int64
TargetAssetID int64
}

@ -1,19 +0,0 @@
package model
import "time"
type OperationStatus int32
const (
OperationStatusCreated OperationStatus = 0
OperationStatusCompleted OperationStatus = 1
OperationStatusFailed OperationStatus = 2
)
type Operation struct {
ID int32 `gorm:"primaryKey"`
CreatedAt time.Time
Owner uint64 // UserID
StatusID OperationStatus
StatusMessage string
Path string // redirect to view completed operation e.g. "/mapfixes/4"
}

@ -1,13 +0,0 @@
package model
type ResourceType int32
const (
ResourceUnknown ResourceType = 0
ResourceMapfix ResourceType = 1
ResourceSubmission ResourceType = 2
)
type Resource struct{
ID int64
Type ResourceType
}

@ -23,6 +23,13 @@ func HashParse(hash string) (uint64, error){
return strconv.ParseUint(hash, 16, 64)
}
type ResourceType int32
const (
ResourceUnknown ResourceType = 0
ResourceMapfix ResourceType = 1
ResourceSubmission ResourceType = 2
)
type Script struct {
ID int64 `gorm:"primaryKey"`
Name string

@ -5,39 +5,37 @@ import "time"
type SubmissionStatus int32
const (
// Phase: Creation
SubmissionStatusUnderConstruction SubmissionStatus = 0
SubmissionStatusChangesRequested SubmissionStatus = 1
// Phase: Review
SubmissionStatusSubmitting SubmissionStatus = 2
SubmissionStatusSubmitted SubmissionStatus = 3
// Phase: Final SubmissionStatus
SubmissionStatusReleased SubmissionStatus = 9
SubmissionStatusRejected SubmissionStatus = 8
// Phase: Testing
SubmissionStatusAcceptedUnvalidated SubmissionStatus = 4 // pending script review, can re-trigger validation
SubmissionStatusValidating SubmissionStatus = 5
SubmissionStatusValidated SubmissionStatus = 6
SubmissionStatusUploading SubmissionStatus = 7
SubmissionStatusUploaded SubmissionStatus = 8 // uploaded to the group, but pending release
SubmissionStatusUploaded SubmissionStatus = 7 // uploaded to the group, but pending release
SubmissionStatusUploading SubmissionStatus = 6
SubmissionStatusValidated SubmissionStatus = 5
SubmissionStatusValidating SubmissionStatus = 4
SubmissionStatusAccepted SubmissionStatus = 3 // pending script review, can re-trigger validation
// Phase: Final SubmissionStatus
SubmissionStatusRejected SubmissionStatus = 9
SubmissionStatusReleased SubmissionStatus = 10
// Phase: Creation
SubmissionStatusChangesRequested SubmissionStatus = 2
SubmissionStatusSubmitted SubmissionStatus = 1
SubmissionStatusUnderConstruction SubmissionStatus = 0
)
type Submission struct {
ID int64 `gorm:"primaryKey"`
DisplayName string
Creator string
GameID uint32
GameID int32
CreatedAt time.Time
UpdatedAt time.Time
Submitter uint64 // UserID
AssetID uint64
AssetVersion uint64
ValidatedAssetID uint64
ValidatedAssetVersion uint64
Submitter int64 // UserID
AssetID int64
AssetVersion int64
ValidatedAssetID int64
ValidatedAssetVersion int64
Completed bool // Has this version of the map been completed at least once on maptest
UploadedAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
UploadedAssetID int64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID SubmissionStatus
StatusMessage string
}

@ -1,132 +0,0 @@
package service
import (
"context"
"encoding/json"
"git.itzana.me/strafesnet/go-grpc/users"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
func (svc *Service) ListAuditEvents(ctx context.Context, resource model.Resource, page model.Page) ([]api.AuditEvent, error){
filter := datastore.Optional()
filter.Add("resource_type", resource.Type)
filter.Add("resource_id", resource.ID)
items, err := svc.DB.AuditEvents().List(ctx, filter, page)
if err != nil {
return nil, err
}
idMap := make(map[int64]bool)
for _, item := range items {
idMap[int64(item.User)] = true
}
var idList users.IdList
idList.ID = make([]int64,len(idMap))
for userId := range idMap {
idList.ID = append(idList.ID, userId)
}
userList, err := svc.Users.GetList(ctx, &idList)
if err != nil {
return nil, err
}
userMap := make(map[int64]*users.UserResponse)
for _,user := range userList.Users {
userMap[user.ID] = user
}
var resp []api.AuditEvent
for _, item := range items {
EventData := api.AuditEventEventData{}
err = EventData.UnmarshalJSON(item.EventData)
if err != nil {
return nil, err
}
username := ""
if userMap[int64(item.User)] != nil {
username = userMap[int64(item.User)].Username
}
resp = append(resp, api.AuditEvent{
ID: item.ID,
Date: item.CreatedAt.Unix(),
User: int64(item.User),
Username: username,
ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
EventType: int32(item.EventType),
EventData: EventData,
})
}
return resp, nil
}
func (svc *Service) CreateAuditEventAction(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataAction) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventComment(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataComment) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeComment,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventChangeModel(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataChangeModel) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeChangeModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}

File diff suppressed because it is too large Load Diff

@ -1,73 +0,0 @@
package service
import (
"context"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/maps-service/pkg/api"
)
// ListMaps implements listMaps operation.
//
// Get list of maps.
//
// GET /maps
func (svc *Service) ListMaps(ctx context.Context, params api.ListMapsParams) ([]api.Map, error) {
filter := maps.MapFilter{}
if params.DisplayName.IsSet(){
filter.DisplayName = &params.DisplayName.Value
}
if params.Creator.IsSet(){
filter.Creator = &params.Creator.Value
}
if params.GameID.IsSet(){
filter.GameID = &params.GameID.Value
}
mapList, err := svc.Maps.List(ctx, &maps.ListRequest{
Filter: &filter,
Page: &maps.Pagination{
Size: params.Limit,
Number: params.Page,
},
})
if err != nil {
return nil, err
}
var resp []api.Map
for _, item := range mapList.Maps {
resp = append(resp, api.Map{
ID: item.ID,
DisplayName: item.DisplayName,
Creator: item.Creator,
GameID: item.GameID,
Date: item.Date,
})
}
return resp, nil
}
// GetMap implements getScript operation.
//
// Get the specified script by ID.
//
// GET /maps/{MapID}
func (svc *Service) GetMap(ctx context.Context, params api.GetMapParams) (*api.Map, error) {
mapResponse, err := svc.Maps.Get(ctx, &maps.IdMessage{
ID: params.MapID,
})
if err != nil {
return nil, err
}
return &api.Map{
ID: mapResponse.ID,
DisplayName: mapResponse.DisplayName,
Creator: mapResponse.Creator,
GameID: mapResponse.GameID,
Date: mapResponse.Date,
}, nil
}

@ -1,46 +0,0 @@
package service
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/api"
)
// GetOperation implements getOperation operation.
//
// Get the specified operation by ID.
//
// GET /operations/{OperationID}
func (svc *Service) GetOperation(ctx context.Context, params api.GetOperationParams) (*api.Operation, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
// You must be the operation owner to read it
operation, err := svc.DB.Operations().Get(ctx, params.OperationID)
if err != nil {
return nil, err
}
userId, err := userInfo.GetUserID()
if err != nil {
return nil, err
}
// check if caller is the submitter
has_role := userId == operation.Owner
if !has_role {
return nil, ErrPermissionDeniedNotSubmitter
}
return &api.Operation{
OperationID: operation.ID,
Date: operation.CreatedAt.Unix(),
Owner: int64(operation.Owner),
Status: int32(operation.StatusID),
StatusMessage: operation.StatusMessage,
Path: operation.Path,
}, nil
}

@ -13,7 +13,7 @@ import (
// Create a new script policy.
//
// POST /script-policy
func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ScriptPolicyID, error) {
func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
@ -44,8 +44,8 @@ func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolic
return nil, err
}
return &api.ScriptPolicyID{
ScriptPolicyID: script.ID,
return &api.ID{
ID: script.ID,
}, nil
}
@ -63,13 +63,13 @@ func (svc *Service) ListScriptPolicy(ctx context.Context, params api.ListScriptP
if err != nil {
return nil, err
}
filter.Add("from_script_hash", int64(hash)) // No type safety!
filter.AddNotNil("from_script_hash", int64(hash)) // No type safety!
}
if params.ToScriptID.IsSet(){
filter.Add("to_script_id", params.ToScriptID.Value)
filter.AddNotNil("to_script_id", params.ToScriptID.Value)
}
if params.Policy.IsSet(){
filter.Add("policy", params.Policy.Value)
filter.AddNotNil("policy", params.Policy.Value)
}
items, err := svc.DB.ScriptPolicy().List(ctx, filter, model.Page{
@ -121,6 +121,13 @@ func (svc *Service) DeleteScriptPolicy(ctx context.Context, params api.DeleteScr
//
// GET /script-policy/{ScriptPolicyID}
func (svc *Service) GetScriptPolicy(ctx context.Context, params api.GetScriptPolicyParams) (*api.ScriptPolicy, error) {
_, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
// Read permission for script policy only requires you to be logged in
policy, err := svc.DB.ScriptPolicy().Get(ctx, params.ScriptPolicyID)
if err != nil {
return nil, err

@ -13,7 +13,7 @@ import (
// Create a new script.
//
// POST /scripts
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ScriptID, error) {
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
@ -39,8 +39,8 @@ func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*a
return nil, err
}
return &api.ScriptID{
ScriptID: script.ID,
return &api.ID{
ID: script.ID,
}, nil
}
@ -57,19 +57,16 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
if err != nil {
return nil, err
}
filter.Add("hash", int64(hash)) // No type safety!
filter.AddNotNil("hash", int64(hash)) // No type safety!
}
if params.Name.IsSet(){
filter.Add("name", params.Name.Value)
filter.AddNotNil("name", params.Name.Value)
}
if params.Source.IsSet(){
filter.Add("source", params.Source.Value)
filter.AddNotNil("source", params.Source.Value)
}
if params.ResourceType.IsSet(){
filter.Add("resource_type", params.ResourceType.Value)
}
if params.ResourceID.IsSet(){
filter.Add("resource_id", params.ResourceID.Value)
if params.SubmissionID.IsSet(){
filter.AddNotNil("submission_id", params.SubmissionID.Value)
}
items, err := svc.DB.Scripts().List(ctx, filter, model.Page{
@ -84,7 +81,6 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
for _, item := range items {
resp = append(resp, api.Script{
ID: item.ID,
Name: item.Name,
Hash: model.HashFormat(uint64(item.Hash)),
Source: item.Source,
ResourceType: int32(item.ResourceType),
@ -123,6 +119,13 @@ func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptPar
//
// GET /scripts/{ScriptID}
func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (*api.Script, error) {
_, ok := ctx.Value("UserInfo").(UserInfoHandle)
if !ok {
return nil, ErrUserInfo
}
// Read permission for scripts only requires you to be logged in
script, err := svc.DB.Scripts().Get(ctx, params.ScriptID)
if err != nil {
return nil, err

@ -17,12 +17,10 @@ var (
// Submissions roles bitflag
type Roles int32
var (
RolesSubmissionUpload Roles = 1<<6
RolesSubmissionReview Roles = 1<<5
RolesSubmissionRelease Roles = 1<<4
RolesScriptWrite Roles = 1<<3
RolesMapfixUpload Roles = 1<<2
RolesMapfixReview Roles = 1<<1
RolesMapUpload Roles = 1<<2
RolesMapReview Roles = 1<<1
RolesMapDownload Roles = 1<<0
RolesEmpty Roles = 0
)
@ -34,13 +32,13 @@ var (
RoleQuat GroupRole = 255
RoleItzaname GroupRole = 254
RoleStagingDeveloper GroupRole = 240
RolesAll Roles = ^RolesEmpty
RolesAll Roles = RolesScriptWrite|RolesSubmissionRelease|RolesMapUpload|RolesMapReview|RolesMapDownload
// has SubmissionUpload
RoleMapAdmin GroupRole = 128
RolesMapAdmin Roles = RolesSubmissionRelease|RolesSubmissionUpload|RolesSubmissionReview|RolesMapCouncil
// has MapfixReview
RolesMapAdmin Roles = RolesSubmissionRelease|RolesMapUpload|RolesMapReview|RolesMapDownload
// has SubmissionReview
RoleMapCouncil GroupRole = 64
RolesMapCouncil Roles = RolesMapfixReview|RolesMapfixUpload|RolesMapAccess
RolesMapCouncil Roles = RolesMapReview|RolesMapUpload|RolesMapDownload
// access to downloading maps
RoleMapAccess GroupRole = 32
RolesMapAccess Roles = RolesMapDownload
@ -88,6 +86,13 @@ func (usr UserInfoHandle) Validate() (bool, error) {
}
return validate.Valid, nil
}
func (usr UserInfoHandle) IsSubmitter(submitter uint64) (bool, error) {
userId, err := usr.GetUserID()
if err != nil {
return false, err
}
return userId == submitter, nil
}
func (usr UserInfoHandle) hasRoles(wantRoles Roles) (bool, error) {
haveroles, err := usr.GetRoles()
if err != nil {
@ -124,10 +129,10 @@ func (usr UserInfoHandle) GetRoles() (Roles, error) {
// RoleThumbnail
func (usr UserInfoHandle) HasRoleMapfixUpload() (bool, error) {
return usr.hasRoles(RolesMapfixUpload)
return usr.hasRoles(RolesMapUpload)
}
func (usr UserInfoHandle) HasRoleMapfixReview() (bool, error) {
return usr.hasRoles(RolesMapfixReview)
return usr.hasRoles(RolesMapReview)
}
func (usr UserInfoHandle) HasRoleMapDownload() (bool, error) {
return usr.hasRoles(RolesMapDownload)
@ -136,10 +141,10 @@ func (usr UserInfoHandle) HasRoleSubmissionRelease() (bool, error) {
return usr.hasRoles(RolesSubmissionRelease)
}
func (usr UserInfoHandle) HasRoleSubmissionUpload() (bool, error) {
return usr.hasRoles(RolesSubmissionUpload)
return usr.hasRoles(RolesMapUpload)
}
func (usr UserInfoHandle) HasRoleSubmissionReview() (bool, error) {
return usr.hasRoles(RolesSubmissionReview)
return usr.hasRoles(RolesMapReview)
}
func (usr UserInfoHandle) HasRoleScriptWrite() (bool, error) {
return usr.hasRoles(RolesScriptWrite)

@ -6,7 +6,6 @@ import (
"fmt"
"git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/go-grpc/users"
"git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"github.com/nats-io/nats.go"
@ -20,21 +19,17 @@ var (
ErrDelayReset = errors.New("Please give the validator at least 10 seconds to operate before attempting to reset the status")
ErrPermissionDeniedNotSubmitter = fmt.Errorf("%w: You must be the submitter to perform this action", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionRelease = fmt.Errorf("%w: Need Role SubmissionRelease", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapfixUpload = fmt.Errorf("%w: Need Role MapfixUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapfixReview = fmt.Errorf("%w: Need Role MapfixReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionUpload = fmt.Errorf("%w: Need Role SubmissionUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionReview = fmt.Errorf("%w: Need Role SubmissionReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapUpload = fmt.Errorf("%w: Need Role MapUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapReview = fmt.Errorf("%w: Need Role MapReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapDownload = fmt.Errorf("%w: Need Role MapDownload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleScriptWrite = fmt.Errorf("%w: Need Role ScriptWrite", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMaptest = fmt.Errorf("%w: Need Role Maptest", ErrPermissionDenied)
ErrNegativeID = errors.New("A negative ID was provided")
)
type Service struct {
DB datastore.Datastore
Nats nats.JetStreamContext
Maps maps.MapsServiceClient
Users users.UsersServiceClient
Client maps.MapsServiceClient
}
// NewError creates *ErrorStatusCode from error returned by handler.

File diff suppressed because it is too large Load Diff

@ -1,71 +0,0 @@
package service_internal
import (
"context"
"encoding/json"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
func (svc *Service) CreateAuditEventAction(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataAction) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeAction,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventChangeValidatedModel(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataChangeValidatedModel) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeChangeValidatedModel,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}
func (svc *Service) CreateAuditEventError(ctx context.Context, userId uint64, resource model.Resource, event_data model.AuditEventDataError) error {
EventData, err := json.Marshal(event_data)
if err != nil {
return err
}
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
ID: 0,
User: userId,
ResourceType: resource.Type,
ResourceID: resource.ID,
EventType: model.AuditEventTypeError,
EventData: EventData,
})
if err != nil {
return err
}
return nil
}

@ -1,299 +0,0 @@
package service_internal
import (
"context"
"errors"
"fmt"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
var(
// prevent two mapfixes with same asset id
ActiveMapfixStatuses = []model.MapfixStatus{
model.MapfixStatusUploading,
model.MapfixStatusValidated,
model.MapfixStatusValidating,
model.MapfixStatusAcceptedUnvalidated,
model.MapfixStatusChangesRequested,
model.MapfixStatusSubmitted,
model.MapfixStatusUnderConstruction,
}
)
var(
ErrActiveMapfixSameAssetID = errors.New("There is an active mapfix with the same AssetID")
ErrNotAssetOwner = errors.New("You can only submit an asset you own")
)
// UpdateMapfixValidatedModel implements patchMapfixModel operation.
//
// Update model following role restrictions.
//
// POST /mapfixes/{MapfixID}/validated-model
func (svc *Service) UpdateMapfixValidatedModel(ctx context.Context, params internal.UpdateMapfixValidatedModelParams) error {
ValidatedModelID := uint64(params.ValidatedModelID)
ValidatedModelVersion := uint64(params.ValidatedModelVersion)
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.Add("validated_asset_id", ValidatedModelID)
pmap.Add("validated_asset_version", ValidatedModelVersion)
// DO NOT reset completed when validated model is updated
// pmap.Add("completed", false)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, pmap)
if err != nil {
return err
}
event_data := model.AuditEventDataChangeValidatedModel{
ValidatedModelID: ValidatedModelID,
ValidatedModelVersion: ValidatedModelVersion,
}
return svc.CreateAuditEventChangeValidatedModel(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixSubmitted invokes actionMapfixSubmitted operation.
//
// Role Validator changes status from Submitting -> Submitted.
//
// POST /mapfixes/{MapfixID}/status/validator-submitted
func (svc *Service) ActionMapfixSubmitted(ctx context.Context, params internal.ActionMapfixSubmittedParams) error {
// transaction
target_status := model.MapfixStatusSubmitted
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("asset_version", params.ModelVersion)
smap.Add("display_name", params.DisplayName)
smap.Add("creator", params.Creator)
smap.Add("game_id", params.GameID)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixRequestChanges implements actionMapfixRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> RequestChanges.
//
// POST /mapfixes/{MapfixID}/status/validator-request-changes
func (svc *Service) ActionMapfixRequestChanges(ctx context.Context, params internal.ActionMapfixRequestChangesParams) error {
// transaction
target_status := model.MapfixStatusChangesRequested
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixValidate invokes actionMapfixValidate operation.
//
// Role Validator changes status from Validating -> Validated.
//
// POST /mapfixes/{MapfixID}/status/validator-validated
func (svc *Service) ActionMapfixValidated(ctx context.Context, params internal.ActionMapfixValidatedParams) error {
// transaction
smap := datastore.Optional()
smap.Add("status_id", model.MapfixStatusValidated)
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
}
// ActionMapfixAccepted implements actionMapfixAccepted operation.
//
// (Internal endpoint) Role Validator changes status from Validating -> Accepted.
//
// POST /mapfixes/{MapfixID}/status/validator-failed
func (svc *Service) ActionMapfixAccepted(ctx context.Context, params internal.ActionMapfixAcceptedParams) error {
// transaction
target_status := model.MapfixStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, smap)
if err != nil {
return err
}
// push an action audit event
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// ActionMapfixUploaded implements actionMapfixUploaded operation.
//
// (Internal endpoint) Role Validator changes status from Uploading -> Uploaded.
//
// POST /mapfixes/{MapfixID}/status/validator-uploaded
func (svc *Service) ActionMapfixUploaded(ctx context.Context, params internal.ActionMapfixUploadedParams) error {
// transaction
target_status := model.MapfixStatusUploaded
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUploading}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// CreateMapfixAuditError implements createMapfixAuditError operation.
//
// Post an error to the audit log
//
// POST /mapfixes/{MapfixID}/error
func (svc *Service) CreateMapfixAuditError(ctx context.Context, params internal.CreateMapfixAuditErrorParams) (error) {
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
return svc.CreateAuditEventError(
ctx,
ValidtorUserID,
model.Resource{
ID: params.MapfixID,
Type: model.ResourceMapfix,
},
event_data,
)
}
// POST /mapfixes
func (svc *Service) CreateMapfix(ctx context.Context, request *internal.MapfixCreate) (*internal.MapfixID, error) {
// sanitization
if request.GameID<0||
request.AssetOwner<0||
request.AssetID<0||
request.AssetVersion<0||
request.TargetAssetID<0{
return nil, ErrNegativeID
}
var GameID=uint32(request.GameID);
var Submitter=uint64(request.AssetOwner);
var AssetID=uint64(request.AssetID);
var AssetVersion=uint64(request.AssetVersion);
var TargetAssetID=uint64(request.TargetAssetID);
// Check if an active mapfix with the same asset id exists
{
filter := datastore.Optional()
filter.Add("asset_id", request.AssetID)
filter.Add("asset_version", request.AssetVersion)
filter.Add("status_id", ActiveMapfixStatuses)
active_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
Number: 1,
Size: 1,
},datastore.ListSortDisabled)
if err != nil {
return nil, err
}
if len(active_mapfixes) != 0{
return nil, ErrActiveMapfixSameAssetID
}
}
operation, err := svc.DB.Operations().Get(ctx, request.OperationID)
if err != nil {
return nil, err
}
// check if user owns asset
// TODO: allow bypass by admin
if operation.Owner != Submitter {
return nil, ErrNotAssetOwner
}
mapfix, err := svc.DB.Mapfixes().Create(ctx, model.Mapfix{
ID: 0,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: GameID,
Submitter: Submitter,
AssetID: AssetID,
AssetVersion: AssetVersion,
Completed: false,
TargetAssetID: TargetAssetID,
StatusID: model.MapfixStatusUnderConstruction,
Description: request.Description,
})
if err != nil {
return nil, err
}
// mark the operation as completed and provide the path
pmap := datastore.Optional()
pmap.Add("status_id", model.OperationStatusCompleted)
pmap.Add("path", fmt.Sprintf("/mapfixes/%d", mapfix.ID))
err = svc.DB.Operations().Update(ctx, request.OperationID, pmap)
if err != nil {
return nil, err
}
return &internal.MapfixID{
MapfixID: mapfix.ID,
}, nil
}

@ -1,21 +0,0 @@
package service_internal
import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
// ActionOperationFailed implements actionOperationFailed operation.
//
// Fail the specified OperationID with a StatusMessage.
//
// POST /operations/{OperationID}/status/operation-failed
func (svc *Service) ActionOperationFailed(ctx context.Context, params internal.ActionOperationFailedParams) (error) {
pmap := datastore.Optional()
pmap.Add("status_id", model.OperationStatusFailed)
pmap.Add("status_message", params.StatusMessage)
return svc.DB.Operations().Update(ctx, params.OperationID, pmap)
}

@ -4,7 +4,7 @@ import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
api "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
@ -13,7 +13,7 @@ import (
// Create a new script policy.
//
// POST /script-policy
func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ScriptPolicyID, error) {
func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ID, error) {
from_script, err := svc.DB.Scripts().Get(ctx, req.FromScriptID)
if err != nil {
return nil, err
@ -31,8 +31,8 @@ func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolic
return nil, err
}
return &api.ScriptPolicyID{
ScriptPolicyID: script.ID,
return &api.ID{
ID: script.ID,
}, nil
}
@ -49,13 +49,13 @@ func (svc *Service) ListScriptPolicy(ctx context.Context, params api.ListScriptP
if err != nil {
return nil, err
}
filter.Add("from_script_hash", int64(hash)) // No type safety!
filter.AddNotNil("from_script_hash", int64(hash)) // No type safety!
}
if params.ToScriptID.IsSet(){
filter.Add("to_script_id", params.ToScriptID.Value)
filter.AddNotNil("to_script_id", params.ToScriptID.Value)
}
if params.Policy.IsSet(){
filter.Add("policy", params.Policy.Value)
filter.AddNotNil("policy", params.Policy.Value)
}
items, err := svc.DB.ScriptPolicy().List(ctx, filter, model.Page{

@ -4,7 +4,7 @@ import (
"context"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
api "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/model"
)
@ -13,7 +13,7 @@ import (
// Create a new script.
//
// POST /scripts
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ScriptID, error) {
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ID, error) {
script, err := svc.DB.Scripts().Create(ctx, model.Script{
ID: 0,
Name: req.Name,
@ -26,8 +26,8 @@ func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*a
return nil, err
}
return &api.ScriptID{
ScriptID: script.ID,
return &api.ID{
ID: script.ID,
}, nil
}
@ -44,19 +44,16 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
if err != nil {
return nil, err
}
filter.Add("hash", int64(hash)) // No type safety!
filter.AddNotNil("hash", int64(hash)) // No type safety!
}
if params.Name.IsSet(){
filter.Add("name", params.Name.Value)
filter.AddNotNil("name", params.Name.Value)
}
if params.Source.IsSet(){
filter.Add("source", params.Source.Value)
filter.AddNotNil("source", params.Source.Value)
}
if params.ResourceType.IsSet(){
filter.Add("resource_type", params.ResourceType.Value)
}
if params.ResourceID.IsSet(){
filter.Add("resource_id", params.ResourceID.Value)
if params.SubmissionID.IsSet(){
filter.AddNotNil("submission_id", params.SubmissionID.Value)
}
items, err := svc.DB.Scripts().List(ctx, filter, model.Page{
@ -71,7 +68,6 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
for _, item := range items {
resp = append(resp, api.Script{
ID: item.ID,
Name: item.Name,
Hash: model.HashFormat(uint64(item.Hash)),
Source: item.Source,
ResourceType: int32(item.ResourceType),

@ -3,21 +3,12 @@ package service_internal
import (
"context"
"errors"
"math"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"github.com/nats-io/nats.go"
)
const (
ValidtorUserID uint64 = uint64(math.MaxInt64)
)
var (
ErrNegativeID = errors.New("A negative ID was provided")
)
type Service struct {
DB datastore.Datastore
Nats nats.JetStreamContext

@ -2,30 +2,10 @@ package service_internal
import (
"context"
"errors"
"fmt"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
internal "git.itzana.me/strafesnet/maps-service/pkg/internal"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"git.itzana.me/strafesnet/maps-service/pkg/service"
)
var(
// prevent two mapfixes with same asset id
ActiveSubmissionStatuses = []model.SubmissionStatus{
model.SubmissionStatusUploading,
model.SubmissionStatusValidated,
model.SubmissionStatusValidating,
model.SubmissionStatusAcceptedUnvalidated,
model.SubmissionStatusChangesRequested,
model.SubmissionStatusSubmitted,
model.SubmissionStatusUnderConstruction,
}
)
var(
ErrActiveSubmissionSameAssetID = errors.New("There is an active submission with the same AssetID")
)
// UpdateSubmissionValidatedModel implements patchSubmissionModel operation.
@ -34,99 +14,13 @@ var(
//
// POST /submissions/{SubmissionID}/validated-model
func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params internal.UpdateSubmissionValidatedModelParams) error {
ValidatedModelID := uint64(params.ValidatedModelID)
ValidatedModelVersion := uint64(params.ValidatedModelVersion)
// check if Status is ChangesRequested|Submitted|UnderConstruction
pmap := datastore.Optional()
pmap.Add("validated_asset_id", ValidatedModelID)
pmap.Add("validated_asset_version", ValidatedModelVersion)
pmap.AddNotNil("validated_asset_id", params.ValidatedModelID)
pmap.AddNotNil("validated_asset_version", params.ValidatedModelVersion)
// DO NOT reset completed when validated model is updated
// pmap.Add("completed", false)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, pmap)
if err != nil {
return err
}
event_data := model.AuditEventDataChangeValidatedModel{
ValidatedModelID: ValidatedModelID,
ValidatedModelVersion: ValidatedModelVersion,
}
return svc.CreateAuditEventChangeValidatedModel(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation.
//
// Role Validator changes status from Submitting -> Submitted.
//
// POST /submissions/{SubmissionID}/status/validator-submitted
func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params internal.ActionSubmissionSubmittedParams) error {
// transaction
target_status := model.SubmissionStatusSubmitted
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("asset_version", params.ModelVersion)
smap.Add("display_name", params.DisplayName)
smap.Add("creator", params.Creator)
smap.Add("game_id", params.GameID)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation.
//
// (Internal endpoint) Role Validator changes status from Submitting -> RequestChanges.
//
// POST /submissions/{SubmissionID}/status/validator-request-changes
func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params internal.ActionSubmissionRequestChangesParams) error {
// transaction
target_status := model.SubmissionStatusChangesRequested
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap)
if err != nil {
return err
}
// push an action audit event
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, pmap)
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
@ -136,27 +30,9 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params i
// POST /submissions/{SubmissionID}/status/validator-validated
func (svc *Service) ActionSubmissionValidated(ctx context.Context, params internal.ActionSubmissionValidatedParams) error {
// transaction
target_status := model.SubmissionStatusValidated
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
smap.Add("status_id", model.SubmissionStatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
}
// ActionSubmissionAccepted implements actionSubmissionAccepted operation.
@ -166,28 +42,10 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params intern
// POST /submissions/{SubmissionID}/status/validator-failed
func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params internal.ActionSubmissionAcceptedParams) error {
// transaction
target_status := model.SubmissionStatusAcceptedUnvalidated
smap := datastore.Optional()
smap.Add("status_id", target_status)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
if err != nil {
return err
}
// push an action audit event
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
smap.Add("status_id", model.SubmissionStatusAccepted)
smap.Add("status_message", params.StatusMessage)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
}
// ActionSubmissionUploaded implements actionSubmissionUploaded operation.
@ -197,124 +55,8 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params interna
// POST /submissions/{SubmissionID}/status/validator-uploaded
func (svc *Service) ActionSubmissionUploaded(ctx context.Context, params internal.ActionSubmissionUploadedParams) error {
// transaction
target_status := model.SubmissionStatusUploaded
smap := datastore.Optional()
smap.Add("status_id", target_status)
smap.Add("status_id", model.SubmissionStatusUploaded)
smap.Add("uploaded_asset_id", params.UploadedAssetID)
err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
if err != nil {
return err
}
event_data := model.AuditEventDataAction{
TargetStatus: uint32(target_status),
}
return svc.CreateAuditEventAction(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// CreateSubmissionAuditError implements createSubmissionAuditError operation.
//
// Post an error to the audit log
//
// POST /submissions/{SubmissionID}/error
func (svc *Service) CreateSubmissionAuditError(ctx context.Context, params internal.CreateSubmissionAuditErrorParams) (error) {
event_data := model.AuditEventDataError{
Error: params.ErrorMessage,
}
return svc.CreateAuditEventError(
ctx,
ValidtorUserID,
model.Resource{
ID: params.SubmissionID,
Type: model.ResourceSubmission,
},
event_data,
)
}
// POST /submissions
func (svc *Service) CreateSubmission(ctx context.Context, request *internal.SubmissionCreate) (*internal.SubmissionID, error) {
// sanitization
if request.GameID<0||
request.AssetOwner<0||
request.AssetID<0||
request.AssetVersion<0{
return nil, ErrNegativeID
}
var GameID=uint32(request.GameID);
var Submitter=uint64(request.AssetOwner);
var AssetID=uint64(request.AssetID);
var AssetVersion=uint64(request.AssetVersion);
var Status=model.SubmissionStatus(request.Status);
var roles=service.Roles(request.Roles);
// Check if an active submission with the same asset id exists
{
filter := datastore.Optional()
filter.Add("asset_id", request.AssetID)
filter.Add("asset_version", request.AssetVersion)
filter.Add("status_id", ActiveSubmissionStatuses)
active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1,
Size: 1,
},datastore.ListSortDisabled)
if err != nil {
return nil, err
}
if len(active_submissions) != 0{
return nil, ErrActiveSubmissionSameAssetID
}
}
operation, err := svc.DB.Operations().Get(ctx, request.OperationID)
if err != nil {
return nil, err
}
// check if user owns asset
is_submitter := operation.Owner == Submitter
// check if user is map admin
has_submission_review := roles & service.RolesSubmissionReview == service.RolesSubmissionReview
// if neither, u not allowed
if !is_submitter && !has_submission_review {
return nil, ErrNotAssetOwner
}
submission, err := svc.DB.Submissions().Create(ctx, model.Submission{
ID: 0,
DisplayName: request.DisplayName,
Creator: request.Creator,
GameID: GameID,
Submitter: Submitter,
AssetID: AssetID,
AssetVersion: AssetVersion,
Completed: false,
StatusID: Status,
})
if err != nil {
return nil, err
}
// mark the operation as completed and provide the path
pmap := datastore.Optional()
pmap.Add("status_id", model.OperationStatusCompleted)
pmap.Add("path", fmt.Sprintf("/submissions/%d", submission.ID))
err = svc.DB.Operations().Update(ctx, request.OperationID, pmap)
if err != nil {
return nil, err
}
return &internal.SubmissionID{
SubmissionID: submission.ID,
}, nil
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
}

@ -5,16 +5,14 @@ edition = "2021"
[dependencies]
submissions-api = { path = "api", features = ["internal"], default-features = false, registry = "strafesnet" }
async-nats = "0.41.0"
async-nats = "0.40.0"
futures = "0.3.31"
rbx_asset = { version = "0.4.5", registry = "strafesnet" }
rbx_binary = "1.0.0"
rbx_dom_weak = "3.0.0"
rbx_reflection_database = "1.0.3"
rbx_xml = "1.0.0"
rbx_asset = { version = "0.2.5", registry = "strafesnet" }
rbx_binary = { version = "0.7.4", registry = "strafesnet"}
rbx_dom_weak = { version = "2.9.0", registry = "strafesnet"}
rbx_reflection_database = { version = "0.2.12", registry = "strafesnet"}
rbx_xml = { version = "0.13.3", registry = "strafesnet"}
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.133"
siphasher = "1.0.1"
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "signal"] }
heck = "0.5.0"
lazy-regex = "3.4.1"

@ -1,6 +1,6 @@
# Using the `rust-musl-builder` as base image, instead of
# the official Rust toolchain
FROM registry.itzana.me/docker-proxy/clux/muslrust:1.86.0-stable AS chef
FROM docker.io/clux/muslrust:stable AS chef
USER root
RUN cargo install cargo-chef
WORKDIR /app
@ -17,7 +17,7 @@ RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path r
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl --bin maps-validation
FROM registry.itzana.me/docker-proxy/alpine:3.21 AS runtime
FROM docker.io/alpine:latest AS runtime
RUN addgroup -S myuser && adduser -S myuser -G myuser
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/maps-validation /usr/local/bin/
USER myuser

@ -1,6 +1,6 @@
[package]
name = "submissions-api"
version = "0.8.1"
version = "0.6.1"
edition = "2021"
publish = ["strafesnet"]
repository = "https://git.itzana.me/StrafesNET/maps-service"
@ -11,7 +11,6 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4.41", features = ["serde"] }
reqwest = { version = "0", features = ["json"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

@ -44,8 +44,4 @@ impl Context{
.body(body)
.send().await
}
pub async fn delete(&self,url:impl reqwest::IntoUrl)->Result<reqwest::Response,reqwest::Error>{
self.client.delete(url)
.send().await
}
}

@ -14,9 +14,9 @@ impl Context{
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
.json().await.map_err(Error::Reqwest)
}
pub async fn get_scripts(&self,config:GetScriptsRequest<'_>)->Result<Vec<ScriptResponse>,Error>{
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
let url_raw=format!("{}/scripts",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
@ -33,35 +33,31 @@ impl Context{
if let Some(source)=config.Source{
query_pairs.append_pair("Source",source);
}
if let Some(resource_type)=config.ResourceType{
query_pairs.append_pair("ResourceType",(resource_type as i32).to_string().as_str());
}
if let Some(ResourceID(resource_id))=config.ResourceID{
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
if let Some(submission_id)=config.SubmissionID{
query_pairs.append_pair("SubmissionID",submission_id.to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
.json().await.map_err(Error::Reqwest)
}
pub async fn get_script_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptResponse>,ScriptSingleItemError>{
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
let scripts=self.get_scripts(GetScriptsRequest{
Page:1,
Limit:2,
Hash:Some(config.hash),
Name:None,
Source:None,
ResourceType:None,
ResourceID:None,
SubmissionID:None,
}).await.map_err(SingleItemError::Other)?;
if 1<scripts.len(){
return Err(SingleItemError::DuplicateItems(scripts));
return Err(SingleItemError::DuplicateItems);
}
Ok(scripts.into_iter().next())
}
pub async fn create_script(&self,config:CreateScriptRequest<'_>)->Result<ScriptIDResponse,Error>{
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
let url_raw=format!("{}/scripts",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
@ -70,19 +66,9 @@ impl Context{
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
.json().await.map_err(Error::Reqwest)
}
pub async fn delete_script(&self,config:GetScriptRequest)->Result<(),Error>{
let url_raw=format!("{}/scripts/{}",self.0.base_url,config.ScriptID.0);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
response_ok(
self.0.delete(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
pub async fn get_script_policies(&self,config:GetScriptPoliciesRequest<'_>)->Result<Vec<ScriptPolicyResponse>,Error>{
pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
let url_raw=format!("{}/script-policy",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
@ -104,9 +90,9 @@ impl Context{
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
.json().await.map_err(Error::Reqwest)
}
pub async fn get_script_policy_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptPolicyResponse>,ScriptPolicySingleItemError>{
pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
let policies=self.get_script_policies(GetScriptPoliciesRequest{
Page:1,
Limit:2,
@ -115,7 +101,7 @@ impl Context{
Policy:None,
}).await.map_err(SingleItemError::Other)?;
if 1<policies.len(){
return Err(SingleItemError::DuplicateItems(policies));
return Err(SingleItemError::DuplicateItems);
}
Ok(policies.into_iter().next())
}
@ -128,7 +114,7 @@ impl Context{
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
.json().await.map_err(Error::Reqwest)
}
pub async fn update_script_policy(&self,config:UpdateScriptPolicyRequest)->Result<(),Error>{
let url_raw=format!("{}/script-policy/{}",self.0.base_url,config.ID.0);
@ -140,94 +126,6 @@ impl Context{
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
pub async fn delete_script_policy(&self,config:GetScriptPolicyRequest)->Result<(),Error>{
let url_raw=format!("{}/script-policy/{}",self.0.base_url,config.ScriptPolicyID.0);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
response_ok(
self.0.delete(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
pub async fn get_submissions(&self,config:GetSubmissionsRequest<'_>)->Result<SubmissionsResponse,Error>{
let url_raw=format!("{}/submissions",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(sort)=config.Sort{
query_pairs.append_pair("Sort",(sort as u8).to_string().as_str());
}
if let Some(display_name)=config.DisplayName{
query_pairs.append_pair("DisplayName",display_name);
}
if let Some(creator)=config.Creator{
query_pairs.append_pair("Creator",creator);
}
if let Some(game_id)=config.GameID{
query_pairs.append_pair("GameID",(game_id as u8).to_string().as_str());
}
if let Some(submitter)=config.Submitter{
query_pairs.append_pair("Submitter",submitter.to_string().as_str());
}
if let Some(asset_id)=config.AssetID{
query_pairs.append_pair("AssetID",asset_id.to_string().as_str());
}
if let Some(uploaded_asset_id)=config.UploadedAssetID{
query_pairs.append_pair("UploadedAssetID",uploaded_asset_id.to_string().as_str());
}
if let Some(status_id)=config.StatusID{
query_pairs.append_pair("StatusID",(status_id as u8).to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn get_maps(&self,config:GetMapsRequest<'_>)->Result<Vec<MapResponse>,Error>{
let url_raw=format!("{}/maps",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
let mut query_pairs=url.query_pairs_mut();
query_pairs.append_pair("Page",config.Page.to_string().as_str());
query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
if let Some(sort)=config.Sort{
query_pairs.append_pair("Sort",(sort as u8).to_string().as_str());
}
if let Some(display_name)=config.DisplayName{
query_pairs.append_pair("DisplayName",display_name);
}
if let Some(creator)=config.Creator{
query_pairs.append_pair("Creator",creator);
}
if let Some(game_id)=config.GameID{
query_pairs.append_pair("GameID",(game_id as u8).to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
pub async fn release_submissions(&self,config:ReleaseRequest<'_>)->Result<(),Error>{
let url_raw=format!("{}/release-submissions",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(config.schedule).map_err(Error::JSON)?;
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
}

@ -3,27 +3,12 @@ use crate::types::*;
#[derive(Clone)]
pub struct Context(crate::context::Context);
// conditionally include specified query pairs
macro_rules! query_pairs{
($url:expr,)=>{
$url
};
($url:expr,$(($param:expr,$value:expr))+)=>{
{
let mut url=$url;
url.query_pairs_mut()
$(.append_pair($param,$value))*;
url
}
};
}
// there are lots of action endpoints and they all follow the same pattern
macro_rules! action{
($system:expr,$fname:ident,$config:ident,$config_type:ident,$action:expr,$config_submission_id:expr,$(($param:expr,$value:expr))*)=>{
pub async fn $fname(&self,$config:$config_type)->Result<(),Error>{
let url_raw=format!(concat!("{}/",$system,"/{}/",$action),self.0.base_url,$config_submission_id);
let url=query_pairs!(reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?,$(($param,$value))*);
($fname:ident,$action:expr)=>{
pub async fn $fname(&self,config:SubmissionID)->Result<(),Error>{
let url_raw=format!(concat!("{}/submissions/{}/status/",$action),self.0.base_url,config.0);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
response_ok(
self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
@ -44,9 +29,9 @@ impl Context{
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
.json().await.map_err(Error::Reqwest)
}
pub async fn get_scripts(&self,config:GetScriptsRequest<'_>)->Result<Vec<ScriptResponse>,Error>{
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
let url_raw=format!("{}/scripts",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
@ -63,35 +48,31 @@ impl Context{
if let Some(source)=config.Source{
query_pairs.append_pair("Source",source);
}
if let Some(resource_type)=config.ResourceType{
query_pairs.append_pair("ResourceType",(resource_type as i32).to_string().as_str());
}
if let Some(ResourceID(resource_id))=config.ResourceID{
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
if let Some(submission_id)=config.SubmissionID{
query_pairs.append_pair("SubmissionID",submission_id.to_string().as_str());
}
}
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
.json().await.map_err(Error::Reqwest)
}
pub async fn get_script_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptResponse>,ScriptSingleItemError>{
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
let scripts=self.get_scripts(GetScriptsRequest{
Page:1,
Limit:2,
Hash:Some(config.hash),
Name:None,
Source:None,
ResourceType:None,
ResourceID:None,
SubmissionID:None,
}).await.map_err(SingleItemError::Other)?;
if 1<scripts.len(){
return Err(SingleItemError::DuplicateItems(scripts));
return Err(SingleItemError::DuplicateItems);
}
Ok(scripts.into_iter().next())
}
pub async fn create_script(&self,config:CreateScriptRequest<'_>)->Result<ScriptIDResponse,Error>{
pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
let url_raw=format!("{}/scripts",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
@ -100,9 +81,9 @@ impl Context{
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
.json().await.map_err(Error::Reqwest)
}
pub async fn get_script_policies(&self,config:GetScriptPoliciesRequest<'_>)->Result<Vec<ScriptPolicyResponse>,Error>{
pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
let url_raw=format!("{}/script-policy",self.0.base_url);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
@ -124,9 +105,9 @@ impl Context{
response_ok(
self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
.json().await.map_err(Error::Reqwest)
}
pub async fn get_script_policy_from_hash(&self,config:HashRequest<'_>)->Result<Option<ScriptPolicyResponse>,ScriptPolicySingleItemError>{
pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
let policies=self.get_script_policies(GetScriptPoliciesRequest{
Page:1,
Limit:2,
@ -135,7 +116,7 @@ impl Context{
Policy:None,
}).await.map_err(SingleItemError::Other)?;
if 1<policies.len(){
return Err(SingleItemError::DuplicateItems(policies));
return Err(SingleItemError::DuplicateItems);
}
Ok(policies.into_iter().next())
}
@ -148,70 +129,52 @@ impl Context{
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
.json().await.map_err(Error::Reqwest)
}
pub async fn create_submission(&self,config:CreateSubmissionRequest<'_>)->Result<SubmissionIDResponse,Error>{
let url_raw=format!("{}/submissions",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
pub async fn update_submission_validated_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/validated-model",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
let body=serde_json::to_string(&config).map_err(Error::JSON)?;
{
url.query_pairs_mut()
.append_pair("ValidatedModelID",config.ModelID.to_string().as_str())
.append_pair("ValidatedModelVersion",config.ModelVersion.to_string().as_str());
}
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
}
// simple submission endpoints
action!("submissions",action_submission_request_changes,config,ActionSubmissionRequestChangesRequest,"status/validator-request-changes",config.SubmissionID.0,);
action!("submissions",action_submission_submitted,config,ActionSubmissionSubmittedRequest,"status/validator-submitted",config.SubmissionID.0,
("ModelVersion",config.ModelVersion.to_string().as_str())
("DisplayName",config.DisplayName.as_str())
("Creator",config.Creator.as_str())
("GameID",(config.GameID as u8).to_string().as_str())
);
action!("submissions",action_submission_validated,config,SubmissionID,"status/validator-validated",config.0,);
action!("submissions",update_submission_validated_model,config,UpdateSubmissionModelRequest,"validated-model",config.SubmissionID.0,
("ValidatedModelID",config.ModelID.to_string().as_str())
("ValidatedModelVersion",config.ModelVersion.to_string().as_str())
);
action!("submissions",action_submission_uploaded,config,ActionSubmissionUploadedRequest,"status/validator-uploaded",config.SubmissionID.0,
("UploadedAssetID",config.UploadedAssetID.to_string().as_str())
);
action!("submissions",action_submission_accepted,config,ActionSubmissionAcceptedRequest,"status/validator-failed",config.SubmissionID.0,);
action!("submissions",create_submission_audit_error,config,CreateSubmissionAuditErrorRequest,"error",config.SubmissionID.0,
("ErrorMessage",config.ErrorMessage.as_str())
);
pub async fn create_mapfix(&self,config:CreateMapfixRequest<'_>)->Result<MapfixIDResponse,Error>{
let url_raw=format!("{}/mapfixes",self.0.base_url);
let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
let body=serde_json::to_string(&config).map_err(Error::JSON)?;
Ok(())
}
pub async fn action_submission_uploaded(&self,config:ActionSubmissionUploadedRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/status/validator-uploaded",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
url.query_pairs_mut()
.append_pair("UploadedAssetID",config.UploadedAssetID.to_string().as_str());
}
response_ok(
self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
pub async fn action_submission_accepted(&self,config:ActionSubmissionAcceptedRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/status/validator-failed",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
{
url.query_pairs_mut()
.append_pair("StatusMessage",config.StatusMessage.as_str());
}
response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?
.json().await.map_err(Error::ReqwestJson)
self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)?;
Ok(())
}
// simple mapfixes endpoints
action!("mapfixes",action_mapfix_request_changes,config,ActionMapfixRequestChangesRequest,"status/validator-request-changes",config.MapfixID.0,);
action!("mapfixes",action_mapfix_submitted,config,ActionMapfixSubmittedRequest,"status/validator-submitted",config.MapfixID.0,
("ModelVersion",config.ModelVersion.to_string().as_str())
("DisplayName",config.DisplayName.as_str())
("Creator",config.Creator.as_str())
("GameID",(config.GameID as u8).to_string().as_str())
);
action!("mapfixes",action_mapfix_validated,config,MapfixID,"status/validator-validated",config.0,);
action!("mapfixes",update_mapfix_validated_model,config,UpdateMapfixModelRequest,"validated-model",config.MapfixID.0,
("ValidatedModelID",config.ModelID.to_string().as_str())
("ValidatedModelVersion",config.ModelVersion.to_string().as_str())
);
action!("mapfixes",action_mapfix_uploaded,config,ActionMapfixUploadedRequest,"status/validator-uploaded",config.MapfixID.0,);
action!("mapfixes",action_mapfix_accepted,config,ActionMapfixAcceptedRequest,"status/validator-failed",config.MapfixID.0,);
// simple operation endpoint
action!("operations",action_operation_failed,config,ActionOperationFailedRequest,"status/operation-failed",config.OperationID.0,
("StatusMessage",config.StatusMessage.as_str())
);
action!("mapfixes",create_mapfix_audit_error,config,CreateMapfixAuditErrorRequest,"error",config.MapfixID.0,
("ErrorMessage",config.ErrorMessage.as_str())
);
action!(action_submission_validated,"validator-validated");
}

@ -2,7 +2,6 @@
pub enum Error{
Parse(url::ParseError),
Reqwest(reqwest::Error),
ReqwestJson(reqwest::Error),
Response(ResponseError),
JSON(serde_json::Error),
}
@ -14,25 +13,21 @@ impl std::fmt::Display for Error{
impl std::error::Error for Error{}
#[derive(Debug)]
pub enum SingleItemError<Items>{
DuplicateItems(Items),
pub enum SingleItemError{
DuplicateItems,
Other(Error),
}
impl<Items> std::fmt::Display for SingleItemError<Items>
where
Items:std::fmt::Debug
{
impl std::fmt::Display for SingleItemError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl<Items> std::error::Error for SingleItemError<Items> where Items:std::fmt::Debug{}
pub type ScriptSingleItemError=SingleItemError<Vec<ScriptResponse>>;
pub type ScriptPolicySingleItemError=SingleItemError<Vec<ScriptPolicyResponse>>;
impl std::error::Error for SingleItemError{}
#[allow(dead_code)]
#[derive(Debug)]
pub struct UrlAndBody{
pub struct StatusCodeWithUrlAndBody{
pub status_code:reqwest::StatusCode,
pub url:url::Url,
pub body:String,
}
@ -40,10 +35,7 @@ pub struct UrlAndBody{
#[derive(Debug)]
pub enum ResponseError{
Reqwest(reqwest::Error),
Details{
status_code:reqwest::StatusCode,
url_and_body:Box<UrlAndBody>,
},
StatusCodeWithUrlAndBody(StatusCodeWithUrlAndBody),
}
impl std::fmt::Display for ResponseError{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -60,73 +52,19 @@ pub async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,R
let url=response.url().to_owned();
let bytes=response.bytes().await.map_err(ResponseError::Reqwest)?;
let body=String::from_utf8_lossy(&bytes).to_string();
Err(ResponseError::Details{
Err(ResponseError::StatusCodeWithUrlAndBody(StatusCodeWithUrlAndBody{
status_code,
url_and_body:Box::new(UrlAndBody{url,body})
})
url,
body,
}))
}
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq,Ord,PartialOrd,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum GameID{
Bhop=1,
Surf=2,
KreedzClimb=3,
FlyTrials=5,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct CreateMapfixRequest<'a>{
pub OperationID:OperationID,
pub AssetOwner:i64,
pub DisplayName:&'a str,
pub Creator:&'a str,
pub GameID:GameID,
pub AssetID:u64,
pub AssetVersion:u64,
pub TargetAssetID:u64,
pub Description:&'a str,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct MapfixIDResponse{
pub MapfixID:MapfixID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct CreateSubmissionRequest<'a>{
pub OperationID:OperationID,
pub AssetOwner:i64,
pub DisplayName:&'a str,
pub Creator:&'a str,
pub GameID:GameID,
pub AssetID:u64,
pub AssetVersion:u64,
pub Status:u32,
pub Roles:u32,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct SubmissionIDResponse{
pub SubmissionID:SubmissionID,
}
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde::Serialize,serde::Deserialize)]
pub struct ScriptID(pub(crate)i64);
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct ScriptPolicyID(pub(crate)i64);
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
#[repr(i32)]
pub enum ResourceType{
Unknown=0,
Mapfix=1,
Submission=2,
}
#[allow(nonstandard_style)]
pub struct GetScriptRequest{
pub ScriptID:ScriptID,
@ -143,9 +81,7 @@ pub struct GetScriptsRequest<'a>{
#[serde(skip_serializing_if="Option::is_none")]
pub Source:Option<&'a str>,
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceType:Option<ResourceType>,
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceID:Option<ResourceID>,
pub SubmissionID:Option<i64>,
}
#[derive(Clone,Copy,Debug)]
pub struct HashRequest<'a>{
@ -158,8 +94,13 @@ pub struct ScriptResponse{
pub Name:String,
pub Hash:String,
pub Source:String,
pub ResourceType:ResourceType,
pub ResourceID:ResourceID,
pub SubmissionID:i64,
}
#[derive(Clone,Debug,serde::Serialize)]
pub enum ResourceType{
Unknown=0,
Mapfix=1,
Submission=2,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
@ -168,12 +109,12 @@ pub struct CreateScriptRequest<'a>{
pub Source:&'a str,
pub ResourceType:ResourceType,
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceID:Option<ResourceID>,
pub ResourceID:Option<i64>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct ScriptIDResponse{
pub ScriptID:ScriptID,
pub ID:ScriptID,
}
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
@ -186,10 +127,6 @@ pub enum Policy{
Replace=4,
}
#[allow(nonstandard_style)]
pub struct GetScriptPolicyRequest{
pub ScriptPolicyID:ScriptPolicyID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct GetScriptPoliciesRequest<'a>{
@ -220,7 +157,7 @@ pub struct CreateScriptPolicyRequest{
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct ScriptPolicyIDResponse{
pub ScriptPolicyID:ScriptPolicyID,
pub ID:ScriptPolicyID,
}
#[allow(nonstandard_style)]
@ -238,221 +175,48 @@ pub struct UpdateScriptPolicyRequest{
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateSubmissionModelRequest{
pub SubmissionID:SubmissionID,
pub SubmissionID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
}
#[derive(Clone,Debug)]
pub enum Sort{
Disabled=0,
DisplayNameAscending=1,
DisplayNameDescending=2,
DateAscending=3,
DateDescending=4,
}
#[derive(Clone,Debug,serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum SubmissionStatus{
// Phase: Creation
UnderConstruction=0,
ChangesRequested=1,
// Phase: Review
Submitting=2,
Submitted=3,
// Phase: Testing
AcceptedUnvalidated=4, // pending script review, can re-trigger validation
Validating=5,
Validated=6,
Uploading=7,
Uploaded=8, // uploaded to the group, but pending release
// Phase: Final SubmissionStatus
Rejected=9,
Released=10,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct GetSubmissionsRequest<'a>{
pub Page:u32,
pub Limit:u32,
pub Sort:Option<Sort>,
pub DisplayName:Option<&'a str>,
pub Creator:Option<&'a str>,
pub GameID:Option<GameID>,
pub Submitter:Option<u64>,
pub AssetID:Option<u64>,
pub UploadedAssetID:Option<u64>,
pub StatusID:Option<SubmissionStatus>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct SubmissionResponse{
pub ID:SubmissionID,
pub DisplayName:String,
pub Creator:String,
pub GameID:GameID,
pub CreatedAt:i64,
pub UpdatedAt:i64,
pub Submitter:u64,
pub AssetID:u64,
pub AssetVersion:u64,
pub UploadedAssetID:u64,
pub StatusID:SubmissionStatus,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct SubmissionsResponse{
pub Total:u64,
pub Submissions:Vec<SubmissionResponse>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct GetMapsRequest<'a>{
pub Page:u32,
pub Limit:u32,
pub Sort:Option<Sort>,
pub DisplayName:Option<&'a str>,
pub Creator:Option<&'a str>,
pub GameID:Option<GameID>,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)]
pub struct MapResponse{
pub ID:i64,
pub DisplayName:String,
pub Creator:String,
pub GameID:GameID,
pub Date:i64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionSubmittedRequest{
pub SubmissionID:SubmissionID,
pub ModelVersion:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:GameID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionRequestChangesRequest{
pub SubmissionID:SubmissionID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionUploadedRequest{
pub SubmissionID:SubmissionID,
pub SubmissionID:i64,
pub UploadedAssetID:u64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionSubmissionAcceptedRequest{
pub SubmissionID:SubmissionID,
pub SubmissionID:i64,
pub StatusMessage:String,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct CreateSubmissionAuditErrorRequest{
pub SubmissionID:SubmissionID,
pub ErrorMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct SubmissionID(pub(crate)i64);
#[derive(Clone,Copy,Debug)]
pub struct SubmissionID(pub i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct UpdateMapfixModelRequest{
pub MapfixID:MapfixID,
pub MapfixID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixSubmittedRequest{
pub MapfixID:MapfixID,
pub ModelVersion:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:GameID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixRequestChangesRequest{
pub MapfixID:MapfixID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixUploadedRequest{
pub MapfixID:MapfixID,
pub MapfixID:i64,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionMapfixAcceptedRequest{
pub MapfixID:MapfixID,
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct CreateMapfixAuditErrorRequest{
pub MapfixID:MapfixID,
pub ErrorMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct MapfixID(pub(crate)i64);
#[allow(nonstandard_style)]
#[derive(Clone,Debug)]
pub struct ActionOperationFailedRequest{
pub OperationID:OperationID,
pub MapfixID:i64,
pub StatusMessage:String,
}
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct OperationID(pub(crate)i64);
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct ResourceID(pub(crate)i64);
#[derive(Clone,Copy,Debug)]
pub enum Resource{
Submission(SubmissionID),
Mapfix(MapfixID),
}
impl Resource{
pub fn split(self)->(ResourceType,ResourceID){
match self{
Resource::Mapfix(MapfixID(mapfix_id))=>(ResourceType::Mapfix,ResourceID(mapfix_id)),
Resource::Submission(SubmissionID(submission_id))=>(ResourceType::Submission,ResourceID(submission_id)),
}
}
}
#[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)]
pub struct ReleaseInfo{
pub SubmissionID:SubmissionID,
pub Date:chrono::DateTime<chrono::Utc>,
}
pub struct ReleaseRequest<'a>{
pub schedule:&'a [ReleaseInfo],
}
pub struct MapfixID(pub i64);

@ -1,885 +0,0 @@
use std::collections::{HashSet,HashMap};
use crate::download::download_asset_version;
use crate::rbx_util::{get_mapinfo,get_root_instance,read_dom,ReadDomError,GameID,ParseGameIDError,MapInfo,GetRootInstanceError,StringValueError};
use heck::{ToSnakeCase,ToTitleCase};
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
ModelInfoDownload(rbx_asset::cloud::GetError),
CreatorTypeMustBeUser,
Download(crate::download::Error),
ModelFileDecode(ReadDomError),
GetRootInstance(GetRootInstanceError),
IntoMapInfoOwned(IntoMapInfoOwnedError),
ToJsonValue(serde_json::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
#[allow(nonstandard_style)]
pub struct CheckRequest{
ModelID:u64,
SkipChecks:bool,
}
impl From<crate::nats_types::CheckMapfixRequest> for CheckRequest{
fn from(value:crate::nats_types::CheckMapfixRequest)->Self{
Self{
ModelID:value.ModelID,
SkipChecks:value.SkipChecks,
}
}
}
impl From<crate::nats_types::CheckSubmissionRequest> for CheckRequest{
fn from(value:crate::nats_types::CheckSubmissionRequest)->Self{
Self{
ModelID:value.ModelID,
SkipChecks:value.SkipChecks,
}
}
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
struct ModeID(u64);
impl ModeID{
const MAIN:Self=Self(0);
const BONUS:Self=Self(1);
}
enum Zone{
Start,
Finish,
Anticheat,
}
struct ModeElement{
zone:Zone,
mode_id:ModeID,
}
#[allow(dead_code)]
pub enum IDParseError{
NoCaptures,
ParseInt(core::num::ParseIntError),
}
// Parse a Zone from a part name
impl std::str::FromStr for ModeElement{
type Err=IDParseError;
fn from_str(s:&str)->Result<Self,Self::Err>{
match s{
"MapStart"=>Ok(Self{zone:Zone::Start,mode_id:ModeID::MAIN}),
"MapFinish"=>Ok(Self{zone:Zone::Finish,mode_id:ModeID::MAIN}),
"MapAnticheat"=>Ok(Self{zone:Zone::Anticheat,mode_id:ModeID::MAIN}),
"BonusStart"=>Ok(Self{zone:Zone::Start,mode_id:ModeID::BONUS}),
"BonusFinish"=>Ok(Self{zone:Zone::Finish,mode_id:ModeID::BONUS}),
"BonusAnticheat"=>Ok(Self{zone:Zone::Anticheat,mode_id:ModeID::BONUS}),
other=>{
let everything_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Start$|^BonusStart(\d+)$|^Bonus(\d+)Finish$|^BonusFinish(\d+)$|^Bonus(\d+)Anticheat$|^BonusAnticheat(\d+)$");
if let Some(captures)=everything_pattern.captures(other){
if let Some(mode_id)=captures.get(1).or(captures.get(2)){
return Ok(Self{
zone:Zone::Start,
mode_id:ModeID(mode_id.as_str().parse().map_err(IDParseError::ParseInt)?),
});
}
if let Some(mode_id)=captures.get(3).or(captures.get(4)){
return Ok(Self{
zone:Zone::Finish,
mode_id:ModeID(mode_id.as_str().parse().map_err(IDParseError::ParseInt)?),
});
}
if let Some(mode_id)=captures.get(5).or(captures.get(6)){
return Ok(Self{
zone:Zone::Anticheat,
mode_id:ModeID(mode_id.as_str().parse().map_err(IDParseError::ParseInt)?),
});
}
}
Err(IDParseError::NoCaptures)
}
}
}
}
impl std::fmt::Display for ModeElement{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
match self{
ModeElement{zone:Zone::Start,mode_id:ModeID::MAIN}=>write!(f,"MapStart"),
ModeElement{zone:Zone::Start,mode_id:ModeID::BONUS}=>write!(f,"BonusStart"),
ModeElement{zone:Zone::Start,mode_id:ModeID(mode_id)}=>write!(f,"Bonus{mode_id}Start"),
ModeElement{zone:Zone::Finish,mode_id:ModeID::MAIN}=>write!(f,"MapFinish"),
ModeElement{zone:Zone::Finish,mode_id:ModeID::BONUS}=>write!(f,"BonusFinish"),
ModeElement{zone:Zone::Finish,mode_id:ModeID(mode_id)}=>write!(f,"Bonus{mode_id}Finish"),
ModeElement{zone:Zone::Anticheat,mode_id:ModeID::MAIN}=>write!(f,"MapAnticheat"),
ModeElement{zone:Zone::Anticheat,mode_id:ModeID::BONUS}=>write!(f,"BonusAnticheat"),
ModeElement{zone:Zone::Anticheat,mode_id:ModeID(mode_id)}=>write!(f,"Bonus{mode_id}Anticheat"),
}
}
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
struct StageID(u64);
impl StageID{
const FIRST:Self=Self(1);
}
enum StageElementBehaviour{
Teleport,
Spawn,
}
struct StageElement{
stage_id:StageID,
behaviour:StageElementBehaviour,
}
// Parse a SpawnTeleport from a part name
impl std::str::FromStr for StageElement{
type Err=IDParseError;
fn from_str(s:&str)->Result<Self,Self::Err>{
// Trigger ForceTrigger Teleport ForceTeleport SpawnAt ForceSpawnAt
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^(?:Force)?(Teleport|SpawnAt|Trigger)(\d+)$");
if let Some(captures)=bonus_start_pattern.captures(s){
return Ok(StageElement{
behaviour:StageElementBehaviour::Teleport,
stage_id:StageID(captures[1].parse().map_err(IDParseError::ParseInt)?),
});
}
// Spawn
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^Spawn(\d+)$");
if let Some(captures)=bonus_finish_pattern.captures(s){
return Ok(StageElement{
behaviour:StageElementBehaviour::Spawn,
stage_id:StageID(captures[1].parse().map_err(IDParseError::ParseInt)?),
});
}
Err(IDParseError::NoCaptures)
}
}
impl std::fmt::Display for StageElement{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
match self{
StageElement{behaviour:StageElementBehaviour::Spawn,stage_id:StageID(stage_id)}=>write!(f,"Spawn{stage_id}"),
StageElement{behaviour:StageElementBehaviour::Teleport,stage_id:StageID(stage_id)}=>write!(f,"Teleport{stage_id}"),
}
}
}
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
struct WormholeID(u64);
enum WormholeBehaviour{
In,
Out,
}
struct WormholeElement{
behaviour:WormholeBehaviour,
wormhole_id:WormholeID,
}
// Parse a Wormhole from a part name
impl std::str::FromStr for WormholeElement{
type Err=IDParseError;
fn from_str(s:&str)->Result<Self,Self::Err>{
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^WormholeIn(\d+)$");
if let Some(captures)=bonus_start_pattern.captures(s){
return Ok(Self{
behaviour:WormholeBehaviour::In,
wormhole_id:WormholeID(captures[1].parse().map_err(IDParseError::ParseInt)?),
});
}
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^WormholeOut(\d+)$");
if let Some(captures)=bonus_finish_pattern.captures(s){
return Ok(Self{
behaviour:WormholeBehaviour::Out,
wormhole_id:WormholeID(captures[1].parse().map_err(IDParseError::ParseInt)?),
});
}
Err(IDParseError::NoCaptures)
}
}
impl std::fmt::Display for WormholeElement{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
match self{
WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id:WormholeID(wormhole_id)}=>write!(f,"WormholeIn{wormhole_id}"),
WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id:WormholeID(wormhole_id)}=>write!(f,"WormholeOut{wormhole_id}"),
}
}
}
/// Count various map elements
#[derive(Default)]
struct Counts<'a>{
mode_start_counts:HashMap<ModeID,Vec<&'a str>>,
mode_finish_counts:HashMap<ModeID,Vec<&'a str>>,
mode_anticheat_counts:HashMap<ModeID,Vec<&'a str>>,
teleport_counts:HashMap<StageID,Vec<&'a str>>,
spawn_counts:HashMap<StageID,u64>,
wormhole_in_counts:HashMap<WormholeID,u64>,
wormhole_out_counts:HashMap<WormholeID,u64>,
}
pub struct ModelInfo<'a>{
model_class:&'a str,
model_name:&'a str,
map_info:MapInfo<'a>,
counts:Counts<'a>,
}
pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_dom_weak::Instance)->ModelInfo<'a>{
// extract model info
let map_info=get_mapinfo(dom,model_instance);
// count objects (default count is 0)
let mut counts=Counts::default();
let db=rbx_reflection_database::get();
let base_part=&db.classes["BasePart"];
let base_parts=dom.descendants_of(model_instance.referent()).filter(|&instance|
db.classes.get(instance.class.as_str()).is_some_and(|class|
db.has_superclass(class,base_part)
)
);
for instance in base_parts{
// Zones
match instance.name.parse(){
Ok(ModeElement{zone:Zone::Start,mode_id})=>counts.mode_start_counts.entry(mode_id).or_default().push(instance.name.as_str()),
Ok(ModeElement{zone:Zone::Finish,mode_id})=>counts.mode_finish_counts.entry(mode_id).or_default().push(instance.name.as_str()),
Ok(ModeElement{zone:Zone::Anticheat,mode_id})=>counts.mode_anticheat_counts.entry(mode_id).or_default().push(instance.name.as_str()),
Err(_)=>(),
}
// Spawns & Teleports
match instance.name.parse(){
Ok(StageElement{behaviour:StageElementBehaviour::Teleport,stage_id})=>counts.teleport_counts.entry(stage_id).or_default().push(instance.name.as_str()),
Ok(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id})=>*counts.spawn_counts.entry(stage_id).or_insert(0)+=1,
Err(_)=>(),
}
// Wormholes
match instance.name.parse(){
Ok(WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id})=>*counts.wormhole_in_counts.entry(wormhole_id).or_insert(0)+=1,
Ok(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id})=>*counts.wormhole_out_counts.entry(wormhole_id).or_insert(0)+=1,
Err(_)=>(),
}
}
ModelInfo{
model_class:model_instance.class.as_str(),
model_name:model_instance.name.as_str(),
map_info,
counts,
}
}
// check if an observed string matches an expected string
pub struct StringCheck<'a,T,Str>(Result<T,StringCheckContext<'a,Str>>);
pub struct StringCheckContext<'a,Str>{
observed:&'a str,
expected:Str,
}
impl<'a,Str> StringCheckContext<'a,Str>
where
&'a str:PartialEq<Str>,
{
/// Compute the StringCheck, passing through the provided value on success.
fn check<T>(self,value:T)->StringCheck<'a,T,Str>{
if self.observed==self.expected{
StringCheck(Ok(value))
}else{
StringCheck(Err(self))
}
}
}
impl<Str:std::fmt::Display> std::fmt::Display for StringCheckContext<'_,Str>{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"expected: {}, observed: {}",self.expected,self.observed)
}
}
// check if a string is empty
pub struct StringEmpty;
impl std::fmt::Display for StringEmpty{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"Empty string")
}
}
fn check_empty(value:&str)->Result<&str,StringEmpty>{
(!value.is_empty()).then_some(value).ok_or(StringEmpty)
}
// check for duplicate objects
pub struct DuplicateCheckContext<ID,T>(HashMap<ID,T>);
pub struct DuplicateCheck<ID,T>(Result<(),DuplicateCheckContext<ID,T>>);
impl<ID,T> DuplicateCheckContext<ID,T>{
/// Compute the DuplicateCheck using the contents predicate.
fn check(self,f:impl Fn(&T)->bool)->DuplicateCheck<ID,T>{
let Self(mut set)=self;
// remove correct entries
set.retain(|_,c|f(c));
// if any entries remain, they are incorrect
if set.is_empty(){
DuplicateCheck(Ok(()))
}else{
DuplicateCheck(Err(Self(set)))
}
}
}
// Check that there are no items which do not have a matching item in a reference set
pub struct SetDifferenceCheckContextAllowNone<ID,T>{
extra:HashMap<ID,T>,
}
// Check that there is at least one matching item for each item in a reference set, and no extra items
pub struct SetDifferenceCheckContextAtLeastOne<ID,T>{
extra:HashMap<ID,T>,
missing:HashSet<ID>,
}
pub struct SetDifferenceCheck<Context>(Result<(),Context>);
impl<ID,T> SetDifferenceCheckContextAllowNone<ID,T>{
fn new(initial_set:HashMap<ID,T>)->Self{
Self{
extra:initial_set,
}
}
}
impl<ID:Eq+std::hash::Hash,T> SetDifferenceCheckContextAllowNone<ID,T>{
/// Compute the SetDifferenceCheck result for the specified reference set.
fn check<U>(mut self,reference_set:&HashMap<ID,U>)->SetDifferenceCheck<Self>{
// remove correct entries
for id in reference_set.keys(){
self.extra.remove(id);
}
// if any entries remain, they are incorrect
if self.extra.is_empty(){
SetDifferenceCheck(Ok(()))
}else{
SetDifferenceCheck(Err(self))
}
}
}
impl<ID,T> SetDifferenceCheckContextAtLeastOne<ID,T>{
fn new(initial_set:HashMap<ID,T>)->Self{
Self{
extra:initial_set,
missing:HashSet::new(),
}
}
}
impl<ID:Copy+Eq+std::hash::Hash,T> SetDifferenceCheckContextAtLeastOne<ID,T>{
/// Compute the SetDifferenceCheck result for the specified reference set.
fn check<U>(mut self,reference_set:&HashMap<ID,U>)->SetDifferenceCheck<Self>{
// remove correct entries
for id in reference_set.keys(){
if self.extra.remove(id).is_none(){
// the set did not contain a required item. This is a fail
self.missing.insert(*id);
}
}
// if any entries remain, they are incorrect
if self.extra.is_empty()&&self.missing.is_empty(){
SetDifferenceCheck(Ok(()))
}else{
SetDifferenceCheck(Err(self))
}
}
}
/// Info lifted out of a fully compliant map
pub struct MapInfoOwned{
pub display_name:String,
pub creator:String,
pub game_id:GameID,
}
#[derive(Debug)]
pub enum IntoMapInfoOwnedError{
DisplayName(StringValueError),
Creator(StringValueError),
GameID(ParseGameIDError),
}
impl TryFrom<MapInfo<'_>> for MapInfoOwned{
type Error=IntoMapInfoOwnedError;
fn try_from(value:MapInfo<'_>)->Result<Self,Self::Error>{
Ok(Self{
display_name:value.display_name.map_err(IntoMapInfoOwnedError::DisplayName)?.to_owned(),
creator:value.creator.map_err(IntoMapInfoOwnedError::Creator)?.to_owned(),
game_id:value.game_id.map_err(IntoMapInfoOwnedError::GameID)?,
})
}
}
// Named dummy types for readability
struct Exists;
struct Absent;
/// The result of every map check.
struct MapCheck<'a>{
// === METADATA CHECKS ===
// The root must be of class Model
model_class:StringCheck<'a,(),&'static str>,
// Model's name must be in snake case
model_name:StringCheck<'a,(),String>,
// Map must have a StringValue named DisplayName.
// Value must not be empty, must be in title case.
display_name:Result<Result<StringCheck<'a,&'a str,String>,StringEmpty>,StringValueError>,
// Map must have a StringValue named Creator.
// Value must not be empty.
creator:Result<Result<&'a str,StringEmpty>,StringValueError>,
// The prefix of the model's name must match the game it was submitted for.
// bhop_ for bhop, and surf_ for surf
game_id:Result<GameID,ParseGameIDError>,
// === MODE CHECKS ===
// MapStart must exist
mapstart:Result<Exists,Absent>,
// No duplicate map starts (including bonuses)
mode_start_counts:DuplicateCheck<ModeID,Vec<&'a str>>,
// At least one finish zone for each start zone, and no finishes with no start
mode_finish_counts:SetDifferenceCheck<SetDifferenceCheckContextAtLeastOne<ModeID,Vec<&'a str>>>,
// Check for dangling MapAnticheat zones (no associated MapStart)
mode_anticheat_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<ModeID,Vec<&'a str>>>,
// Spawn1 must exist
spawn1:Result<Exists,Absent>,
// Check for dangling Teleport# (no associated Spawn#)
teleport_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<StageID,Vec<&'a str>>>,
// No duplicate Spawn#
spawn_counts:DuplicateCheck<StageID,u64>,
// Check for dangling WormholeIn# (no associated WormholeOut#)
wormhole_in_counts:SetDifferenceCheck<SetDifferenceCheckContextAtLeastOne<WormholeID,u64>>,
// No duplicate WormholeOut# (duplicate WormholeIn# ok)
// No dangling WormholeOut#
wormhole_out_counts:DuplicateCheck<WormholeID,u64>,
}
impl<'a> ModelInfo<'a>{
fn check(self)->MapCheck<'a>{
// Check class is exactly "Model"
let model_class=StringCheckContext{
observed:self.model_class,
expected:"Model",
}.check(());
// Check model name is snake case
let model_name=StringCheckContext{
observed:self.model_name,
expected:self.model_name.to_snake_case(),
}.check(());
// Check display name is not empty and has title case
let display_name=self.map_info.display_name.map(|display_name|{
check_empty(display_name).map(|display_name|StringCheckContext{
observed:display_name,
expected:display_name.to_title_case(),
}.check(display_name))
});
// Check Creator is not empty
let creator=self.map_info.creator.map(check_empty);
// Check GameID (model name was prefixed with bhop_ surf_ etc)
let game_id=self.map_info.game_id;
// MapStart must exist
let mapstart=if self.counts.mode_start_counts.contains_key(&ModeID::MAIN){
Ok(Exists)
}else{
Err(Absent)
};
// Spawn1 must exist
let spawn1=if self.counts.spawn_counts.contains_key(&StageID::FIRST){
Ok(Exists)
}else{
Err(Absent)
};
// Check that at least one finish zone exists for each start zone.
// This also checks that there are no finish zones without a corresponding start zone.
let mode_finish_counts=SetDifferenceCheckContextAtLeastOne::new(self.counts.mode_finish_counts)
.check(&self.counts.mode_start_counts);
// Check that there are no anticheat zones without a corresponding start zone.
// Modes are allowed to have 0 anticheat zones.
let mode_anticheat_counts=SetDifferenceCheckContextAllowNone::new(self.counts.mode_anticheat_counts)
.check(&self.counts.mode_start_counts);
// There must be exactly one start zone for every mode in the map.
let mode_start_counts=DuplicateCheckContext(self.counts.mode_start_counts).check(|c|1<c.len());
// Check that there are no Teleports without a corresponding Spawn.
// Spawns are allowed to have 0 Teleports.
let teleport_counts=SetDifferenceCheckContextAllowNone::new(self.counts.teleport_counts)
.check(&self.counts.spawn_counts);
// There must be exactly one of any perticular spawn id in the map.
let spawn_counts=DuplicateCheckContext(self.counts.spawn_counts).check(|&c|1<c);
// Check that at least one WormholeIn exists for each WormholeOut.
// This also checks that there are no WormholeIn without a corresponding WormholeOut.
let wormhole_in_counts=SetDifferenceCheckContextAtLeastOne::new(self.counts.wormhole_in_counts)
.check(&self.counts.wormhole_out_counts);
// There must be exactly one of any perticular wormhole out id in the map.
let wormhole_out_counts=DuplicateCheckContext(self.counts.wormhole_out_counts).check(|&c|1<c);
MapCheck{
model_class,
model_name,
display_name,
creator,
game_id,
mapstart,
mode_start_counts,
mode_finish_counts,
mode_anticheat_counts,
spawn1,
teleport_counts,
spawn_counts,
wormhole_in_counts,
wormhole_out_counts,
}
}
}
impl MapCheck<'_>{
fn result(self)->Result<MapInfoOwned,Result<MapCheckList,serde_json::Error>>{
match self{
MapCheck{
model_class:StringCheck(Ok(())),
model_name:StringCheck(Ok(())),
display_name:Ok(Ok(StringCheck(Ok(display_name)))),
creator:Ok(Ok(creator)),
game_id:Ok(game_id),
mapstart:Ok(Exists),
mode_start_counts:DuplicateCheck(Ok(())),
mode_finish_counts:SetDifferenceCheck(Ok(())),
mode_anticheat_counts:SetDifferenceCheck(Ok(())),
spawn1:Ok(Exists),
teleport_counts:SetDifferenceCheck(Ok(())),
spawn_counts:DuplicateCheck(Ok(())),
wormhole_in_counts:SetDifferenceCheck(Ok(())),
wormhole_out_counts:DuplicateCheck(Ok(())),
}=>{
Ok(MapInfoOwned{
display_name:display_name.to_owned(),
creator:creator.to_owned(),
game_id,
})
},
other=>Err(other.itemize()),
}
}
}
struct Separated<F>{
f:F,
separator:&'static str,
}
impl<F> Separated<F>{
fn new(separator:&'static str,f:F)->Self{
Self{separator,f}
}
}
impl<F,I,D> std::fmt::Display for Separated<F>
where
D:std::fmt::Display,
I:IntoIterator<Item=D>,
F:Fn()->I,
{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
let mut it=(self.f)().into_iter();
if let Some(first)=it.next(){
write!(f,"{first}")?;
for item in it{
write!(f,"{}{item}",self.separator)?;
}
}
Ok(())
}
}
struct Duplicates<D>{
display:D,
duplicates:usize,
}
impl<D> Duplicates<D>{
fn new(display:D,duplicates:usize)->Self{
Self{
display,
duplicates,
}
}
}
impl<D:std::fmt::Display> std::fmt::Display for Duplicates<D>{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{} ({} duplicates)",self.display,self.duplicates)
}
}
#[derive(serde::Serialize)]
struct CheckSummary{
name:&'static str,
summary:String,
passed:bool,
details:serde_json::Value,
}
impl CheckSummary{
const fn passed(name:&'static str)->Self{
Self{
name,
summary:String::new(),
passed:true,
details:serde_json::Value::Null,
}
}
}
macro_rules! summary{
($name:literal,$summary:expr,$details:expr)=>{
CheckSummary{
name:$name,
summary:$summary,
passed:false,
details:serde_json::to_value($details)?,
}
};
}
macro_rules! summary_format{
($name:literal,$fmt:literal,$details:expr)=>{
CheckSummary{
name:$name,
summary:format!($fmt),
passed:false,
details:serde_json::to_value($details)?,
}
};
}
// Generate an error message for each observed issue separated by newlines.
// This defines MapCheck.to_string() which is used in MapCheck.result()
impl MapCheck<'_>{
fn itemize(&self)->Result<MapCheckList,serde_json::Error>{
let model_class=match &self.model_class{
StringCheck(Ok(()))=>CheckSummary::passed("ModelClass"),
StringCheck(Err(context))=>summary_format!("ModelClass","Invalid model class: {context}",()),
};
let model_name=match &self.model_name{
StringCheck(Ok(()))=>CheckSummary::passed("ModelName"),
StringCheck(Err(context))=>summary_format!("ModelName","Model name must have snake_case: {context}",()),
};
let display_name=match &self.display_name{
Ok(Ok(StringCheck(Ok(_))))=>CheckSummary::passed("DisplayName"),
Ok(Ok(StringCheck(Err(context))))=>summary_format!("DisplayName","DisplayName must have Title Case: {context}",()),
Ok(Err(context))=>summary_format!("DisplayName","Invalid DisplayName: {context}",()),
Err(StringValueError::ObjectNotFound)=>summary!("DisplayName","Missing DisplayName StringValue".to_owned(),()),
Err(StringValueError::ValueNotSet)=>summary!("DisplayName","DisplayName Value not set".to_owned(),()),
Err(StringValueError::NonStringValue)=>summary!("DisplayName","DisplayName Value is not a String".to_owned(),()),
};
let creator=match &self.creator{
Ok(Ok(_))=>CheckSummary::passed("Creator"),
Ok(Err(context))=>summary_format!("Creator","Invalid Creator: {context}",()),
Err(StringValueError::ObjectNotFound)=>summary!("Creator","Missing Creator StringValue".to_owned(),()),
Err(StringValueError::ValueNotSet)=>summary!("Creator","Creator Value not set".to_owned(),()),
Err(StringValueError::NonStringValue)=>summary!("Creator","Creator Value is not a String".to_owned(),()),
};
let game_id=match &self.game_id{
Ok(_)=>CheckSummary::passed("GameID"),
Err(ParseGameIDError)=>summary!("GameID","Model name must be prefixed with bhop_ surf_ or flytrials_".to_owned(),()),
};
let mapstart=match &self.mapstart{
Ok(Exists)=>CheckSummary::passed("MapStart"),
Err(Absent)=>summary_format!("MapStart","Model has no MapStart",()),
};
let duplicate_start=match &self.mode_start_counts{
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateStart"),
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
let context=Separated::new(", ",||context.iter().map(|(&mode_id,names)|
Duplicates::new(ModeElement{zone:Zone::Start,mode_id},names.len())
));
summary_format!("DuplicateStart","Duplicate start zones: {context}",())
}
};
let (extra_finish,missing_finish)=match &self.mode_finish_counts{
SetDifferenceCheck(Ok(()))=>(CheckSummary::passed("ExtraFinish"),CheckSummary::passed("MissingFinish")),
SetDifferenceCheck(Err(context))=>(
if context.extra.is_empty(){
CheckSummary::passed("ExtraFinish")
}else{
let plural=if context.extra.len()==1{"zone"}else{"zones"};
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_names)|
ModeElement{zone:Zone::Finish,mode_id}
));
summary_format!("ExtraFinish","No matching start zone for finish {plural}: {context}",())
},
if context.missing.is_empty(){
CheckSummary::passed("MissingFinish")
}else{
let plural=if context.missing.len()==1{"zone"}else{"zones"};
let context=Separated::new(", ",||context.missing.iter().map(|&mode_id|
ModeElement{zone:Zone::Finish,mode_id}
));
summary_format!("MissingFinish","Missing finish {plural}: {context}",())
}
),
};
let dangling_anticheat=match &self.mode_anticheat_counts{
SetDifferenceCheck(Ok(()))=>CheckSummary::passed("DanglingAnticheat"),
SetDifferenceCheck(Err(context))=>{
if context.extra.is_empty(){
CheckSummary::passed("DanglingAnticheat")
}else{
let plural=if context.extra.len()==1{"zone"}else{"zones"};
let context=Separated::new(", ",||context.extra.iter().map(|(&mode_id,_names)|
ModeElement{zone:Zone::Anticheat,mode_id}
));
summary_format!("DanglingAnticheat","No matching start zone for anticheat {plural}: {context}",())
}
}
};
let spawn1=match &self.spawn1{
Ok(Exists)=>CheckSummary::passed("Spawn1"),
Err(Absent)=>summary_format!("Spawn1","Model has no Spawn1",()),
};
let dangling_teleport=match &self.teleport_counts{
SetDifferenceCheck(Ok(()))=>CheckSummary::passed("DanglingTeleport"),
SetDifferenceCheck(Err(context))=>{
let unique_names:HashSet<_>=context.extra.values().flat_map(|names|names.iter().copied()).collect();
let plural=if unique_names.len()==1{"object"}else{"objects"};
let context=Separated::new(", ",||&unique_names);
summary_format!("DanglingTeleport","No matching Spawn for {plural}: {context}",())
}
};
let duplicate_spawns=match &self.spawn_counts{
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateSpawn"),
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
let context=Separated::new(", ",||context.iter().map(|(&stage_id,&names)|
Duplicates::new(StageElement{behaviour:StageElementBehaviour::Spawn,stage_id},names as usize)
));
summary_format!("DuplicateSpawn","Duplicate Spawn: {context}",())
}
};
let (extra_wormhole_in,missing_wormhole_in)=match &self.wormhole_in_counts{
SetDifferenceCheck(Ok(()))=>(CheckSummary::passed("ExtraWormholeIn"),CheckSummary::passed("MissingWormholeIn")),
SetDifferenceCheck(Err(context))=>(
if context.extra.is_empty(){
CheckSummary::passed("ExtraWormholeIn")
}else{
let context=Separated::new(", ",||context.extra.iter().map(|(&wormhole_id,_names)|
WormholeElement{behaviour:WormholeBehaviour::In,wormhole_id}
));
summary_format!("ExtraWormholeIn","WormholeIn with no matching WormholeOut: {context}",())
},
if context.missing.is_empty(){
CheckSummary::passed("MissingWormholeIn")
}else{
// This counts WormholeIn objects, but
// flipped logic is easier to understand
let context=Separated::new(", ",||context.missing.iter().map(|&wormhole_id|
WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id}
));
summary_format!("MissingWormholeIn","WormholeOut with no matching WormholeIn: {context}",())
}
)
};
let duplicate_wormhole_out=match &self.wormhole_out_counts{
DuplicateCheck(Ok(()))=>CheckSummary::passed("DuplicateWormholeOut"),
DuplicateCheck(Err(DuplicateCheckContext(context)))=>{
let context=Separated::new(", ",||context.iter().map(|(&wormhole_id,&names)|
Duplicates::new(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id},names as usize)
));
summary_format!("DuplicateWormholeOut","Duplicate WormholeOut: {context}",())
}
};
Ok(MapCheckList{checks:Box::new([
model_class,
model_name,
display_name,
creator,
game_id,
mapstart,
duplicate_start,
extra_finish,
missing_finish,
dangling_anticheat,
spawn1,
dangling_teleport,
duplicate_spawns,
extra_wormhole_in,
missing_wormhole_in,
duplicate_wormhole_out,
])})
}
}
#[derive(serde::Serialize)]
struct MapCheckList{
checks:Box<[CheckSummary;16]>,
}
impl MapCheckList{
fn summary(&self)->String{
Separated::new("; ",||self.checks.iter().filter_map(|check|
(!check.passed).then_some(check.summary.as_str())
)).to_string()
}
}
pub struct Summary{
pub summary:String,
pub json:serde_json::Value,
}
pub struct CheckReportAndVersion{
pub status:Result<MapInfoOwned,Summary>,
pub version:u64,
}
impl crate::message_handler::MessageHandler{
pub async fn check_inner(&self,check_info:CheckRequest)->Result<CheckReportAndVersion,Error>{
// discover asset creator and latest version
let info=self.cloud_context.get_asset_info(
rbx_asset::cloud::GetAssetLatestRequest{asset_id:check_info.ModelID}
).await.map_err(Error::ModelInfoDownload)?;
// reject models created by a group
let rbx_asset::cloud::Creator::userId(_user_id)=info.creationContext.creator else{
return Err(Error::CreatorTypeMustBeUser);
};
// parse model version string
let version=info.revisionId;
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
asset_id:check_info.ModelID,
version,
}).await.map_err(Error::Download)?;
// decode dom (slow!)
let dom=maybe_gzip.read_with(read_dom,read_dom).map_err(Error::ModelFileDecode)?;
// extract the root instance
let model_instance=get_root_instance(&dom).map_err(Error::GetRootInstance)?;
// skip checks
if check_info.SkipChecks{
// extract required fields
let map_info=get_mapinfo(&dom,model_instance);
let map_info_owned=map_info.try_into().map_err(Error::IntoMapInfoOwned)?;
let status=Ok(map_info_owned);
// return early
return Ok(CheckReportAndVersion{status,version});
}
// extract information from the model
let model_info=get_model_info(&dom,model_instance);
// convert the model information into a structured report
let map_check=model_info.check();
// check the report, generate an error message if it fails the check
let status=match map_check.result(){
Ok(map_info)=>Ok(map_info),
Err(Ok(summary))=>Err(Summary{
summary:summary.summary(),
json:serde_json::to_value(&summary).map_err(Error::ToJsonValue)?,
}),
Err(Err(e))=>return Err(Error::ToJsonValue(e)),
};
Ok(CheckReportAndVersion{status,version})
}
}

@ -1,67 +0,0 @@
use crate::check::CheckReportAndVersion;
use crate::nats_types::CheckMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Check(crate::check::Error),
ApiActionMapfixCheck(submissions_api::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
pub async fn check_mapfix(&self,check_info:CheckMapfixRequest)->Result<(),Error>{
let mapfix_id=check_info.MapfixID;
let check_result=self.check_inner(check_info.into()).await;
// update the mapfix depending on the result
match check_result{
Ok(CheckReportAndVersion{status:Ok(map_info),version})=>{
self.api.action_mapfix_submitted(
submissions_api::types::ActionMapfixSubmittedRequest{
MapfixID:mapfix_id,
ModelVersion:version,
DisplayName:map_info.display_name,
Creator:map_info.creator,
GameID:map_info.game_id.into(),
}
).await.map_err(Error::ApiActionMapfixCheck)?;
// Do not proceed to request changes
return Ok(());
},
// update the mapfix model status to request changes
Ok(CheckReportAndVersion{status:Err(report),..})=>self.api.create_mapfix_audit_error(
submissions_api::types::CreateMapfixAuditErrorRequest{
MapfixID:mapfix_id,
ErrorMessage:report.summary,
}
).await.map_err(Error::ApiActionMapfixCheck)?,
// update the mapfix model status to request changes
Err(e)=>{
// log error
println!("[check_mapfix] Error: {e}");
self.api.create_mapfix_audit_error(
submissions_api::types::CreateMapfixAuditErrorRequest{
MapfixID:mapfix_id,
ErrorMessage:e.to_string(),
}
).await.map_err(Error::ApiActionMapfixCheck)?;
},
}
self.api.action_mapfix_request_changes(
submissions_api::types::ActionMapfixRequestChangesRequest{
MapfixID:mapfix_id,
}
).await.map_err(Error::ApiActionMapfixCheck)?;
Ok(())
}
}

@ -1,68 +0,0 @@
use crate::check::CheckReportAndVersion;
use crate::nats_types::CheckSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Check(crate::check::Error),
ApiActionSubmissionCheck(submissions_api::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
pub async fn check_submission(&self,check_info:CheckSubmissionRequest)->Result<(),Error>{
let submission_id=check_info.SubmissionID;
let check_result=self.check_inner(check_info.into()).await;
// update the submission depending on the result
match check_result{
// update the submission model status to submitted
Ok(CheckReportAndVersion{status:Ok(map_info),version})=>{
self.api.action_submission_submitted(
submissions_api::types::ActionSubmissionSubmittedRequest{
SubmissionID:submission_id,
ModelVersion:version,
DisplayName:map_info.display_name,
Creator:map_info.creator,
GameID:map_info.game_id.into(),
}
).await.map_err(Error::ApiActionSubmissionCheck)?;
// Do not proceed to request changes
return Ok(());
},
// update the submission model status to request changes
Ok(CheckReportAndVersion{status:Err(report),..})=>self.api.create_submission_audit_error(
submissions_api::types::CreateSubmissionAuditErrorRequest{
SubmissionID:submission_id,
ErrorMessage:report.summary,
}
).await.map_err(Error::ApiActionSubmissionCheck)?,
// update the submission model status to request changes
Err(e)=>{
// log error
println!("[check_submission] Error: {e}");
self.api.create_submission_audit_error(
submissions_api::types::CreateSubmissionAuditErrorRequest{
SubmissionID:submission_id,
ErrorMessage:e.to_string(),
}
).await.map_err(Error::ApiActionSubmissionCheck)?;
},
}
self.api.action_submission_request_changes(
submissions_api::types::ActionSubmissionRequestChangesRequest{
SubmissionID:submission_id,
}
).await.map_err(Error::ApiActionSubmissionCheck)?;
Ok(())
}
}

@ -1,73 +0,0 @@
use crate::download::download_asset_version;
use crate::rbx_util::{get_root_instance,get_mapinfo,read_dom,MapInfo,ReadDomError,GetRootInstanceError,GameID};
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
CreatorTypeMustBeUser,
ModelInfoDownload(rbx_asset::cloud::GetError),
Download(crate::download::Error),
ModelFileDecode(ReadDomError),
GetRootInstance(GetRootInstanceError),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
#[allow(nonstandard_style)]
pub struct CreateRequest{
pub ModelID:u64,
}
#[allow(nonstandard_style)]
pub struct CreateResult{
pub AssetOwner:u64,
pub DisplayName:Option<String>,
pub Creator:Option<String>,
pub GameID:Option<GameID>,
pub AssetVersion:u64,
}
impl crate::message_handler::MessageHandler{
pub async fn create_inner(&self,create_info:CreateRequest)->Result<CreateResult,Error>{
// discover asset creator and latest version
let info=self.cloud_context.get_asset_info(
rbx_asset::cloud::GetAssetLatestRequest{asset_id:create_info.ModelID}
).await.map_err(Error::ModelInfoDownload)?;
// reject models created by a group
let rbx_asset::cloud::Creator::userId(user_id)=info.creationContext.creator else{
return Err(Error::CreatorTypeMustBeUser);
};
let asset_version=info.revisionId;
// download the map model
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
asset_id:create_info.ModelID,
version:asset_version,
}).await.map_err(Error::Download)?;
// decode dom (slow!)
let dom=maybe_gzip.read_with(read_dom,read_dom).map_err(Error::ModelFileDecode)?;
// extract the root instance
let model_instance=get_root_instance(&dom).map_err(Error::GetRootInstance)?;
// parse create fields out of asset
let MapInfo{
display_name,
creator,
game_id,
}=get_mapinfo(&dom,model_instance);
Ok(CreateResult{
AssetOwner:user_id,
DisplayName:display_name.ok().map(ToOwned::to_owned),
Creator:creator.ok().map(ToOwned::to_owned),
GameID:game_id.ok(),
AssetVersion:asset_version,
})
}
}

@ -1,57 +0,0 @@
use crate::nats_types::CreateMapfixRequest;
use crate::create::CreateRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Create(crate::create::Error),
ApiActionMapfixCreate(submissions_api::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
async fn create_mapfix_inner(&self,create_info:CreateMapfixRequest)->Result<(),Error>{
// call deduplicated inner code
let create_request=self.create_inner(CreateRequest{
ModelID:create_info.ModelID,
}).await.map_err(Error::Create)?;
// call create on api
self.api.create_mapfix(submissions_api::types::CreateMapfixRequest{
OperationID:create_info.OperationID,
AssetOwner:create_request.AssetOwner as i64,
DisplayName:create_request.DisplayName.as_deref().unwrap_or_default(),
Creator:create_request.Creator.as_deref().unwrap_or_default(),
// not great TODO: make this great
GameID:create_request.GameID.unwrap_or(crate::rbx_util::GameID::Bhop).into(),
AssetID:create_info.ModelID,
AssetVersion:create_request.AssetVersion,
TargetAssetID:create_info.TargetAssetID,
Description:create_info.Description.as_str(),
}).await.map_err(Error::ApiActionMapfixCreate)?;
Ok(())
}
pub async fn create_mapfix(&self,create_info:CreateMapfixRequest)->Result<(),submissions_api::Error>{
let operation_id=create_info.OperationID;
let create_result=self.create_mapfix_inner(create_info).await;
if let Err(e)=create_result{
// log error
println!("[create_mapfix] Error: {e}");
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
OperationID:operation_id,
StatusMessage:e.to_string(),
}).await?;
}
Ok(())
}
}

@ -1,71 +0,0 @@
use crate::nats_types::CreateSubmissionRequest;
use crate::create::CreateRequest;
use crate::rbx_util::GameID;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Create(crate::create::Error),
ApiActionSubmissionCreate(submissions_api::Error),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl crate::message_handler::MessageHandler{
async fn create_submission_inner(&self,create_info:CreateSubmissionRequest)->Result<(),Error>{
let create_request=self.create_inner(CreateRequest{
ModelID:create_info.ModelID,
}).await.map_err(Error::Create)?;
// grab values from submission form, otherwise try to fill blanks from map data
let display_name=if create_info.DisplayName.is_empty(){
create_request.DisplayName.as_deref().unwrap_or_default()
}else{
create_info.DisplayName.as_str()
};
let creator=if create_info.Creator.is_empty(){
create_request.Creator.as_deref().unwrap_or_default()
}else{
create_info.Creator.as_str()
};
let game_id=create_info.GameID.try_into().ok().or(create_request.GameID).unwrap_or(GameID::Bhop);
// call create on api
self.api.create_submission(submissions_api::types::CreateSubmissionRequest{
OperationID:create_info.OperationID,
AssetOwner:create_request.AssetOwner as i64,
DisplayName:display_name,
Creator:creator,
GameID:game_id.into(),
AssetID:create_info.ModelID,
AssetVersion:create_request.AssetVersion,
Status:create_info.Status,
Roles:create_info.Roles,
}).await.map_err(Error::ApiActionSubmissionCreate)?;
Ok(())
}
pub async fn create_submission(&self,create_info:CreateSubmissionRequest)->Result<(),submissions_api::Error>{
let operation_id=create_info.OperationID;
let create_result=self.create_submission_inner(create_info).await;
if let Err(e)=create_result{
// log error
println!("[create_submission] Error: {e}");
self.api.action_operation_failed(submissions_api::types::ActionOperationFailedRequest{
OperationID:operation_id,
StatusMessage:e.to_string(),
}).await?;
}
Ok(())
}
}

@ -1,26 +0,0 @@
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
ModelLocationDownload(rbx_asset::cloud::GetError),
NonFreeModel,
ModelFileDownload(rbx_asset::cloud::GetError),
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
pub async fn download_asset_version(cloud_context:&rbx_asset::cloud::Context,request:rbx_asset::cloud::GetAssetVersionRequest)->Result<rbx_asset::types::MaybeGzippedBytes,Error>{
// download the location of the map model
let location=cloud_context.get_asset_version_location(request).await.map_err(Error::ModelLocationDownload)?;
// if the location does not exist, you are not allowed to download it
let location=location.location.ok_or(Error::NonFreeModel)?;
// download the map model
let maybe_gzip=cloud_context.get_asset(&location).await.map_err(Error::ModelFileDownload)?;
Ok(maybe_gzip)
}

@ -1,15 +1,9 @@
use futures::StreamExt;
mod rbx_util;
mod message_handler;
mod nats_types;
mod download;
mod check;
mod check_mapfix;
mod check_submission;
mod create;
mod create_mapfix;
mod create_submission;
mod types;
mod uploader;
mod upload_mapfix;
mod upload_submission;
mod validator;
@ -23,7 +17,6 @@ pub enum StartupError{
NatsConnect(async_nats::ConnectError),
NatsGetStream(async_nats::jetstream::context::GetStreamError),
NatsConsumer(async_nats::jetstream::stream::ConsumerError),
NatsConsumerUpdate(async_nats::jetstream::stream::ConsumerUpdateError),
NatsStream(async_nats::jetstream::consumer::StreamError),
}
impl std::fmt::Display for StartupError{
@ -42,15 +35,12 @@ async fn main()->Result<(),StartupError>{
"None"=>None,
_=>Some(s.parse().expect("ROBLOX_GROUP_ID int parse")),
},
Err(e)=>panic!("{e}: ROBLOX_GROUP_ID env required"),
Err(e)=>Err(e).expect("ROBLOX_GROUP_ID env required"),
};
// create / upload models through STRAFESNET_CI2 account
// talk to roblox through STRAFESNET_CI2 account
let cookie=std::env::var("RBXCOOKIE").expect("RBXCOOKIE env required");
let cookie_context=rbx_asset::cookie::Context::new(rbx_asset::cookie::Cookie::new(cookie));
// download models through cloud api
let api_key=std::env::var("RBX_API_KEY").expect("RBX_API_KEY env required");
let cloud_context=rbx_asset::cloud::Context::new(rbx_asset::cloud::ApiKey::new(api_key));
let cookie_context=rbx_asset::cookie::CookieContext::new(rbx_asset::cookie::Cookie::new(cookie));
// maps-service api
let api_host_internal=std::env::var("API_HOST_INTERNAL").expect("API_HOST_INTERNAL env required");
@ -59,35 +49,20 @@ async fn main()->Result<(),StartupError>{
// nats
let nats_host=std::env::var("NATS_HOST").expect("NATS_HOST env required");
let nats_fut=async{
const STREAM_NAME:&str="maptest";
const DURABLE_NAME:&str="validation";
const FILTER_SUBJECT:&str="maptest.>";
let nats_config=async_nats::jetstream::consumer::pull::Config{
name:Some(DURABLE_NAME.to_owned()),
durable_name:Some(DURABLE_NAME.to_owned()),
filter_subject:FILTER_SUBJECT.to_owned(),
..Default::default()
};
let nasty=async_nats::connect(nats_host).await.map_err(StartupError::NatsConnect)?;
// use nats jetstream
let stream=async_nats::jetstream::new(nasty)
.get_stream(STREAM_NAME).await.map_err(StartupError::NatsGetStream)?;
let consumer=stream.get_or_create_consumer(DURABLE_NAME,nats_config.clone()).await.map_err(StartupError::NatsConsumer)?;
// check if config matches expected config
if consumer.cached_info().config.filter_subject!=FILTER_SUBJECT{
stream.update_consumer(nats_config).await.map_err(StartupError::NatsConsumerUpdate)?;
}
// only need messages
consumer.messages().await.map_err(StartupError::NatsStream)
async_nats::jetstream::new(nasty)
.get_stream("maptest").await.map_err(StartupError::NatsGetStream)?
.get_or_create_consumer("validation",async_nats::jetstream::consumer::pull::Config{
name:Some("validation".to_owned()),
durable_name:Some("validation".to_owned()),
filter_subject:"maptest.>".to_owned(),
..Default::default()
}).await.map_err(StartupError::NatsConsumer)?
.messages().await.map_err(StartupError::NatsStream)
};
let message_handler=message_handler::MessageHandler::new(cloud_context,cookie_context,group_id,api);
let message_handler=message_handler::MessageHandler::new(cookie_context,group_id,api);
// Create a signal listener for SIGTERM
let mut sig_term=tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()).expect("Failed to create SIGTERM signal listener");
@ -109,7 +84,7 @@ async fn main()->Result<(),StartupError>{
Err(e)=>println!("[Validation] There was an error, oopsie! {e}"),
}
// explicitly call drop to make the move semantics and permit release more obvious
drop(permit);
core::mem::drop(permit);
});
}
};

@ -5,14 +5,10 @@ pub enum HandleMessageError{
DoubleAck(async_nats::Error),
Json(serde_json::Error),
UnknownSubject(String),
CreateMapfix(submissions_api::Error),
CreateSubmission(submissions_api::Error),
CheckMapfix(crate::check_mapfix::Error),
CheckSubmission(crate::check_submission::Error),
UploadMapfix(crate::upload_mapfix::Error),
UploadSubmission(crate::upload_submission::Error),
ValidateMapfix(crate::validate_mapfix::Error),
ValidateSubmission(crate::validate_submission::Error),
UploadMapfix(crate::upload_mapfix::UploadError),
UploadSubmission(crate::upload_submission::UploadError),
ValidateMapfix(crate::validate_mapfix::ValidateMapfixError),
ValidateSubmission(crate::validate_submission::ValidateSubmissionError),
}
impl std::fmt::Display for HandleMessageError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@ -28,38 +24,33 @@ fn from_slice<'a,T:serde::de::Deserialize<'a>>(slice:&'a [u8])->Result<T,HandleM
}
pub struct MessageHandler{
pub(crate) cloud_context:rbx_asset::cloud::Context,
pub(crate) cookie_context:rbx_asset::cookie::Context,
pub(crate) group_id:Option<u64>,
pub(crate) api:submissions_api::internal::Context,
upload_mapfix:crate::upload_mapfix::Uploader,
upload_submission:crate::upload_submission::Uploader,
validate_mapfix:crate::validate_mapfix::Validator,
validate_submission:crate::validate_submission::Validator,
}
impl MessageHandler{
pub fn new(
cloud_context:rbx_asset::cloud::Context,
cookie_context:rbx_asset::cookie::Context,
cookie_context:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api:submissions_api::internal::Context,
)->Self{
Self{
cloud_context,
cookie_context,
group_id,
api,
upload_mapfix:crate::upload_mapfix::Uploader::new(crate::uploader::Uploader::new(cookie_context.clone(),group_id,api.clone())),
upload_submission:crate::upload_submission::Uploader::new(crate::uploader::Uploader::new(cookie_context.clone(),group_id,api.clone())),
validate_mapfix:crate::validate_mapfix::Validator::new(crate::validator::Validator::new(cookie_context.clone(),api.clone())),
validate_submission:crate::validate_submission::Validator::new(crate::validator::Validator::new(cookie_context,api)),
}
}
pub async fn handle_message_result(&self,message_result:MessageResult)->Result<(),HandleMessageError>{
let message=message_result.map_err(HandleMessageError::Messages)?;
message.double_ack().await.map_err(HandleMessageError::DoubleAck)?;
match message.subject.as_str(){
"maptest.mapfixes.create"=>self.create_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::CreateMapfix),
"maptest.submissions.create"=>self.create_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::CreateSubmission),
"maptest.mapfixes.check"=>self.check_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::CheckMapfix),
"maptest.submissions.check"=>self.check_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::CheckSubmission),
"maptest.mapfixes.upload"=>self.upload_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadMapfix),
"maptest.submissions.upload"=>self.upload_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadSubmission),
"maptest.mapfixes.validate"=>self.validate_mapfix(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateMapfix),
"maptest.submissions.validate"=>self.validate_submission(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateSubmission),
"maptest.mapfixes.upload"=>self.upload_mapfix.upload(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadMapfix),
"maptest.submissions.upload"=>self.upload_submission.upload(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadSubmission),
"maptest.mapfixes.validate"=>self.validate_mapfix.validate(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateMapfix),
"maptest.submissions.validate"=>self.validate_submission.validate(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateSubmission),
other=>Err(HandleMessageError::UnknownSubject(other.to_owned()))
}
}

@ -1,55 +1,14 @@
use submissions_api::types::{SubmissionID,MapfixID,OperationID};
// These represent the information needed in the nats message
// to perform the operation, not necessarily the over-the-wire format
// Requests are sent from maps-service to validator
// Validation invokes the REST api to update the submissions
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CreateSubmissionRequest{
// operation_id is passed back in the response message
pub OperationID:OperationID,
pub ModelID:u64,
pub DisplayName:String,
pub Creator:String,
pub GameID:u32,
// initial status is passed back on create
pub Status:u32,
pub Roles:u32,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CreateMapfixRequest{
pub OperationID:OperationID,
pub ModelID:u64,
pub TargetAssetID:u64,
pub Description:String,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CheckSubmissionRequest{
pub SubmissionID:SubmissionID,
pub ModelID:u64,
pub SkipChecks:bool,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct CheckMapfixRequest{
pub MapfixID:MapfixID,
pub ModelID:u64,
pub SkipChecks:bool,
}
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct ValidateSubmissionRequest{
// submission_id is passed back in the response message
pub SubmissionID:SubmissionID,
pub SubmissionID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
@ -59,7 +18,7 @@ pub struct ValidateSubmissionRequest{
#[derive(serde::Deserialize)]
pub struct ValidateMapfixRequest{
// submission_id is passed back in the response message
pub MapfixID:MapfixID,
pub MapfixID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
@ -69,7 +28,7 @@ pub struct ValidateMapfixRequest{
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct UploadSubmissionRequest{
pub SubmissionID:SubmissionID,
pub SubmissionID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub ModelName:String,
@ -78,7 +37,7 @@ pub struct UploadSubmissionRequest{
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct UploadMapfixRequest{
pub MapfixID:MapfixID,
pub MapfixID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub TargetAssetID:u64,

@ -1,122 +0,0 @@
#[allow(dead_code)]
#[derive(Debug)]
pub enum ReadDomError{
Binary(rbx_binary::DecodeError),
Xml(rbx_xml::DecodeError),
Read(std::io::Error),
UnknownFormat(Vec<u8>),
}
impl std::fmt::Display for ReadDomError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for ReadDomError{}
pub fn read_dom<R:std::io::Read>(input:R)->Result<rbx_dom_weak::WeakDom,ReadDomError>{
let mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadDomError::Read)?;
match peek.get(0..8){
Some(b"<roblox!")=>rbx_binary::from_reader(buf).map_err(ReadDomError::Binary),
Some(b"<roblox ")=>rbx_xml::from_reader_default(buf).map_err(ReadDomError::Xml),
_=>Err(ReadDomError::UnknownFormat(peek.to_owned())),
}
}
pub fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
rbx_dom_weak::ustr(s)
}
fn find_first_child_name_and_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str,class:&str)->Option<&'a rbx_dom_weak::Instance>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name&&inst.class==class)
}
pub enum GameID{
Bhop=1,
Surf=2,
FlyTrials=5,
}
impl From<GameID> for submissions_api::types::GameID{
fn from(value:GameID)->Self{
match value{
GameID::Bhop=>submissions_api::types::GameID::Bhop,
GameID::Surf=>submissions_api::types::GameID::Surf,
GameID::FlyTrials=>submissions_api::types::GameID::FlyTrials,
}
}
}
#[derive(Debug)]
pub struct ParseGameIDError;
impl std::str::FromStr for GameID{
type Err=ParseGameIDError;
fn from_str(s:&str)->Result<Self,Self::Err>{
if s.starts_with("bhop_"){
return Ok(GameID::Bhop);
}
if s.starts_with("surf_"){
return Ok(GameID::Surf);
}
if s.starts_with("flytrials_"){
return Ok(GameID::FlyTrials);
}
Err(ParseGameIDError)
}
}
pub struct GameIDError;
impl TryFrom<u32> for GameID{
type Error=GameIDError;
fn try_from(value:u32)->Result<Self,Self::Error>{
match value{
1=>Ok(GameID::Bhop),
2=>Ok(GameID::Surf),
5=>Ok(GameID::FlyTrials),
_=>Err(GameIDError)
}
}
}
pub struct MapInfo<'a>{
pub display_name:Result<&'a str,StringValueError>,
pub creator:Result<&'a str,StringValueError>,
pub game_id:Result<GameID,ParseGameIDError>,
}
#[derive(Debug)]
pub enum StringValueError{
ObjectNotFound,
ValueNotSet,
NonStringValue,
}
fn string_value(instance:Option<&rbx_dom_weak::Instance>)->Result<&str,StringValueError>{
let instance=instance.ok_or(StringValueError::ObjectNotFound)?;
let value=instance.properties.get(&static_ustr("Value")).ok_or(StringValueError::ValueNotSet)?;
match value{
rbx_dom_weak::types::Variant::String(value)=>Ok(value),
_=>Err(StringValueError::NonStringValue),
}
}
#[derive(Debug)]
pub enum GetRootInstanceError{
ModelFileRootMustHaveOneChild,
ModelFileChildRefIsNil,
}
pub fn get_root_instance(dom:&rbx_dom_weak::WeakDom)->Result<&rbx_dom_weak::Instance,GetRootInstanceError>{
let &[map_ref]=dom.root().children()else{
return Err(GetRootInstanceError::ModelFileRootMustHaveOneChild);
};
let model_instance=dom.get_by_ref(map_ref).ok_or(GetRootInstanceError::ModelFileChildRefIsNil)?;
Ok(model_instance)
}
pub fn get_mapinfo<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&rbx_dom_weak::Instance)->MapInfo<'a>{
MapInfo{
display_name:string_value(find_first_child_name_and_class(dom,model_instance,"DisplayName","StringValue")),
creator:string_value(find_first_child_name_and_class(dom,model_instance,"Creator","StringValue")),
game_id:model_instance.name.parse(),
}
}

4
validation/src/types.rs Normal file

@ -0,0 +1,4 @@
pub enum ResourceID{
Mapfix(i64),
Submission(i64),
}

@ -1,49 +1,49 @@
use crate::download::download_asset_version;
use crate::nats_types::UploadMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Download(crate::download::Error),
IO(std::io::Error),
pub enum UploadError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Upload(rbx_asset::cookie::UploadError),
ApiActionMapfixUploaded(submissions_api::Error),
}
impl std::fmt::Display for Error{
impl std::fmt::Display for UploadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for UploadError{}
impl crate::message_handler::MessageHandler{
pub async fn upload_mapfix(&self,upload_info:UploadMapfixRequest)->Result<(),Error>{
// download the map model
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
pub struct Uploader(crate::uploader::Uploader);
impl Uploader{
pub const fn new(inner:crate::uploader::Uploader)->Self{
Self(inner)
}
pub async fn upload(&self,upload_info:UploadMapfixRequest)->Result<(),UploadError>{
let Self(uploader)=self;
// download the map model version
let model_data=uploader.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:upload_info.ModelID,
version:upload_info.ModelVersion,
}).await.map_err(Error::Download)?;
// transparently handle gzipped models
let model_data=maybe_gzip.to_vec().map_err(Error::IO)?;
version:Some(upload_info.ModelVersion),
}).await.map_err(UploadError::Get)?;
// upload the map to the strafesnet group
let _upload_response=self.cookie_context.upload(rbx_asset::cookie::UploadRequest{
let _upload_response=uploader.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{
assetid:upload_info.TargetAssetID,
groupId:self.group_id,
groupId:uploader.group_id,
name:None,
description:None,
ispublic:None,
allowComments:None,
},model_data).await.map_err(Error::Upload)?;
},model_data).await.map_err(UploadError::Upload)?;
// that's it, the database entry does not need to be changed.
// mark mapfix as uploaded, TargetAssetID is unchanged
self.api.action_mapfix_uploaded(submissions_api::types::ActionMapfixUploadedRequest{
uploader.api.action_mapfix_uploaded(submissions_api::types::ActionMapfixUploadedRequest{
MapfixID:upload_info.MapfixID,
}).await.map_err(Error::ApiActionMapfixUploaded)?;
}).await.map_err(UploadError::ApiActionMapfixUploaded)?;
Ok(())
}

@ -1,48 +1,48 @@
use crate::download::download_asset_version;
use crate::nats_types::UploadSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
Download(crate::download::Error),
IO(std::io::Error),
pub enum UploadError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Create(rbx_asset::cookie::CreateError),
SystemTime(std::time::SystemTimeError),
ApiActionSubmissionUploaded(submissions_api::Error),
}
impl std::fmt::Display for Error{
impl std::fmt::Display for UploadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for UploadError{}
impl crate::message_handler::MessageHandler{
pub async fn upload_submission(&self,upload_info:UploadSubmissionRequest)->Result<(),Error>{
// download the map model
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
pub struct Uploader(crate::uploader::Uploader);
impl Uploader{
pub const fn new(inner:crate::uploader::Uploader)->Self{
Self(inner)
}
pub async fn upload(&self,upload_info:UploadSubmissionRequest)->Result<(),UploadError>{
let Self(uploader)=self;
// download the map model version
let model_data=uploader.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:upload_info.ModelID,
version:upload_info.ModelVersion,
}).await.map_err(Error::Download)?;
// transparently handle gzipped models
let model_data=maybe_gzip.to_vec().map_err(Error::IO)?;
version:Some(upload_info.ModelVersion),
}).await.map_err(UploadError::Get)?;
// upload the map to the strafesnet group
let upload_response=self.cookie_context.create(rbx_asset::cookie::CreateRequest{
let upload_response=uploader.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
name:upload_info.ModelName.clone(),
description:"".to_owned(),
ispublic:false,
allowComments:false,
groupId:self.group_id,
},model_data).await.map_err(Error::Create)?;
groupId:uploader.group_id,
},model_data).await.map_err(UploadError::Create)?;
// note the asset id of the created model for later release, and mark the submission as uploaded
self.api.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{
uploader.api.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{
SubmissionID:upload_info.SubmissionID,
UploadedAssetID:upload_response.AssetId,
}).await.map_err(Error::ApiActionSubmissionUploaded)?;
}).await.map_err(UploadError::ApiActionSubmissionUploaded)?;
Ok(())
}

@ -0,0 +1,18 @@
pub struct Uploader{
pub(crate) roblox_cookie:rbx_asset::cookie::CookieContext,
pub(crate) group_id:Option<u64>,
pub(crate) api:submissions_api::internal::Context,
}
impl Uploader{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api:submissions_api::internal::Context,
)->Self{
Self{
roblox_cookie,
group_id,
api,
}
}
}

@ -2,44 +2,41 @@ use crate::nats_types::ValidateMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
pub enum ValidateMapfixError{
ApiActionMapfixValidate(submissions_api::Error),
}
impl std::fmt::Display for Error{
impl std::fmt::Display for ValidateMapfixError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for ValidateMapfixError{}
pub struct Validator(crate::validator::Validator);
impl Validator{
pub const fn new(inner:crate::validator::Validator)->Self{
Self(inner)
}
pub async fn validate(&self,validate_info:ValidateMapfixRequest)->Result<(),ValidateMapfixError>{
let Self(validator)=self;
impl crate::message_handler::MessageHandler{
pub async fn validate_mapfix(&self,validate_info:ValidateMapfixRequest)->Result<(),Error>{
let mapfix_id=validate_info.MapfixID;
let validate_result=self.validate_inner(validate_info.into()).await;
let validate_result=validator.validate(validate_info.into()).await;
// update the mapfix depending on the result
match &validate_result{
Ok(())=>{
// update the mapfix model status to validated
self.api.action_mapfix_validated(
mapfix_id
).await.map_err(Error::ApiActionMapfixValidate)?;
validator.api.action_mapfix_validated(
submissions_api::types::MapfixID(mapfix_id)
).await.map_err(ValidateMapfixError::ApiActionMapfixValidate)?;
},
Err(e)=>{
// log error
println!("[validate_mapfix] Error: {e}");
self.api.create_mapfix_audit_error(
submissions_api::types::CreateMapfixAuditErrorRequest{
MapfixID:mapfix_id,
ErrorMessage:e.to_string(),
}
).await.map_err(Error::ApiActionMapfixValidate)?;
// update the mapfix model status to accepted
self.api.action_mapfix_accepted(submissions_api::types::ActionMapfixAcceptedRequest{
validator.api.action_mapfix_accepted(submissions_api::types::ActionMapfixAcceptedRequest{
MapfixID:mapfix_id,
}).await.map_err(Error::ApiActionMapfixValidate)?;
StatusMessage:format!("{e}"),
}).await.map_err(ValidateMapfixError::ApiActionMapfixValidate)?;
},
}

@ -2,44 +2,41 @@ use crate::nats_types::ValidateSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
pub enum ValidateSubmissionError{
ApiActionSubmissionValidate(submissions_api::Error),
}
impl std::fmt::Display for Error{
impl std::fmt::Display for ValidateSubmissionError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for ValidateSubmissionError{}
pub struct Validator(crate::validator::Validator);
impl Validator{
pub const fn new(inner:crate::validator::Validator)->Self{
Self(inner)
}
pub async fn validate(&self,validate_info:ValidateSubmissionRequest)->Result<(),ValidateSubmissionError>{
let Self(validator)=self;
impl crate::message_handler::MessageHandler{
pub async fn validate_submission(&self,validate_info:ValidateSubmissionRequest)->Result<(),Error>{
let submission_id=validate_info.SubmissionID;
let validate_result=self.validate_inner(validate_info.into()).await;
let validate_result=validator.validate(validate_info.into()).await;
// update the submission depending on the result
match &validate_result{
Ok(())=>{
// update the submission model status to validated
self.api.action_submission_validated(
submission_id
).await.map_err(Error::ApiActionSubmissionValidate)?;
validator.api.action_submission_validated(
submissions_api::types::SubmissionID(submission_id)
).await.map_err(ValidateSubmissionError::ApiActionSubmissionValidate)?;
},
Err(e)=>{
// log error
println!("[validate_submission] Error: {e}");
self.api.create_submission_audit_error(
submissions_api::types::CreateSubmissionAuditErrorRequest{
SubmissionID:submission_id,
ErrorMessage:e.to_string(),
}
).await.map_err(Error::ApiActionSubmissionValidate)?;
// update the submission model status to accepted
self.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{
validator.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{
SubmissionID:submission_id,
}).await.map_err(Error::ApiActionSubmissionValidate)?;
StatusMessage:format!("{e}"),
}).await.map_err(ValidateSubmissionError::ApiActionSubmissionValidate)?;
},
}

@ -1,8 +1,7 @@
use futures::TryStreamExt;
use submissions_api::types::Resource;
use submissions_api::types::ResourceType;
use crate::download::download_asset_version;
use crate::rbx_util::{read_dom,static_ustr,ReadDomError};
use crate::types::ResourceID;
const SCRIPT_CONCURRENCY:usize=16;
@ -20,7 +19,7 @@ struct NamePolicy{
}
fn source_has_illegal_keywords(source:&str)->bool{
source.contains("getfenv")||source.contains("require")
source.find("getfenv").is_some()||source.find("require").is_some()
}
fn hash_source(source:&str)->String{
@ -30,25 +29,19 @@ fn hash_source(source:&str)->String{
format!("{:016x}",hash)
}
#[allow(dead_code)]
// #[allow(dead_code)]
#[derive(Debug)]
pub enum Error{
ModelInfoDownload(rbx_asset::cloud::GetError),
CreatorTypeMustBeUser,
RevisionMismatch{
current:u64,
submitted:u64,
},
pub enum ValidateError{
ScriptFlaggedIllegalKeyword(String),
ScriptBlocked(Option<submissions_api::types::ScriptID>),
ScriptNotYetReviewed(Option<submissions_api::types::ScriptID>),
Download(crate::download::Error),
ModelFileDownload(rbx_asset::cookie::GetError),
ModelFileDecode(ReadDomError),
ApiGetScriptPolicyFromHash(submissions_api::types::ScriptPolicySingleItemError),
ApiGetScriptPolicyFromHash(submissions_api::types::SingleItemError),
ApiGetScript(submissions_api::Error),
ApiCreateScript(submissions_api::Error),
ApiCreateScriptPolicy(submissions_api::Error),
ApiGetScriptFromHash(submissions_api::types::ScriptSingleItemError),
ApiGetScriptFromHash(submissions_api::types::SingleItemError),
ApiUpdateMapfixModel(submissions_api::Error),
ApiUpdateSubmissionModel(submissions_api::Error),
ModelFileRootMustHaveOneChild,
@ -57,19 +50,19 @@ pub enum Error{
AssetUpload(rbx_asset::cookie::UploadError),
AssetCreate(rbx_asset::cookie::CreateError),
}
impl std::fmt::Display for Error{
impl std::fmt::Display for ValidateError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
impl std::error::Error for ValidateError{}
#[allow(nonstandard_style)]
pub struct ValidateRequest{
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
pub Resource:Resource,
pub ResourceID:ResourceID,
}
impl From<crate::nats_types::ValidateMapfixRequest> for ValidateRequest{
@ -78,7 +71,7 @@ impl From<crate::nats_types::ValidateMapfixRequest> for ValidateRequest{
ModelID:value.ModelID,
ModelVersion:value.ModelVersion,
ValidatedModelID:value.ValidatedModelID,
Resource:Resource::Mapfix(value.MapfixID),
ResourceID:ResourceID::Mapfix(value.MapfixID),
}
}
}
@ -88,57 +81,50 @@ impl From<crate::nats_types::ValidateSubmissionRequest> for ValidateRequest{
ModelID:value.ModelID,
ModelVersion:value.ModelVersion,
ValidatedModelID:value.ValidatedModelID,
Resource:Resource::Submission(value.SubmissionID),
ResourceID:ResourceID::Submission(value.SubmissionID),
}
}
}
impl crate::message_handler::MessageHandler{
pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),Error>{
// discover asset creator and latest version
let info=self.cloud_context.get_asset_info(
rbx_asset::cloud::GetAssetLatestRequest{asset_id:validate_info.ModelID}
).await.map_err(Error::ModelInfoDownload)?;
pub struct Validator{
pub(crate) roblox_cookie:rbx_asset::cookie::CookieContext,
pub(crate) api:submissions_api::internal::Context,
}
// reject models created by a group
let rbx_asset::cloud::Creator::userId(_user_id)=info.creationContext.creator else{
return Err(Error::CreatorTypeMustBeUser);
};
// Has the map been updated since it was submitted?
if info.revisionId!=validate_info.ModelVersion{
return Err(Error::RevisionMismatch{
current:info.revisionId,
submitted:validate_info.ModelVersion,
});
impl Validator{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
api:submissions_api::internal::Context,
)->Self{
Self{
roblox_cookie,
api,
}
// download the map model
let maybe_gzip=download_asset_version(&self.cloud_context,rbx_asset::cloud::GetAssetVersionRequest{
}
pub async fn validate(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{
// download map
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:validate_info.ModelID,
version:validate_info.ModelVersion,
}).await.map_err(Error::Download)?;
version:Some(validate_info.ModelVersion),
}).await.map_err(ValidateError::ModelFileDownload)?;
// decode dom (slow!)
let mut dom=maybe_gzip.read_with(read_dom,read_dom).map_err(Error::ModelFileDecode)?;
let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ModelFileDecode)?;
/* VALIDATE MAP */
// stupid ustr thing
let source_property=static_ustr("Source");
// collect unique scripts
let script_refs=get_script_refs(&dom);
let mut script_map=std::collections::HashMap::<String,NamePolicy>::new();
for &script_ref in &script_refs{
if let Some(script)=dom.get_by_ref(script_ref){
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get(&source_property){
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get("Source"){
// check the source for illegal keywords
if source_has_illegal_keywords(source){
// immediately abort
// grab path to offending script
let path=get_partial_path(&dom,script);
return Err(Error::ScriptFlaggedIllegalKeyword(path));
return Err(ValidateError::ScriptFlaggedIllegalKeyword(path));
}
// associate a name and policy with the source code
// policy will be fetched from the database to replace the default policy
@ -159,7 +145,7 @@ impl crate::message_handler::MessageHandler{
// fetch the script policy
let script_policy=self.api.get_script_policy_from_hash(submissions_api::types::HashRequest{
hash:hash.as_str(),
}).await.map_err(Error::ApiGetScriptPolicyFromHash)?;
}).await.map_err(ValidateError::ApiGetScriptPolicyFromHash)?;
// write the policy to the script_map, fetching the replacement code if necessary
if let Some(script_policy)=script_policy{
@ -171,12 +157,15 @@ impl crate::message_handler::MessageHandler{
submissions_api::types::Policy::Replace=>{
let script=self.api.get_script(submissions_api::types::GetScriptRequest{
ScriptID:script_policy.ToScriptID,
}).await.map_err(Error::ApiGetScript)?;
}).await.map_err(ValidateError::ApiGetScript)?;
Policy::Replace(script.Source)
},
};
}else{
let (resource_type,resource_id)=validate_info.Resource.split();
let (resource_type,resource_id)=match validate_info.ResourceID{
ResourceID::Mapfix(mapfix_id)=>(ResourceType::Mapfix,mapfix_id),
ResourceID::Submission(submission_id)=>(ResourceType::Submission,submission_id),
};
// upload the script
let script=self.api.create_script(submissions_api::types::CreateScriptRequest{
@ -184,14 +173,14 @@ impl crate::message_handler::MessageHandler{
Source:source.as_str(),
ResourceType:resource_type,
ResourceID:Some(resource_id),
}).await.map_err(Error::ApiCreateScript)?;
}).await.map_err(ValidateError::ApiCreateScript)?;
// create a None policy (pending review by yours truly)
self.api.create_script_policy(submissions_api::types::CreateScriptPolicyRequest{
ToScriptID:script.ScriptID,
FromScriptID:script.ScriptID,
ToScriptID:script.ID,
FromScriptID:script.ID,
Policy:submissions_api::types::Policy::None,
}).await.map_err(Error::ApiCreateScriptPolicy)?;
}).await.map_err(ValidateError::ApiCreateScriptPolicy)?;
}
Ok(())
@ -199,17 +188,17 @@ impl crate::message_handler::MessageHandler{
.await?;
// make the replacements
let mut modified=false;
let mut modified=true;
for &script_ref in &script_refs{
if let Some(script)=dom.get_by_ref_mut(script_ref){
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut(&source_property){
if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut("Source"){
match script_map.get(source.as_str()).map(|p|&p.policy){
Some(Policy::Blocked)=>{
let hash=hash_source(source.as_str());
let script=self.api.get_script_from_hash(submissions_api::types::HashRequest{
hash:hash.as_str(),
}).await.map_err(Error::ApiGetScriptFromHash)?;
return Err(Error::ScriptBlocked(script.map(|s|s.ID)));
}).await.map_err(ValidateError::ApiGetScriptFromHash)?;
return Err(ValidateError::ScriptBlocked(script.map(|s|s.ID)));
},
None
|Some(Policy::None)
@ -217,8 +206,8 @@ impl crate::message_handler::MessageHandler{
let hash=hash_source(source.as_str());
let script=self.api.get_script_from_hash(submissions_api::types::HashRequest{
hash:hash.as_str(),
}).await.map_err(Error::ApiGetScriptFromHash)?;
return Err(Error::ScriptNotYetReviewed(script.map(|s|s.ID)));
}).await.map_err(ValidateError::ApiGetScriptFromHash)?;
return Err(ValidateError::ScriptNotYetReviewed(script.map(|s|s.ID)));
},
Some(Policy::Allowed)=>(),
Some(Policy::Delete)=>{
@ -235,71 +224,127 @@ impl crate::message_handler::MessageHandler{
}
}
println!("[Validator] Forcing model upload! modified=true");
// if the model was validated, the submission must be changed to use the modified model
let (validated_model_id,validated_model_version)=if modified{
if modified{
// serialize model (slow!)
let mut data=Vec::new();
let &[map_ref]=dom.root().children()else{
return Err(Error::ModelFileRootMustHaveOneChild);
return Err(ValidateError::ModelFileRootMustHaveOneChild);
};
rbx_binary::to_writer(&mut data,&dom,&[map_ref]).map_err(Error::ModelFileEncode)?;
rbx_binary::to_writer(&mut data,&dom,&[map_ref]).map_err(ValidateError::ModelFileEncode)?;
// upload a model lol
if let Some(model_id)=validate_info.ValidatedModelID{
let model_id=if let Some(model_id)=validate_info.ValidatedModelID{
// upload to existing id
let response=self.cookie_context.upload(rbx_asset::cookie::UploadRequest{
let response=self.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{
assetid:model_id,
name:None,
description:None,
ispublic:None,
allowComments:None,
groupId:None,
},data).await.map_err(Error::AssetUpload)?;
},data).await.map_err(ValidateError::AssetUpload)?;
(response.AssetId,response.AssetVersion)
response.AssetId
}else{
// grab the map instance from the map ref
// grab the map instance from the map re
let Some(map_instance)=dom.get_by_ref(map_ref)else{
return Err(Error::ModelFileChildRefIsNil);
return Err(ValidateError::ModelFileChildRefIsNil);
};
// create new model
let response=self.cookie_context.create(rbx_asset::cookie::CreateRequest{
let response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
name:map_instance.name.clone(),
description:"".to_owned(),
ispublic:true,
allowComments:true,
groupId:None,
},data).await.map_err(Error::AssetCreate)?;
},data).await.map_err(ValidateError::AssetCreate)?;
(response.AssetId,response.AssetVersion)
response.AssetId
};
match validate_info.ResourceID{
ResourceID::Mapfix(mapfix_id)=>{
// update the mapfix to use the validated model
self.api.update_mapfix_validated_model(submissions_api::types::UpdateMapfixModelRequest{
MapfixID:mapfix_id,
ModelID:model_id,
ModelVersion:1, //TODO
}).await.map_err(ValidateError::ApiUpdateMapfixModel)?;
},
ResourceID::Submission(submission_id)=>{
// update the submission to use the validated model
self.api.update_submission_validated_model(submissions_api::types::UpdateSubmissionModelRequest{
SubmissionID:submission_id,
ModelID:model_id,
ModelVersion:1, //TODO
}).await.map_err(ValidateError::ApiUpdateSubmissionModel)?;
},
}
}else{
(validate_info.ModelID,validate_info.ModelVersion)
};
match validate_info.Resource{
Resource::Mapfix(mapfix_id)=>{
// update the mapfix to use the validated model
self.api.update_mapfix_validated_model(submissions_api::types::UpdateMapfixModelRequest{
MapfixID:mapfix_id,
ModelID:validated_model_id,
ModelVersion:validated_model_version,
}).await.map_err(Error::ApiUpdateMapfixModel)?;
},
Resource::Submission(submission_id)=>{
// update the submission to use the validated model
self.api.update_submission_validated_model(submissions_api::types::UpdateSubmissionModelRequest{
SubmissionID:submission_id,
ModelID:validated_model_id,
ModelVersion:validated_model_version,
}).await.map_err(Error::ApiUpdateSubmissionModel)?;
},
}
Ok(())
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum ReadDomError{
Binary(rbx_binary::DecodeError),
Xml(rbx_xml::DecodeError),
Read(std::io::Error),
Seek(std::io::Error),
UnknownFormat([u8;8]),
}
impl std::fmt::Display for ReadDomError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for ReadDomError{}
fn read_dom<R:std::io::Read+std::io::Seek>(input:&mut R)->Result<rbx_dom_weak::WeakDom,ReadDomError>{
let mut first_8=[0u8;8];
std::io::Read::read_exact(input,&mut first_8).map_err(ReadDomError::Read)?;
std::io::Seek::rewind(input).map_err(ReadDomError::Seek)?;
match &first_8[0..4]{
b"<rob"=>{
match &first_8[4..8]{
b"lox!"=>rbx_binary::from_reader(input).map_err(ReadDomError::Binary),
b"lox "=>rbx_xml::from_reader(input,rbx_xml::DecodeOptions::default()).map_err(ReadDomError::Xml),
_=>Err(ReadDomError::UnknownFormat(first_8)),
}
},
_=>Err(ReadDomError::UnknownFormat(first_8)),
}
}
fn class_is_a(class:&str,superclass:&str)->bool{
if class==superclass{
return true
}
let class_descriptor=rbx_reflection_database::get().classes.get(class);
if let Some(descriptor)=&class_descriptor{
if let Some(class_super)=&descriptor.superclass{
return class_is_a(&class_super,superclass)
}
}
false
}
fn recursive_collect_superclass(objects:&mut std::vec::Vec<rbx_dom_weak::types::Ref>,dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str){
for &referent in instance.children(){
if let Some(c)=dom.get_by_ref(referent){
if class_is_a(c.class.as_str(),superclass){
objects.push(c.referent());//copy ref
}
recursive_collect_superclass(objects,dom,c,superclass);
}
}
}
fn get_partial_path(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->String{
let mut names:Vec<_>=core::iter::successors(
Some(instance),
@ -314,11 +359,7 @@ fn get_partial_path(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)
}
fn get_script_refs(dom:&rbx_dom_weak::WeakDom)->Vec<rbx_dom_weak::types::Ref>{
let db=rbx_reflection_database::get();
let superclass=&db.classes["LuaSourceContainer"];
dom.descendants().filter_map(|inst|{
let class=db.classes.get(inst.class.as_str())?;
db.has_superclass(class,superclass)
.then_some(inst.referent())
}).collect()
let mut scripts=std::vec::Vec::new();
recursive_collect_superclass(&mut scripts,dom,dom.root(),"LuaSourceContainer");
scripts
}

@ -1,4 +1,4 @@
FROM registry.itzana.me/docker-proxy/oven/bun:1.2.8
FROM oven/bun:latest
WORKDIR /app
@ -10,4 +10,4 @@ ENV NEXT_TELEMETRY_DISABLED=1
RUN bun install
RUN bun run build
ENTRYPOINT ["bun", "run", "start"]
ENTRYPOINT ["bun", "run", "start"]

@ -1,886 +0,0 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "map-service-web",
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^6.1.10",
"@mui/material": "^6.1.10",
"next": "^15.1.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sass": "^1.82.0",
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@types/node": "^20.17.9",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.2",
"eslint": "^9.16.0",
"eslint-config-next": "15.1.0",
"typescript": "^5.7.2",
},
},
},
"packages": {
"@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
"@babel/generator": ["@babel/generator@7.27.0", "", { "dependencies": { "@babel/parser": "^7.27.0", "@babel/types": "^7.27.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw=="],
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="],
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
"@babel/parser": ["@babel/parser@7.27.0", "", { "dependencies": { "@babel/types": "^7.27.0" }, "bin": "./bin/babel-parser.js" }, "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg=="],
"@babel/runtime": ["@babel/runtime@7.27.0", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw=="],
"@babel/template": ["@babel/template@7.27.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/parser": "^7.27.0", "@babel/types": "^7.27.0" } }, "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA=="],
"@babel/traverse": ["@babel/traverse@7.27.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.27.0", "@babel/parser": "^7.27.0", "@babel/template": "^7.27.0", "@babel/types": "^7.27.0", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA=="],
"@babel/types": ["@babel/types@7.27.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="],
"@emnapi/core": ["@emnapi/core@1.4.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.1", "tslib": "^2.4.0" } }, "sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg=="],
"@emnapi/runtime": ["@emnapi/runtime@1.4.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw=="],
"@emotion/babel-plugin": ["@emotion/babel-plugin@11.13.5", "", { "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", "find-root": "^1.1.0", "source-map": "^0.5.7", "stylis": "4.2.0" } }, "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ=="],
"@emotion/cache": ["@emotion/cache@11.14.0", "", { "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA=="],
"@emotion/hash": ["@emotion/hash@0.9.2", "", {}, "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="],
"@emotion/is-prop-valid": ["@emotion/is-prop-valid@1.3.1", "", { "dependencies": { "@emotion/memoize": "^0.9.0" } }, "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw=="],
"@emotion/memoize": ["@emotion/memoize@0.9.0", "", {}, "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="],
"@emotion/react": ["@emotion/react@11.14.0", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA=="],
"@emotion/serialize": ["@emotion/serialize@1.3.3", "", { "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA=="],
"@emotion/sheet": ["@emotion/sheet@1.4.0", "", {}, "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="],
"@emotion/styled": ["@emotion/styled@11.14.0", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/is-prop-valid": "^1.3.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2" }, "peerDependencies": { "@emotion/react": "^11.0.0-rc.0", "react": ">=16.8.0" } }, "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA=="],
"@emotion/unitless": ["@emotion/unitless@0.10.0", "", {}, "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="],
"@emotion/use-insertion-effect-with-fallbacks": ["@emotion/use-insertion-effect-with-fallbacks@1.2.0", "", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg=="],
"@emotion/utils": ["@emotion/utils@1.4.2", "", {}, "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="],
"@emotion/weak-memoize": ["@emotion/weak-memoize@0.4.0", "", {}, "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.5.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w=="],
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
"@eslint/config-array": ["@eslint/config-array@0.19.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w=="],
"@eslint/config-helpers": ["@eslint/config-helpers@0.2.1", "", {}, "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw=="],
"@eslint/core": ["@eslint/core@0.12.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg=="],
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
"@eslint/js": ["@eslint/js@9.23.0", "", {}, "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.8", "", { "dependencies": { "@eslint/core": "^0.13.0", "levn": "^0.4.1" } }, "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.2", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="],
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="],
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="],
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="],
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="],
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="],
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="],
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="],
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="],
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="],
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="],
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="],
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="],
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="],
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
"@mui/core-downloads-tracker": ["@mui/core-downloads-tracker@6.4.10", "", {}, "sha512-cblGjlM6+xsptwyaALw8RbRIUoqmKxOqLxlk2LkTDhxqUuql1YSOKKLH3w+Yd2QLz28b7MR65sx1OjsRZUfOSQ=="],
"@mui/icons-material": ["@mui/icons-material@6.4.10", "", { "dependencies": { "@babel/runtime": "^7.26.0" }, "peerDependencies": { "@mui/material": "^6.4.10", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-c2KdFl4KZ0QYC+JSDTMCNjcuOL2rVSdIx/beo7FwJDh2e9XqC1MoLCjw6L1Jo40zbArkgJyg3oFORbXcRfgZOA=="],
"@mui/material": ["@mui/material@6.4.10", "", { "dependencies": { "@babel/runtime": "^7.26.0", "@mui/core-downloads-tracker": "^6.4.10", "@mui/system": "^6.4.10", "@mui/types": "~7.2.24", "@mui/utils": "^6.4.9", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1", "react-is": "^19.0.0", "react-transition-group": "^4.4.5" }, "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@mui/material-pigment-css": "^6.4.10", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/react", "@emotion/styled", "@mui/material-pigment-css", "@types/react"] }, "sha512-L1B0+Vg9NFjo3NcfODH3bohl6fIkzjyDBHBHb3Al4QI7owaJrFm2sSDyfz++iatzICug6U6q5tHLQrCLO71xkg=="],
"@mui/private-theming": ["@mui/private-theming@6.4.9", "", { "dependencies": { "@babel/runtime": "^7.26.0", "@mui/utils": "^6.4.9", "prop-types": "^15.8.1" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw=="],
"@mui/styled-engine": ["@mui/styled-engine@6.4.9", "", { "dependencies": { "@babel/runtime": "^7.26.0", "@emotion/cache": "^11.13.5", "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "peerDependencies": { "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/react", "@emotion/styled"] }, "sha512-qZRWO0cT407NI4ZRjZcH+1SOu8f3JzLHqdMlg52GyEufM9pkSZFnf7xjpwnlvkixcGjco6wLlMD0VB43KRcBuA=="],
"@mui/system": ["@mui/system@6.4.10", "", { "dependencies": { "@babel/runtime": "^7.26.0", "@mui/private-theming": "^6.4.9", "@mui/styled-engine": "^6.4.9", "@mui/types": "~7.2.24", "@mui/utils": "^6.4.9", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/react", "@emotion/styled", "@types/react"] }, "sha512-RyBGQwP3tgo4JEibK+RwVu1a6nQ6y8urMCNsb2aiN/nvTxxumq6P26aoG4GTUf8L4O1sthC4lMXlP4r8ixDkMg=="],
"@mui/types": ["@mui/types@7.2.24", "", { "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw=="],
"@mui/utils": ["@mui/utils@6.4.9", "", { "dependencies": { "@babel/runtime": "^7.26.0", "@mui/types": "~7.2.24", "@types/prop-types": "^15.7.14", "clsx": "^2.1.1", "prop-types": "^15.8.1", "react-is": "^19.0.0" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.8", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" } }, "sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg=="],
"@next/env": ["@next/env@15.2.4", "", {}, "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g=="],
"@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.1.0", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-+jPT0h+nelBT6HC9ZCHGc7DgGVy04cv4shYdAe6tKlEbjQUtwU3LzQhzbDHQyY2m6g39m6B0kOFVuLGBrxxbGg=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew=="],
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ=="],
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA=="],
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw=="],
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw=="],
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg=="],
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="],
"@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="],
"@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="],
"@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="],
"@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="],
"@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="],
"@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="],
"@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="],
"@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="],
"@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="],
"@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="],
"@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="],
"@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="],
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
"@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="],
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
"@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.11.0", "", {}, "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ=="],
"@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="],
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
"@types/node": ["@types/node@20.17.30", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg=="],
"@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
"@types/prop-types": ["@types/prop-types@15.7.14", "", {}, "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="],
"@types/react": ["@types/react@19.1.0", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w=="],
"@types/react-dom": ["@types/react-dom@19.1.1", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w=="],
"@types/react-transition-group": ["@types/react-transition-group@4.4.12", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.29.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.29.0", "@typescript-eslint/type-utils": "8.29.0", "@typescript-eslint/utils": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.29.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.29.0", "@typescript-eslint/types": "8.29.0", "@typescript-eslint/typescript-estree": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.29.0", "", { "dependencies": { "@typescript-eslint/types": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0" } }, "sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.29.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.29.0", "@typescript-eslint/utils": "8.29.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.29.0", "", {}, "sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.29.0", "", { "dependencies": { "@typescript-eslint/types": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.29.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.29.0", "@typescript-eslint/types": "8.29.0", "@typescript-eslint/typescript-estree": "8.29.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.29.0", "", { "dependencies": { "@typescript-eslint/types": "8.29.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg=="],
"@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EpRILdWr3/xDa/7MoyfO7JuBIJqpBMphtu4+80BK1bRfFcniVT74h3Z7q1+WOc92FuIAYatB1vn9TJR67sORGw=="],
"@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-ntj/g7lPyqwinMJWZ+DKHBse8HhVxswGTmNgFKJtdgGub3M3zp5BSZ3bvMP+kBT6dnYJLSVlDqdwOq1P8i0+/g=="],
"@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.3.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-l6BT8f2CU821EW7U8hSUK8XPq4bmyTlt9Mn4ERrfjJNoCw0/JoHAh9amZZtV3cwC3bwwIat+GUnrcHTG9+qixw=="],
"@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.3.3", "", { "os": "linux", "cpu": "arm" }, "sha512-8ScEc5a4y7oE2BonRvzJ+2GSkBaYWyh0/Ko4Q25e/ix6ANpJNhwEPZvCR6GVRmsQAYMIfQvYLdM6YEN+qRjnAQ=="],
"@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.3.3", "", { "os": "linux", "cpu": "arm" }, "sha512-8qQ6l1VTzLNd3xb2IEXISOKwMGXDCzY/UNy/7SovFW2Sp0K3YbL7Ao7R18v6SQkLqQlhhqSBIFRk+u6+qu5R5A=="],
"@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-v81R2wjqcWXJlQY23byqYHt9221h4anQ6wwN64oMD/WAE+FmxPHFZee5bhRkNVtzqO/q7wki33VFWlhiADwUeQ=="],
"@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-cAOx/j0u5coMg4oct/BwMzvWJdVciVauUvsd+GQB/1FZYKQZmqPy0EjJzJGbVzFc6gbnfEcSqvQE6gvbGf2N8Q=="],
"@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.3.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-mq2blqwErgDJD4gtFDlTX/HZ7lNP8YCHYFij2gkXPtMzrXxPW1hOtxL6xg4NWxvnj4bppppb0W3s/buvM55yfg=="],
"@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.3.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-u0VRzfFYysarYHnztj2k2xr+eu9rmgoTUUgCCIT37Nr+j0A05Xk2c3RY8Mh5+DhCl2aYibihnaAEJHeR0UOFIQ=="],
"@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-OrVo5ZsG29kBF0Ug95a2KidS16PqAMmQNozM6InbquOfW/udouk063e25JVLqIBhHLB2WyBnixOQ19tmeC/hIg=="],
"@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-PYnmrwZ4HMp9SkrOhqPghY/aoL+Rtd4CQbr93GlrRTjK6kDzfMfgz3UH3jt6elrQAfupa1qyr1uXzeVmoEAxUA=="],
"@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.3.3", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.7" }, "cpu": "none" }, "sha512-81AnQY6fShmktQw4hWDUIilsKSdvr/acdJ5azAreu2IWNlaJOKphJSsUVWE+yCk6kBMoQyG9ZHCb/krb5K0PEA=="],
"@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.3.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-X/42BMNw7cW6xrB9syuP5RusRnWGoq+IqvJO8IDpp/BZg64J1uuIW6qA/1Cl13Y4LyLXbJVYbYNSKwR/FiHEng=="],
"@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.3.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-EGNnNGQxMU5aTN7js3ETYvuw882zcO+dsVjs+DwO2j/fRVKth87C8e2GzxW1L3+iWAXMyJhvFBKRavk9Og1Z6A=="],
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-GraLbYqOJcmW1qY3osB+2YIiD62nVf2/bVLHZmrb4t/YSUwE03l7TwcDJl08T/Tm3SVhepX8RQkpzWbag/Sb4w=="],
"acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
"array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
"array-includes": ["array-includes@3.1.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" } }, "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ=="],
"array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="],
"array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="],
"array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="],
"array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="],
"array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="],
"arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="],
"ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="],
"async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
"axe-core": ["axe-core@4.10.3", "", {}, "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
"babel-plugin-macros": ["babel-plugin-macros@3.1.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"caniuse-lite": ["caniuse-lite@1.0.30001709", "", {}, "sha512-NgL3vUTnDrPCZ3zTahp4fsugQ4dc7EKTSzwQDPEel6DMoMnfH2jhry9n2Zm8onbSR+f/QtKHFOA+iAQu4kbtWA=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="],
"cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="],
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
"data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
"detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
"dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
"es-abstract": ["es-abstract@1.23.9", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.3", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.0", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-regex": "^1.2.1", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.0", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.18" } }, "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-iterator-helpers": ["es-iterator-helpers@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.4", "safe-array-concat": "^1.1.3" } }, "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
"es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="],
"es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.23.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.23.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw=="],
"eslint-config-next": ["eslint-config-next@15.1.0", "", { "dependencies": { "@next/eslint-plugin-next": "15.1.0", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^5.0.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-gADO+nKVseGso3DtOrYX9H7TxB/MuX7AUYhMlvQMqLYvUWu4HrOQuU7cC1HW74tHIqkAvXdwgAz3TCbczzSEXw=="],
"eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="],
"eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.0", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.12", "unrs-resolver": "^1.3.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-aV3/dVsT0/H9BtpNwbaqvl+0xGMRGzncLyhm793NFGvbwGGvzyAykqWZ8oZlZuGwuHkwJjhWJkG1cM3ynvd2pQ=="],
"eslint-module-utils": ["eslint-module-utils@2.12.0", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg=="],
"eslint-plugin-import": ["eslint-plugin-import@2.31.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", "array.prototype.findlastindex": "^1.2.5", "array.prototype.flat": "^1.3.2", "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.0", "hasown": "^2.0.2", "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.0", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.8", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A=="],
"eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="],
"eslint-plugin-react": ["eslint-plugin-react@7.37.4", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ=="],
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
"eslint-scope": ["eslint-scope@8.3.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ=="],
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
"espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-glob": ["fast-glob@3.3.1", "", { "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.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
"fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
"find-root": ["find-root@1.1.0", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="],
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="],
"functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
"get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
"globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
"globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
"has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
"has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"immutable": ["immutable@5.1.1", "", {}, "sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg=="],
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
"is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
"is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="],
"is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="],
"is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="],
"is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
"is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="],
"is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="],
"is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
"is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
"is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="],
"is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="],
"is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="],
"is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
"is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="],
"is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="],
"is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="],
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
"json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="],
"jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="],
"language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"next": ["next@15.2.4", "", { "dependencies": { "@next/env": "15.2.4", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.2.4", "@next/swc-darwin-x64": "15.2.4", "@next/swc-linux-arm64-gnu": "15.2.4", "@next/swc-linux-arm64-musl": "15.2.4", "@next/swc-linux-x64-gnu": "15.2.4", "@next/swc-linux-x64-musl": "15.2.4", "@next/swc-win32-arm64-msvc": "15.2.4", "@next/swc-win32-x64-msvc": "15.2.4", "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ=="],
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
"object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
"object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
"object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="],
"object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="],
"object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="],
"object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
"own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
"path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
"postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
"react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
"react-is": ["react-is@19.1.0", "", {}, "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg=="],
"react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
"regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
"safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="],
"safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="],
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
"sass": ["sass@1.86.2", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-Rpfn0zAIDqvnSb2DihJTDFjbhqLHu91Wqac9rxontWk7R+2txcPjuujMqu1eeoezh5kAblVCS5EdFdyr0Jmu+w=="],
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
"set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="],
"sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
"source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="],
"streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="],
"string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="],
"string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="],
"string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="],
"string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="],
"string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="],
"string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
"stylis": ["stylis@4.2.0", "", {}, "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
"tinyglobby": ["tinyglobby@0.2.12", "", { "dependencies": { "fdir": "^6.4.3", "picomatch": "^4.0.2" } }, "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
"tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
"typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="],
"typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="],
"typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="],
"typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="],
"unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
"undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
"unrs-resolver": ["unrs-resolver@1.3.3", "", { "optionalDependencies": { "@unrs/resolver-binding-darwin-arm64": "1.3.3", "@unrs/resolver-binding-darwin-x64": "1.3.3", "@unrs/resolver-binding-freebsd-x64": "1.3.3", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.3.3", "@unrs/resolver-binding-linux-arm-musleabihf": "1.3.3", "@unrs/resolver-binding-linux-arm64-gnu": "1.3.3", "@unrs/resolver-binding-linux-arm64-musl": "1.3.3", "@unrs/resolver-binding-linux-ppc64-gnu": "1.3.3", "@unrs/resolver-binding-linux-s390x-gnu": "1.3.3", "@unrs/resolver-binding-linux-x64-gnu": "1.3.3", "@unrs/resolver-binding-linux-x64-musl": "1.3.3", "@unrs/resolver-binding-wasm32-wasi": "1.3.3", "@unrs/resolver-binding-win32-arm64-msvc": "1.3.3", "@unrs/resolver-binding-win32-ia32-msvc": "1.3.3", "@unrs/resolver-binding-win32-x64-msvc": "1.3.3" } }, "sha512-PFLAGQzYlyjniXdbmQ3dnGMZJXX5yrl2YS4DLRfR3BhgUsE1zpRIrccp9XMOGRfIHpdFvCn/nr5N1KMVda4x3A=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
"which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="],
"which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="],
"which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"@babel/traverse/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.13.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw=="],
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
"@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
"@typescript-eslint/typescript-estree/fast-glob": ["fast-glob@3.3.3", "", { "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" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
"error-ex/is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
"eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
"eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
"eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
"is-bun-module/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
"sharp/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
"@typescript-eslint/typescript-estree/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
}
}

@ -6,11 +6,11 @@ const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "tr.rbxcdn.com",
pathname: "/**",
port: "",
search: "",
protocol: 'https',
hostname: 'api.ic3.space',
pathname: '/strafe/map-images/**',
port: '',
search: '',
},
],
},

@ -13,19 +13,18 @@
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^6.1.10",
"@mui/material": "^6.1.10",
"date-fns": "^4.1.0",
"next": "^15.1.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sass": "^1.82.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"typescript": "^5.7.2",
"@types/node": "^20.17.9",
"@types/react": "^19.0.1",
"@types/react-dom": "^19.0.2",
"eslint": "^9.16.0",
"eslint-config-next": "15.1.0",
"typescript": "^5.7.2"
"@eslint/eslintrc": "^3.2.0"
}
}

Binary file not shown.

Before

(image error) Size: 30 KiB

Binary file not shown.

Before

(image error) Size: 35 KiB

@ -1,43 +0,0 @@
import { Button, Container, Paper, Typography } from "@mui/material";
import Webpage from "@/app/_components/webpage";
interface ErrorDisplayProps {
title: string;
message: string;
buttonText?: string;
onButtonClick?: () => void;
}
export function ErrorDisplay({
title,
message,
buttonText,
onButtonClick
}: ErrorDisplayProps) {
return (
<Webpage>
<Container maxWidth="lg" sx={{ py: 6 }}>
<Paper
elevation={3}
sx={{
p: 4,
textAlign: 'center',
borderRadius: 2
}}
>
<Typography variant="h5" gutterBottom>{title}</Typography>
<Typography variant="body1">{message}</Typography>
{buttonText && onButtonClick && (
<Button
variant="contained"
onClick={onButtonClick}
sx={{ mt: 3 }}
>
{buttonText}
</Button>
)}
</Paper>
</Container>
</Webpage>
);
}

@ -1,151 +0,0 @@
import {Box, IconButton, Typography} from "@mui/material";
import {useEffect, useRef, useState} from "react";
import Link from "next/link";
import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
import {SubmissionInfo} from "@/app/ts/Submission";
import {MapfixInfo} from "@/app/ts/Mapfix";
// Type for the items in the carousel
type CarouselItem = SubmissionInfo | MapfixInfo;
// Props for the Carousel component
interface CarouselProps<T extends CarouselItem> {
title: string;
items: T[] | undefined;
renderItem: (item: T) => React.ReactNode;
viewAllLink: string;
}
export function Carousel<T extends CarouselItem>({ title, items, renderItem, viewAllLink }: CarouselProps<T>) {
const carouselRef = useRef<HTMLDivElement | null>(null);
const [scrollPosition, setScrollPosition] = useState<number>(0);
const [maxScroll, setMaxScroll] = useState<number>(0);
const SCROLL_AMOUNT = 300;
useEffect(() => {
if (carouselRef.current) {
const scrollWidth = carouselRef.current.scrollWidth;
const clientWidth = carouselRef.current.clientWidth;
setMaxScroll(scrollWidth - clientWidth);
}
}, [items]);
const scroll = (direction: 'left' | 'right'): void => {
if (carouselRef.current) {
const scrollAmount = direction === 'left' ? -SCROLL_AMOUNT : SCROLL_AMOUNT;
carouselRef.current.scrollBy({
left: scrollAmount,
behavior: 'smooth'
});
setTimeout(() => {
if (carouselRef.current) {
setScrollPosition(carouselRef.current.scrollLeft);
}
}, 300);
}
};
useEffect(() => {
const handleScroll = () => {
if (carouselRef.current) {
setScrollPosition(carouselRef.current.scrollLeft);
}
};
const ref = carouselRef.current;
if (ref) {
ref.addEventListener('scroll', handleScroll);
return () => ref.removeEventListener('scroll', handleScroll);
}
}, []);
return (
<Box mb={6}>
<Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
<Typography variant="h4" component="h2" fontWeight="bold">
{title}
</Typography>
<Link href={viewAllLink} style={{textDecoration: 'none'}}>
<Typography component="span" color="primary">
View All
</Typography>
</Link>
</Box>
<Box position="relative">
<IconButton
sx={{
position: 'absolute',
left: -20,
top: '50%',
transform: 'translateY(-50%)',
zIndex: 2,
backgroundColor: 'background.paper',
boxShadow: 2,
'&:hover': {
backgroundColor: 'action.hover',
},
visibility: scrollPosition <= 5 ? 'hidden' : 'visible',
}}
onClick={() => scroll('left')}
>
<ArrowBackIosNewIcon />
</IconButton>
<Box
ref={carouselRef}
sx={{
display: 'flex',
overflowX: 'auto',
scrollbarWidth: 'none',
msOverflowStyle: 'none',
'&::-webkit-scrollbar': {
display: 'none',
},
gap: '16px', // Fixed 16px gap - using string with px unit to ensure it's absolute
padding: '8px 4px',
}}
>
{items?.map((item, index) => (
<Box
key={index}
sx={{
flex: '0 0 auto',
width: {
xs: '260px', // Fixed width at different breakpoints
sm: '280px',
md: '300px'
}
}}
>
{renderItem(item)}
</Box>
))}
</Box>
<IconButton
sx={{
position: 'absolute',
right: -20,
top: '50%',
transform: 'translateY(-50%)',
zIndex: 2,
backgroundColor: 'background.paper',
boxShadow: 2,
'&:hover': {
backgroundColor: 'action.hover',
},
visibility: scrollPosition >= maxScroll - 5 ? 'hidden' : 'visible',
}}
onClick={() => scroll('right')}
>
<ArrowForwardIosIcon />
</IconButton>
</Box>
</Box>
);
}

Some files were not shown because too many files have changed in this diff Show More