mirror of
https://github.com/versia-pub/api.git
synced 2026-03-13 12:19:15 +01:00
refactor: ♻️ Refactor naming and code exports for both modules
This commit is contained in:
parent
8a37e7df95
commit
dc352bc276
29 changed files with 629 additions and 312 deletions
|
|
@ -10,9 +10,9 @@ type HttpVerb =
|
|||
| "OPTIONS"
|
||||
| "HEAD";
|
||||
|
||||
const checkEvironmentSupport = async () => {
|
||||
const checkEvironmentSupport = () => {
|
||||
// Check if WebCrypto is supported
|
||||
if (!globalThis.crypto || !globalThis.crypto.subtle) {
|
||||
if (!globalThis.crypto?.subtle) {
|
||||
throw new Error("WebCrypto is not supported in this environment");
|
||||
}
|
||||
|
||||
|
|
@ -27,9 +27,9 @@ const checkEvironmentSupport = async () => {
|
|||
export class SignatureValidator {
|
||||
/**
|
||||
* Creates a new instance of SignatureValidator.
|
||||
* @param public_key The public key used for signature verification.
|
||||
* @param publicKey The public key used for signature verification.
|
||||
*/
|
||||
constructor(private public_key: CryptoKey) {
|
||||
constructor(private publicKey: CryptoKey) {
|
||||
checkEvironmentSupport();
|
||||
}
|
||||
|
||||
|
|
@ -114,9 +114,9 @@ export class SignatureValidator {
|
|||
].filter(Boolean);
|
||||
|
||||
// Check if all headers are present
|
||||
if (!signature || !date || !method || !url || !body) {
|
||||
if (!(signature && date && method && url && body)) {
|
||||
// Say which headers are missing
|
||||
throw TypeError(
|
||||
throw new TypeError(
|
||||
`Headers are missing in request: ${missingHeaders.join(
|
||||
", ",
|
||||
)}`,
|
||||
|
|
@ -124,7 +124,7 @@ export class SignatureValidator {
|
|||
}
|
||||
|
||||
if (signature.split("signature=").length < 2) {
|
||||
throw TypeError(
|
||||
throw new TypeError(
|
||||
"Invalid Signature header (wrong format or missing signature)",
|
||||
);
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ export class SignatureValidator {
|
|||
.replace(/"/g, "");
|
||||
|
||||
if (!extractedSignature) {
|
||||
throw TypeError(
|
||||
throw new TypeError(
|
||||
"Invalid Signature header (wrong format or missing signature)",
|
||||
);
|
||||
}
|
||||
|
|
@ -148,8 +148,8 @@ export class SignatureValidator {
|
|||
);
|
||||
}
|
||||
|
||||
if (!date || !method || !url || !body) {
|
||||
throw TypeError(
|
||||
if (!(date && method && url && body)) {
|
||||
throw new TypeError(
|
||||
"Missing or empty required parameters: date, method, url or body",
|
||||
);
|
||||
}
|
||||
|
|
@ -172,7 +172,7 @@ export class SignatureValidator {
|
|||
// Check if signed string is valid
|
||||
const isValid = await crypto.subtle.verify(
|
||||
"Ed25519",
|
||||
this.public_key,
|
||||
this.publicKey,
|
||||
Buffer.from(signature, "base64"),
|
||||
new TextEncoder().encode(expectedSignedString),
|
||||
);
|
||||
|
|
@ -302,8 +302,8 @@ export class SignatureConstructor {
|
|||
return { request, signedString };
|
||||
}
|
||||
|
||||
if (!url || !body || !headers) {
|
||||
throw TypeError(
|
||||
if (!(url && body && headers)) {
|
||||
throw new TypeError(
|
||||
"Missing or empty required parameters: url, body or headers",
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,27 +69,35 @@ export class RequestParserHandler {
|
|||
public async parseBody<ReturnType = void>(
|
||||
callbacks: Partial<ParserCallbacks<ReturnType>>,
|
||||
): Promise<ReturnType> {
|
||||
if (!this.body.type) throw new Error("Missing type field in body");
|
||||
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) return await callbacks.note(note);
|
||||
if (callbacks.note) {
|
||||
return await callbacks.note(note);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "Patch": {
|
||||
const patch = await this.validator.Patch(this.body);
|
||||
|
||||
if (callbacks.patch) return await callbacks.patch(patch);
|
||||
if (callbacks.patch) {
|
||||
return await callbacks.patch(patch);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "Follow": {
|
||||
const follow = await this.validator.Follow(this.body);
|
||||
|
||||
if (callbacks.follow) return await callbacks.follow(follow);
|
||||
if (callbacks.follow) {
|
||||
return await callbacks.follow(follow);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -98,8 +106,9 @@ export class RequestParserHandler {
|
|||
this.body,
|
||||
);
|
||||
|
||||
if (callbacks.followAccept)
|
||||
if (callbacks.followAccept) {
|
||||
return await callbacks.followAccept(followAccept);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -108,36 +117,45 @@ export class RequestParserHandler {
|
|||
this.body,
|
||||
);
|
||||
|
||||
if (callbacks.followReject)
|
||||
if (callbacks.followReject) {
|
||||
return await callbacks.followReject(followReject);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "User": {
|
||||
const user = await this.validator.User(this.body);
|
||||
|
||||
if (callbacks.user) return await callbacks.user(user);
|
||||
if (callbacks.user) {
|
||||
return await callbacks.user(user);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "Like": {
|
||||
const like = await this.validator.Like(this.body);
|
||||
|
||||
if (callbacks.like) return await callbacks.like(like);
|
||||
if (callbacks.like) {
|
||||
return await callbacks.like(like);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "Dislike": {
|
||||
const dislike = await this.validator.Dislike(this.body);
|
||||
|
||||
if (callbacks.dislike) return await callbacks.dislike(dislike);
|
||||
if (callbacks.dislike) {
|
||||
return await callbacks.dislike(dislike);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "Undo": {
|
||||
const undo = await this.validator.Undo(this.body);
|
||||
|
||||
if (callbacks.undo) return await callbacks.undo(undo);
|
||||
if (callbacks.undo) {
|
||||
return await callbacks.undo(undo);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -146,16 +164,18 @@ export class RequestParserHandler {
|
|||
this.body,
|
||||
);
|
||||
|
||||
if (callbacks.serverMetadata)
|
||||
if (callbacks.serverMetadata) {
|
||||
return await callbacks.serverMetadata(serverMetadata);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "Extension": {
|
||||
const extension = await this.validator.Extension(this.body);
|
||||
|
||||
if (callbacks.extension)
|
||||
if (callbacks.extension) {
|
||||
return await callbacks.extension(extension);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
"name": "@lysand-org/federation",
|
||||
"version": "0.0.0",
|
||||
"exports": {
|
||||
".": "./index.ts"
|
||||
".": "./index.ts",
|
||||
"./types": "./schemas.ts"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@
|
|||
".": {
|
||||
"import": "./index.ts",
|
||||
"default": "./index.ts"
|
||||
},
|
||||
"./types": {
|
||||
"import": "./schemas.ts",
|
||||
"default": "./schemas.ts"
|
||||
}
|
||||
},
|
||||
"funding": {
|
||||
|
|
|
|||
51
federation/schemas.ts
Normal file
51
federation/schemas.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import type { z } from "zod";
|
||||
import type {
|
||||
ActionSchema,
|
||||
ActorPublicKeyDataSchema,
|
||||
ContentFormatSchema,
|
||||
CustomEmojiExtensionSchema,
|
||||
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<any, any, any>;
|
||||
|
||||
type InferType<T extends AnyZod> = z.infer<T>;
|
||||
|
||||
export type Note = InferType<typeof NoteSchema>;
|
||||
export type Patch = InferType<typeof PatchSchema>;
|
||||
export type ActorPublicKeyData = InferType<typeof ActorPublicKeyDataSchema>;
|
||||
export type ExtensionProperty = InferType<typeof ExtensionPropertySchema>;
|
||||
export type VanityExtension = InferType<typeof VanityExtensionSchema>;
|
||||
export type User = InferType<typeof UserSchema>;
|
||||
export type Action = InferType<typeof ActionSchema>;
|
||||
export type Like = InferType<typeof LikeSchema>;
|
||||
export type Undo = InferType<typeof UndoSchema>;
|
||||
export type Dislike = InferType<typeof DislikeSchema>;
|
||||
export type Follow = InferType<typeof FollowSchema>;
|
||||
export type FollowAccept = InferType<typeof FollowAcceptSchema>;
|
||||
export type FollowReject = InferType<typeof FollowRejectSchema>;
|
||||
export type Extension = InferType<typeof ExtensionSchema>;
|
||||
export type Report = InferType<typeof ReportSchema>;
|
||||
export type ServerMetadata = InferType<typeof ServerMetadataSchema>;
|
||||
export type ContentFormat = InferType<typeof ContentFormatSchema>;
|
||||
export type CustomEmojiExtension = InferType<typeof CustomEmojiExtensionSchema>;
|
||||
export type Visibility = InferType<typeof VisibilitySchema>;
|
||||
export type Publication = InferType<typeof PublicationSchema>;
|
||||
export type Entity = InferType<typeof EntitySchema>;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { z } from "zod";
|
||||
import { ContentFormatSchema } from "./content_format";
|
||||
import { ExtensionPropertySchema } from "./extensions";
|
||||
import { CustomEmojiExtension } from "./extensions/custom_emojis";
|
||||
import { CustomEmojiExtensionSchema } from "./extensions/custom_emojis";
|
||||
import { VanityExtensionSchema } from "./extensions/vanity";
|
||||
import { extensionTypeRegex } from "./regex";
|
||||
|
||||
|
|
@ -187,6 +187,6 @@ export {
|
|||
ReportSchema,
|
||||
ServerMetadataSchema,
|
||||
ContentFormatSchema,
|
||||
CustomEmojiExtension,
|
||||
CustomEmojiExtensionSchema,
|
||||
ExtensionPropertySchema,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { z } from "zod";
|
||||
import { CustomEmojiExtension } from "./extensions/custom_emojis";
|
||||
import { CustomEmojiExtensionSchema } from "./extensions/custom_emojis";
|
||||
|
||||
export const ExtensionPropertySchema = z.object({
|
||||
"org.lysand:custom_emojis": CustomEmojiExtension.optional(),
|
||||
"org.lysand:custom_emojis": CustomEmojiExtensionSchema.optional(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import { emojiRegex } from "../regex";
|
|||
* // ...
|
||||
* }
|
||||
*/
|
||||
export const CustomEmojiExtension = z.object({
|
||||
export const CustomEmojiExtensionSchema = z.object({
|
||||
emojis: z.array(
|
||||
z.object({
|
||||
name: z
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
|||
import { EntityValidator } from "../index";
|
||||
|
||||
describe("Package testing", () => {
|
||||
it("should not validate a bad Note", async () => {
|
||||
it("should not validate a bad Note", () => {
|
||||
const badObject = {
|
||||
IamBad: "Note",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
ActionSchema,
|
||||
ActorPublicKeyDataSchema,
|
||||
ContentFormatSchema,
|
||||
CustomEmojiExtension,
|
||||
CustomEmojiExtensionSchema,
|
||||
DislikeSchema,
|
||||
EntitySchema,
|
||||
ExtensionPropertySchema,
|
||||
|
|
@ -54,10 +54,11 @@ type InferType<T extends AnyZod> = z.infer<T>;
|
|||
* }
|
||||
*
|
||||
* // Types are also included for TypeScript users that don't use the extracted ones
|
||||
* const noteObject: typeof EntityValidator.$Note = {
|
||||
* type: "Note",
|
||||
* // ...
|
||||
* }
|
||||
* import type { Note } from "@lysand-org/federation/types";
|
||||
*
|
||||
* const note: Note = {
|
||||
* ...
|
||||
* };
|
||||
*/
|
||||
export class EntityValidator {
|
||||
private async validate<T extends AnyZod>(
|
||||
|
|
@ -71,34 +72,6 @@ export class EntityValidator {
|
|||
}
|
||||
}
|
||||
|
||||
declare static $Note: InferType<typeof NoteSchema>;
|
||||
declare static $Patch: InferType<typeof PatchSchema>;
|
||||
declare static $ActorPublicKeyData: InferType<
|
||||
typeof ActorPublicKeyDataSchema
|
||||
>;
|
||||
declare static $ExtensionProperty: InferType<
|
||||
typeof ExtensionPropertySchema
|
||||
>;
|
||||
declare static $VanityExtension: InferType<typeof VanityExtensionSchema>;
|
||||
declare static $User: InferType<typeof UserSchema>;
|
||||
declare static $Action: InferType<typeof ActionSchema>;
|
||||
declare static $Like: InferType<typeof LikeSchema>;
|
||||
declare static $Undo: InferType<typeof UndoSchema>;
|
||||
declare static $Dislike: InferType<typeof DislikeSchema>;
|
||||
declare static $Follow: InferType<typeof FollowSchema>;
|
||||
declare static $FollowAccept: InferType<typeof FollowAcceptSchema>;
|
||||
declare static $FollowReject: InferType<typeof FollowRejectSchema>;
|
||||
declare static $Extension: InferType<typeof ExtensionSchema>;
|
||||
declare static $Report: InferType<typeof ReportSchema>;
|
||||
declare static $ServerMetadata: InferType<typeof ServerMetadataSchema>;
|
||||
declare static $ContentFormat: InferType<typeof ContentFormatSchema>;
|
||||
declare static $CustomEmojiExtension: InferType<
|
||||
typeof CustomEmojiExtension
|
||||
>;
|
||||
declare static $Visibility: InferType<typeof VisibilitySchema>;
|
||||
declare static $Publication: InferType<typeof PublicationSchema>;
|
||||
declare static $Entity: InferType<typeof EntitySchema>;
|
||||
|
||||
/**
|
||||
* Validates a Note entity.
|
||||
* @param data - The data to validate
|
||||
|
|
@ -264,8 +237,8 @@ export class EntityValidator {
|
|||
*/
|
||||
public CustomEmojiExtension(
|
||||
data: unknown,
|
||||
): Promise<InferType<typeof CustomEmojiExtension>> {
|
||||
return this.validate(CustomEmojiExtension, data);
|
||||
): Promise<InferType<typeof CustomEmojiExtensionSchema>> {
|
||||
return this.validate(CustomEmojiExtensionSchema, data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue