Skip to content

@astrolabe/server

The grading endpoint (protocol 1) on the standard Fetch API, with the attempt store contract and an in-memory store.

One attempt as the grading handler sees it. The key never leaves the server.

Property Modifier Type Description
checksUsed readonly number Checks counted so far.
createdAt readonly string ISO 8601.
id readonly string -
key readonly AnswerKey -
policy readonly AttemptPolicy -
submitted readonly boolean -

Storage the grading handler needs. Both writes must be atomic per attempt (for example UPDATE ... RETURNING or a conditional update), so parallel requests can neither exceed maxChecks nor submit twice.

get(id): Promise<
| AttemptRecord
| null>;

null for an unknown attempt.

Parameter Type
id string

Promise< | AttemptRecord | null>

recordCheck(id): Promise<
| AttemptRecord
| null>;

Counts one check; resolves to the record after counting, or null for an unknown attempt.

Parameter Type
id string

Promise< | AttemptRecord | null>

recordSubmit(id, submission): Promise<boolean>;

Closes the attempt with its submission; resolves false when it was already submitted (or is unknown).

Parameter Type
id string
submission Submission

Promise<boolean>


Which attempt a request is for, as the host resolved it after authorising the request.

Property Modifier Type
attemptId readonly string

Property Modifier Type Description
attempts readonly AttemptStore -
engine readonly Engine -
now? readonly () => number Clock in milliseconds, for deadlines and submittedAt. Default Date.now.
onError? readonly (error, request) => void Called with every internal failure (a store that throws or returns an inconsistent record, a grading error), for logging. The response stays a generic 500 internal_error either way. Default: nothing.

Storage the grading handler needs. Both writes must be atomic per attempt (for example UPDATE ... RETURNING or a conditional update), so parallel requests can neither exceed maxChecks nor submit twice.

create(attempt): Promise<AttemptRecord>;

Starts an attempt with a random id. Throws a TypeError for an invalid key or policy.

Parameter Type
attempt NewAttempt

Promise<AttemptRecord>

get(id): Promise<
| AttemptRecord
| null>;

null for an unknown attempt.

Parameter Type
id string

Promise< | AttemptRecord | null>

AttemptStore.get

recordCheck(id): Promise<
| AttemptRecord
| null>;

Counts one check; resolves to the record after counting, or null for an unknown attempt.

Parameter Type
id string

Promise< | AttemptRecord | null>

AttemptStore.recordCheck

recordSubmit(id, submission): Promise<boolean>;

Closes the attempt with its submission; resolves false when it was already submitted (or is unknown).

Parameter Type
id string
submission Submission

Promise<boolean>

AttemptStore.recordSubmit

submission(id): Promise<Submission | null>;

The stored submission of an attempt, or null.

Parameter Type
id string

Promise<Submission | null>


Property Modifier Type
key readonly AnswerKey
policy readonly AttemptPolicy

What a submit stores: the final answers and the response the student got (re-grade the answers to release more later). The answers are those within their slot kind's length limit, open answers (for the teacher) included: Metadata.open lists their slot ids.

Property Modifier Type
answers readonly Answers
response readonly SubmitResponse
type GradingHandler = (request, context) => Promise<Response>;

Handles POST {endpoint}/check and POST {endpoint}/submit. The host passes the attempt: the handler reads only the action (the final path segment) from the URL, so no part of the URL can point it at another attempt.

Parameter Type
request Request
context GradingContext

Promise<Response>

const ATTEMPT_ID_PATTERN: RegExp;

Attempt ids the handler accepts; anything else is answered 404 not_found.


const DEADLINE_GRACE_MS: 30000 = 30_000;

A submit this late after the deadline still counts: the player hands in when its countdown ends, and that request needs a moment to arrive. Later submits are refused like any submit on a closed attempt.

function createGradingHandler(options): GradingHandler;

Creates the grading endpoint. It answers only protocol-1 requests and never sends key data: check responses carry the GradeResult, and solutions go out with a submit only when the attempt's policy reveals answers.

Parameter Type
options GradingHandlerOptions

GradingHandler


function createMemoryAttemptStore(): MemoryAttemptStore;

An AttemptStore in a Map: fine for one process; attempts are lost on restart.

MemoryAttemptStore