feat(core): scaffold account manager platform
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@minecraft-account-manager/contracts",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^4.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const isoDateTimeSchema = z.iso.datetime({ offset: true });
|
||||
const minecraftUuidSchema = z
|
||||
.string()
|
||||
.regex(/^[0-9a-f]{32}$/i, "Expected a compact Java Edition UUID");
|
||||
const minecraftUsernameSchema = z
|
||||
.string()
|
||||
.regex(/^[A-Za-z0-9_]{3,16}$/, "Expected a valid Java Edition username");
|
||||
|
||||
export const cloudEventSchema = z.object({
|
||||
id: z.uuid(),
|
||||
specversion: z.literal("1.0"),
|
||||
source: z.string().startsWith("/"),
|
||||
type: z.string().min(1),
|
||||
subject: z.string().min(1).optional(),
|
||||
time: isoDateTimeSchema,
|
||||
datacontenttype: z.literal("application/json").default("application/json"),
|
||||
dataschema: z.url().optional(),
|
||||
data: z.record(z.string(), z.unknown()),
|
||||
});
|
||||
|
||||
export type CloudEvent = z.infer<typeof cloudEventSchema>;
|
||||
|
||||
export const velocityAccessRequestSchema = z.object({
|
||||
requestId: z.uuid(),
|
||||
serverId: z.string().min(1).max(100),
|
||||
minecraftUuid: minecraftUuidSchema,
|
||||
username: minecraftUsernameSchema,
|
||||
ipAddress: z.union([z.ipv4(), z.ipv6()]),
|
||||
occurredAt: isoDateTimeSchema,
|
||||
});
|
||||
|
||||
export type VelocityAccessRequest = z.infer<typeof velocityAccessRequestSchema>;
|
||||
|
||||
export const velocityAccessResponseSchema = z.discriminatedUnion("allowed", [
|
||||
z.object({
|
||||
allowed: z.literal(true),
|
||||
message: z.string().min(1).optional(),
|
||||
}),
|
||||
z.object({
|
||||
allowed: z.literal(false),
|
||||
message: z.string().min(1),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type VelocityAccessResponse = z.infer<typeof velocityAccessResponseSchema>;
|
||||
@@ -0,0 +1,51 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
cloudEventSchema,
|
||||
velocityAccessRequestSchema,
|
||||
velocityAccessResponseSchema,
|
||||
} from "../src/index";
|
||||
|
||||
describe("shared service contracts", () => {
|
||||
it("accepts a game login event in CloudEvents format", () => {
|
||||
const event = cloudEventSchema.parse({
|
||||
id: "8cf5b035-4368-4fa8-b9bb-2835da752f20",
|
||||
specversion: "1.0",
|
||||
source: "/velocity/main",
|
||||
type: "games.minecraft.account-manager.game.login.allowed",
|
||||
subject: "minecraft-account/8667ba71b85a4004af54457a9734eed7",
|
||||
time: "2026-03-06T12:00:00.000Z",
|
||||
datacontenttype: "application/json",
|
||||
data: { username: "Notch" },
|
||||
});
|
||||
|
||||
expect(event.specversion).toBe("1.0");
|
||||
});
|
||||
|
||||
it("requires Velocity to send a Java UUID, username, and IP address", () => {
|
||||
const request = velocityAccessRequestSchema.parse({
|
||||
requestId: "6f1fd065-5298-42fa-b1ad-87a8916c6a8a",
|
||||
serverId: "velocity-main",
|
||||
minecraftUuid: "069a79f444e94726a5befca90e38aaf5",
|
||||
username: "Notch",
|
||||
ipAddress: "203.0.113.10",
|
||||
occurredAt: "2026-03-06T12:00:00.000Z",
|
||||
});
|
||||
|
||||
expect(request.username).toBe("Notch");
|
||||
expect(() =>
|
||||
velocityAccessRequestSchema.parse({ ...request, minecraftUuid: "not-a-uuid" }),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it("only returns explicit allow or deny decisions to Velocity", () => {
|
||||
expect(
|
||||
velocityAccessResponseSchema.parse({
|
||||
allowed: false,
|
||||
message: "Please register your Minecraft account before joining.",
|
||||
}),
|
||||
).toEqual({
|
||||
allowed: false,
|
||||
message: "Please register your Minecraft account before joining.",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": ["vitest/globals"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "test/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user