diff --git a/federation/cryptography/index.ts b/federation/cryptography/index.ts
index 2add7c8..b386f90 100644
--- a/federation/cryptography/index.ts
+++ b/federation/cryptography/index.ts
@@ -321,9 +321,7 @@ export class SignatureConstructor {
}\n` +
`host: ${url.host}\n` +
`date: ${finalDate}\n` +
- `digest: SHA-256=${Buffer.from(new Uint8Array(digest)).toString(
- "base64",
- )}\n`;
+ `digest: SHA-256=${Buffer.from(digest).toString("base64")}\n`;
const signature = await crypto.subtle.sign(
"Ed25519",
diff --git a/federation/http/index.test.ts b/federation/http/index.test.ts
new file mode 100644
index 0000000..deaf372
--- /dev/null
+++ b/federation/http/index.test.ts
@@ -0,0 +1,95 @@
+import { beforeEach, describe, expect, jest, test } from "bun:test";
+import { RequestParserHandler } from "../http/index.ts";
+import { EntityValidator } from "../validator/index.ts";
+
+// Pulled from social.lysand.org
+const validUser = {
+ id: "018eb863-753f-76ff-83d6-fd590de7740a",
+ type: "User",
+ uri: "https://social.lysand.org/users/018eb863-753f-76ff-83d6-fd590de7740a",
+ bio: {
+ "text/html": {
+ content: "
Hey
\n",
+ },
+ },
+ created_at: "2024-04-07T11:48:29.623Z",
+ dislikes:
+ "https://social.lysand.org/users/018eb863-753f-76ff-83d6-fd590de7740a/dislikes",
+ featured:
+ "https://social.lysand.org/users/018eb863-753f-76ff-83d6-fd590de7740a/featured",
+ likes: "https://social.lysand.org/users/018eb863-753f-76ff-83d6-fd590de7740a/likes",
+ followers:
+ "https://social.lysand.org/users/018eb863-753f-76ff-83d6-fd590de7740a/followers",
+ following:
+ "https://social.lysand.org/users/018eb863-753f-76ff-83d6-fd590de7740a/following",
+ inbox: "https://social.lysand.org/users/018eb863-753f-76ff-83d6-fd590de7740a/inbox",
+ outbox: "https://social.lysand.org/users/018eb863-753f-76ff-83d6-fd590de7740a/outbox",
+ indexable: false,
+ username: "jessew",
+ display_name: "Jesse Wierzbinski",
+ fields: [
+ {
+ key: { "text/html": { content: "Identity
\n" } },
+ value: {
+ "text/html": {
+ content:
+ 'https://keyoxide.org/aspe:keyoxide.org:NKLLPWPV7P35NEU7JP4K4ID4CA
\n',
+ },
+ },
+ },
+ ],
+ public_key: {
+ actor: "https://social.lysand.org/users/018eb863-753f-76ff-83d6-fd590de7740a",
+ public_key: "XXXXXXXX",
+ },
+ extensions: { "org.lysand:custom_emojis": { emojis: [] } },
+};
+
+describe("LysandRequestHandler", () => {
+ let validator: EntityValidator;
+
+ beforeEach(() => {
+ validator = new EntityValidator();
+ });
+
+ test("parseBody with valid User", async () => {
+ const handler = new RequestParserHandler(validUser, validator);
+
+ const noteCallback = jest.fn();
+ await handler.parseBody({ user: noteCallback });
+
+ expect(noteCallback).toHaveBeenCalled();
+ });
+
+ test("Throw on invalid Note", async () => {
+ const handler = new RequestParserHandler(
+ {
+ type: "Note",
+ body: "bad",
+ },
+ validator,
+ );
+
+ const noteCallback = jest.fn();
+ await expect(
+ handler.parseBody({ note: noteCallback }),
+ ).rejects.toThrow();
+ expect(noteCallback).not.toHaveBeenCalled();
+ });
+
+ test("Throw on incorrect body type property", async () => {
+ const handler = new RequestParserHandler(
+ {
+ type: "DoesntExist",
+ body: "bad",
+ },
+ validator,
+ );
+
+ const noteCallback = jest.fn();
+ await expect(
+ handler.parseBody({ note: noteCallback }),
+ ).rejects.toThrow();
+ expect(noteCallback).not.toHaveBeenCalled();
+ });
+});
diff --git a/federation/http/index.ts b/federation/http/index.ts
new file mode 100644
index 0000000..2e9fab4
--- /dev/null
+++ b/federation/http/index.ts
@@ -0,0 +1,129 @@
+import type { EntityValidator } from "../validator/index";
+
+type MaybePromise = T | Promise;
+
+type ParserCallbacks = {
+ note: (note: typeof EntityValidator.$Note) => MaybePromise;
+ follow: (follow: typeof EntityValidator.$Follow) => MaybePromise;
+ followAccept: (
+ followAccept: typeof EntityValidator.$FollowAccept,
+ ) => MaybePromise;
+ followReject: (
+ followReject: typeof EntityValidator.$FollowReject,
+ ) => MaybePromise;
+ user: (user: typeof EntityValidator.$User) => MaybePromise;
+ like: (like: typeof EntityValidator.$Like) => MaybePromise;
+ dislike: (dislike: typeof EntityValidator.$Dislike) => MaybePromise;
+ undo: (undo: typeof EntityValidator.$Undo) => MaybePromise;
+ serverMetadata: (
+ serverMetadata: typeof EntityValidator.$ServerMetadata,
+ ) => MaybePromise;
+ extension: (
+ extension: typeof EntityValidator.$Extension,
+ ) => MaybePromise;
+};
+
+export class RequestParserHandler {
+ constructor(
+ private readonly body: Record<
+ string,
+ string | number | object | boolean | null
+ >,
+ private readonly validator: EntityValidator,
+ ) {}
+
+ /**
+ * Parse the body of the request and call the appropriate callback.
+ * @param callbacks The callbacks to call when a specific entity is found.
+ * @returns A promise that resolves when the body has been parsed, and the callbacks have finished executing.
+ */
+ public async parseBody(callbacks: Partial): Promise {
+ if (!this.body.type) throw new Error("Missing type field in body");
+
+ switch (this.body.type) {
+ case "Note": {
+ const note = await this.validator.Note(this.body);
+
+ if (callbacks.note) await callbacks.note(note);
+
+ break;
+ }
+ case "Follow": {
+ const follow = await this.validator.Follow(this.body);
+
+ if (callbacks.follow) await callbacks.follow(follow);
+
+ break;
+ }
+ case "FollowAccept": {
+ const followAccept = await this.validator.FollowAccept(
+ this.body,
+ );
+
+ if (callbacks.followAccept)
+ await callbacks.followAccept(followAccept);
+
+ break;
+ }
+ case "FollowReject": {
+ const followReject = await this.validator.FollowReject(
+ this.body,
+ );
+
+ if (callbacks.followReject)
+ await callbacks.followReject(followReject);
+
+ break;
+ }
+ case "User": {
+ const user = await this.validator.User(this.body);
+
+ if (callbacks.user) await callbacks.user(user);
+
+ break;
+ }
+ case "Like": {
+ const like = await this.validator.Like(this.body);
+
+ if (callbacks.like) await callbacks.like(like);
+
+ break;
+ }
+ case "Dislike": {
+ const dislike = await this.validator.Dislike(this.body);
+
+ if (callbacks.dislike) await callbacks.dislike(dislike);
+
+ break;
+ }
+ case "Undo": {
+ const undo = await this.validator.Undo(this.body);
+
+ if (callbacks.undo) await callbacks.undo(undo);
+
+ break;
+ }
+ case "ServerMetadata": {
+ const serverMetadata = await this.validator.ServerMetadata(
+ this.body,
+ );
+
+ if (callbacks.serverMetadata)
+ await callbacks.serverMetadata(serverMetadata);
+
+ break;
+ }
+ case "Extension": {
+ const extension = await this.validator.Extension(this.body);
+
+ if (callbacks.extension) await callbacks.extension(extension);
+
+ break;
+ }
+ default:
+ throw new Error(
+ `Invalid type field in body: ${this.body.type}`,
+ );
+ }
+ }
+}
diff --git a/federation/index.ts b/federation/index.ts
index c92887d..76cbdf9 100644
--- a/federation/index.ts
+++ b/federation/index.ts
@@ -5,323 +5,15 @@
* @see module:federation/schemas/base
*/
-import type { z } from "zod";
-import { type ValidationError, fromError } from "zod-validation-error";
+import type { ValidationError } from "zod-validation-error";
import { SignatureConstructor, SignatureValidator } from "./cryptography";
-import {
- ActionSchema,
- ActorPublicKeyDataSchema,
- ContentFormatSchema,
- CustomEmojiExtension,
- DislikeSchema,
- EntitySchema,
- ExtensionPropertySchema,
- ExtensionSchema,
- FollowAcceptSchema,
- FollowRejectSchema,
- FollowSchema,
- LikeSchema,
- NoteSchema,
- PatchSchema,
- PublicationSchema,
- ReportSchema,
- ServerMetadataSchema,
- UndoSchema,
- UserSchema,
- VanityExtensionSchema,
- VisibilitySchema,
-} from "./schemas/base";
-
-// biome-ignore lint/suspicious/noExplicitAny: Used only as a base type
-type AnyZod = z.ZodType;
-
-type InferType = z.infer;
-
-/**
- * Validates entities against their respective schemas.
- * @module federation/validator
- * @see module:federation/schemas/base
- * @example
- * import { EntityValidator, type ValidationError } from "@lysand-org/federation";
- * const validator = new EntityValidator();
- *
- * // Will throw a special ValidationError with a human-friendly error message
- * // and a machine-friendly error object if the data is invalid.
- * const note = await validator.Note({
- * type: "Note",
- * content: "Hello, world!",
- * });
- *
- * try {
- * await validator.Note({
- * type: "Note",
- * content: 123,
- * });
- * } catch (error) {
- * sendUser((error as ValidationError).toString());
- * }
- *
- * // Types are also included for TypeScript users that don't use the extracted ones
- * const noteObject: typeof EntityValidator.$Note = {
- * type: "Note",
- * // ...
- * }
- */
-class EntityValidator {
- private async validate(
- schema: T,
- data: unknown,
- ): Promise> {
- try {
- return (await schema.parseAsync(data)) as InferType;
- } catch (error) {
- throw fromError(error);
- }
- }
-
- declare static $Note: InferType;
- declare static $Patch: InferType;
- declare static $ActorPublicKeyData: InferType<
- typeof ActorPublicKeyDataSchema
- >;
- declare static $ExtensionProperty: InferType<
- typeof ExtensionPropertySchema
- >;
- declare static $VanityExtension: InferType;
- declare static $User: InferType;
- declare static $Action: InferType;
- declare static $Like: InferType;
- declare static $Undo: InferType;
- declare static $Dislike: InferType;
- declare static $Follow: InferType;
- declare static $FollowAccept: InferType;
- declare static $FollowReject: InferType;
- declare static $Extension: InferType;
- declare static $Report: InferType;
- declare static $ServerMetadata: InferType;
- declare static $ContentFormat: InferType;
- declare static $CustomEmojiExtension: InferType<
- typeof CustomEmojiExtension
- >;
- declare static $Visibility: InferType;
- declare static $Publication: InferType;
- declare static $Entity: InferType;
-
- /**
- * Validates a Note entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Note(data: unknown): Promise> {
- return this.validate(NoteSchema, data);
- }
-
- /**
- * Validates a Patch entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Patch(data: unknown): Promise> {
- return this.validate(PatchSchema, data);
- }
-
- /**
- * Validates an ActorPublicKeyData entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public ActorPublicKeyData(
- data: unknown,
- ): Promise> {
- return this.validate(ActorPublicKeyDataSchema, data);
- }
-
- /**
- * Validates a VanityExtension entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public VanityExtension(
- data: unknown,
- ): Promise> {
- return this.validate(VanityExtensionSchema, data);
- }
-
- /**
- * Validates a User entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public User(data: unknown): Promise> {
- return this.validate(UserSchema, data);
- }
-
- /**
- * Validates an Action entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Action(data: unknown): Promise> {
- return this.validate(ActionSchema, data);
- }
-
- /**
- * Validates a Like entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Like(data: unknown): Promise> {
- return this.validate(LikeSchema, data);
- }
-
- /**
- * Validates an Undo entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Undo(data: unknown): Promise> {
- return this.validate(UndoSchema, data);
- }
-
- /**
- * Validates a Dislike entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Dislike(data: unknown): Promise> {
- return this.validate(DislikeSchema, data);
- }
-
- /**
- * Validates a Follow entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Follow(data: unknown): Promise> {
- return this.validate(FollowSchema, data);
- }
-
- /**
- * Validates a FollowAccept entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public FollowAccept(
- data: unknown,
- ): Promise> {
- return this.validate(FollowAcceptSchema, data);
- }
-
- /**
- * Validates a FollowReject entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public FollowReject(
- data: unknown,
- ): Promise> {
- return this.validate(FollowRejectSchema, data);
- }
-
- /**
- * Validates an Extension entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Extension(
- data: unknown,
- ): Promise> {
- return this.validate(ExtensionSchema, data);
- }
-
- /**
- * Validates a Report entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Report(data: unknown): Promise> {
- return this.validate(ReportSchema, data);
- }
-
- /**
- * Validates a ServerMetadata entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public ServerMetadata(
- data: unknown,
- ): Promise> {
- return this.validate(ServerMetadataSchema, data);
- }
-
- /**
- * Validates a ContentFormat entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public ContentFormat(
- data: unknown,
- ): Promise> {
- return this.validate(ContentFormatSchema, data);
- }
-
- /**
- * Validates a CustomEmojiExtension entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public CustomEmojiExtension(
- data: unknown,
- ): Promise> {
- return this.validate(CustomEmojiExtension, data);
- }
-
- /**
- * Validates a Visibility entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Visibility(
- data: unknown,
- ): Promise> {
- return this.validate(VisibilitySchema, data);
- }
-
- /**
- * Validates a Publication entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Publication(
- data: unknown,
- ): Promise> {
- return this.validate(PublicationSchema, data);
- }
-
- /**
- * Validates an Entity.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public Entity(data: unknown): Promise> {
- return this.validate(EntitySchema, data);
- }
-
- /**
- * Validates an ExtensionProperty.
- * @param data - The data to validate
- * @returns A promise that resolves to the validated data.
- */
- public ExtensionProperty(
- data: unknown,
- ): Promise> {
- return this.validate(ExtensionPropertySchema, data);
- }
-}
+import { RequestParserHandler } from "./http";
+import { EntityValidator } from "./validator";
export {
EntityValidator,
type ValidationError,
SignatureConstructor,
SignatureValidator,
+ RequestParserHandler,
};
diff --git a/federation/tests/index.test.ts b/federation/tests/index.test.ts
index b802cb5..972510d 100644
--- a/federation/tests/index.test.ts
+++ b/federation/tests/index.test.ts
@@ -1,5 +1,4 @@
import { describe, expect, it } from "bun:test";
-import type { ValidationError } from "zod-validation-error";
import { EntityValidator } from "../index";
describe("Package testing", () => {
diff --git a/federation/validator/index.ts b/federation/validator/index.ts
new file mode 100644
index 0000000..3a048a3
--- /dev/null
+++ b/federation/validator/index.ts
@@ -0,0 +1,312 @@
+import type { z } from "zod";
+import { fromError } from "zod-validation-error";
+import {
+ ActionSchema,
+ ActorPublicKeyDataSchema,
+ ContentFormatSchema,
+ CustomEmojiExtension,
+ DislikeSchema,
+ EntitySchema,
+ ExtensionPropertySchema,
+ ExtensionSchema,
+ FollowAcceptSchema,
+ FollowRejectSchema,
+ FollowSchema,
+ LikeSchema,
+ NoteSchema,
+ PatchSchema,
+ PublicationSchema,
+ ReportSchema,
+ ServerMetadataSchema,
+ UndoSchema,
+ UserSchema,
+ VanityExtensionSchema,
+ VisibilitySchema,
+} from "../schemas/base";
+
+// biome-ignore lint/suspicious/noExplicitAny: Used only as a base type
+type AnyZod = z.ZodType;
+
+type InferType = z.infer;
+
+/**
+ * Validates entities against their respective schemas.
+ * @module federation/validator
+ * @see module:federation/schemas/base
+ * @example
+ * import { EntityValidator, type ValidationError } from "@lysand-org/federation";
+ * const validator = new EntityValidator();
+ *
+ * // Will throw a special ValidationError with a human-friendly error message
+ * // and a machine-friendly error object if the data is invalid.
+ * const note = await validator.Note({
+ * type: "Note",
+ * content: "Hello, world!",
+ * });
+ *
+ * try {
+ * await validator.Note({
+ * type: "Note",
+ * content: 123,
+ * });
+ * } catch (error) {
+ * sendUser((error as ValidationError).toString());
+ * }
+ *
+ * // Types are also included for TypeScript users that don't use the extracted ones
+ * const noteObject: typeof EntityValidator.$Note = {
+ * type: "Note",
+ * // ...
+ * }
+ */
+export class EntityValidator {
+ private async validate(
+ schema: T,
+ data: unknown,
+ ): Promise> {
+ try {
+ return (await schema.parseAsync(data)) as InferType;
+ } catch (error) {
+ throw fromError(error);
+ }
+ }
+
+ declare static $Note: InferType;
+ declare static $Patch: InferType;
+ declare static $ActorPublicKeyData: InferType<
+ typeof ActorPublicKeyDataSchema
+ >;
+ declare static $ExtensionProperty: InferType<
+ typeof ExtensionPropertySchema
+ >;
+ declare static $VanityExtension: InferType;
+ declare static $User: InferType;
+ declare static $Action: InferType;
+ declare static $Like: InferType;
+ declare static $Undo: InferType;
+ declare static $Dislike: InferType;
+ declare static $Follow: InferType;
+ declare static $FollowAccept: InferType;
+ declare static $FollowReject: InferType;
+ declare static $Extension: InferType;
+ declare static $Report: InferType;
+ declare static $ServerMetadata: InferType;
+ declare static $ContentFormat: InferType;
+ declare static $CustomEmojiExtension: InferType<
+ typeof CustomEmojiExtension
+ >;
+ declare static $Visibility: InferType;
+ declare static $Publication: InferType;
+ declare static $Entity: InferType;
+
+ /**
+ * Validates a Note entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Note(data: unknown): Promise> {
+ return this.validate(NoteSchema, data);
+ }
+
+ /**
+ * Validates a Patch entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Patch(data: unknown): Promise> {
+ return this.validate(PatchSchema, data);
+ }
+
+ /**
+ * Validates an ActorPublicKeyData entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public ActorPublicKeyData(
+ data: unknown,
+ ): Promise> {
+ return this.validate(ActorPublicKeyDataSchema, data);
+ }
+
+ /**
+ * Validates a VanityExtension entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public VanityExtension(
+ data: unknown,
+ ): Promise> {
+ return this.validate(VanityExtensionSchema, data);
+ }
+
+ /**
+ * Validates a User entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public User(data: unknown): Promise> {
+ return this.validate(UserSchema, data);
+ }
+
+ /**
+ * Validates an Action entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Action(data: unknown): Promise> {
+ return this.validate(ActionSchema, data);
+ }
+
+ /**
+ * Validates a Like entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Like(data: unknown): Promise> {
+ return this.validate(LikeSchema, data);
+ }
+
+ /**
+ * Validates an Undo entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Undo(data: unknown): Promise> {
+ return this.validate(UndoSchema, data);
+ }
+
+ /**
+ * Validates a Dislike entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Dislike(data: unknown): Promise> {
+ return this.validate(DislikeSchema, data);
+ }
+
+ /**
+ * Validates a Follow entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Follow(data: unknown): Promise> {
+ return this.validate(FollowSchema, data);
+ }
+
+ /**
+ * Validates a FollowAccept entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public FollowAccept(
+ data: unknown,
+ ): Promise> {
+ return this.validate(FollowAcceptSchema, data);
+ }
+
+ /**
+ * Validates a FollowReject entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public FollowReject(
+ data: unknown,
+ ): Promise> {
+ return this.validate(FollowRejectSchema, data);
+ }
+
+ /**
+ * Validates an Extension entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Extension(
+ data: unknown,
+ ): Promise> {
+ return this.validate(ExtensionSchema, data);
+ }
+
+ /**
+ * Validates a Report entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Report(data: unknown): Promise> {
+ return this.validate(ReportSchema, data);
+ }
+
+ /**
+ * Validates a ServerMetadata entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public ServerMetadata(
+ data: unknown,
+ ): Promise> {
+ return this.validate(ServerMetadataSchema, data);
+ }
+
+ /**
+ * Validates a ContentFormat entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public ContentFormat(
+ data: unknown,
+ ): Promise> {
+ return this.validate(ContentFormatSchema, data);
+ }
+
+ /**
+ * Validates a CustomEmojiExtension entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public CustomEmojiExtension(
+ data: unknown,
+ ): Promise> {
+ return this.validate(CustomEmojiExtension, data);
+ }
+
+ /**
+ * Validates a Visibility entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Visibility(
+ data: unknown,
+ ): Promise> {
+ return this.validate(VisibilitySchema, data);
+ }
+
+ /**
+ * Validates a Publication entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Publication(
+ data: unknown,
+ ): Promise> {
+ return this.validate(PublicationSchema, data);
+ }
+
+ /**
+ * Validates an Entity.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public Entity(data: unknown): Promise> {
+ return this.validate(EntitySchema, data);
+ }
+
+ /**
+ * Validates an ExtensionProperty.
+ * @param data - The data to validate
+ * @returns A promise that resolves to the validated data.
+ */
+ public ExtensionProperty(
+ data: unknown,
+ ): Promise> {
+ return this.validate(ExtensionPropertySchema, data);
+ }
+}