mirror of
https://github.com/MathMan05/Fermi.git
synced 2026-08-28 05:04:08 +00:00
init type updates
This commit is contained in:
@@ -147,6 +147,7 @@
|
||||
</body>
|
||||
<script src="/index.js" type="module"></script>
|
||||
<!-- <script src="/service.js" --->
|
||||
<!-- <script src="/typeChecker/chekerIndex.js" --->
|
||||
<!-- <script src="/audio/worklet/worklet.js" --->
|
||||
<!-- <script src="/utils/dirrWorker.js" --->
|
||||
</html>
|
||||
|
||||
@@ -18,6 +18,7 @@ import "./oauth2/auth.js";
|
||||
import "./audio/page.js";
|
||||
import "./404.js";
|
||||
import {Channel} from "./channel.js";
|
||||
import type * as C from "./typeChecker/chekerIndex.js";
|
||||
|
||||
if (window.location.pathname === "/app") {
|
||||
window.location.pathname = "/channels/@me";
|
||||
@@ -25,6 +26,17 @@ if (window.location.pathname === "/app") {
|
||||
export interface CustomHTMLDivElement extends HTMLDivElement {
|
||||
markdown: MarkDown;
|
||||
}
|
||||
declare global {
|
||||
interface Window {
|
||||
checker?: typeof C.Check;
|
||||
}
|
||||
}
|
||||
if (localStorage.getItem("checkTypes")) {
|
||||
const i = (await import(
|
||||
"/typeChecker/chekerIndex.js" as "./typeChecker/chekerIndex.js"
|
||||
)) as typeof C;
|
||||
window.checker = i.Check;
|
||||
}
|
||||
if (window.location.pathname.startsWith("/channels")) {
|
||||
let templateID = new URLSearchParams(window.location.search).get("templateID");
|
||||
await I18n.done;
|
||||
|
||||
@@ -722,6 +722,11 @@ class Localuser {
|
||||
}
|
||||
conectionChange = () => {};
|
||||
async handleEvent(temp: wsjson) {
|
||||
try {
|
||||
window.checker?.checkEvent(temp);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
if (temp.d._trace) this.handleTrace(temp.d._trace);
|
||||
if (getDeveloperSettings().gatewayLogging) console.debug(temp);
|
||||
if (temp.s) this.lastSequence = temp.s;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import {ArrayChecker} from "./checkers/array";
|
||||
import {BoolChecker} from "./checkers/bool";
|
||||
import {Never} from "./checkers/never";
|
||||
import {NumberChecker} from "./checkers/number";
|
||||
import {ObjectChecker} from "./checkers/object";
|
||||
import {StringChecker} from "./checkers/string";
|
||||
|
||||
export abstract class TypeChecker {
|
||||
abstract check(obj: unknown): void;
|
||||
static resolve(r: resolveable): TypeChecker {
|
||||
if (r instanceof TypeChecker) return r;
|
||||
if (typeof r === "string" || r instanceof RegExp) {
|
||||
return new StringChecker(r);
|
||||
} else if (r === String) {
|
||||
return new StringChecker();
|
||||
} else if (r === Boolean) {
|
||||
return new BoolChecker();
|
||||
} else if (r === Number) {
|
||||
return new NumberChecker();
|
||||
} else if (typeof r === "number") {
|
||||
return new NumberChecker(r);
|
||||
} else if (r instanceof Array) {
|
||||
return new ArrayChecker(r);
|
||||
} else if (r instanceof Object) {
|
||||
return new ObjectChecker(r as {[key: string]: resolveable});
|
||||
} else {
|
||||
return new Never();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type resolveable =
|
||||
| typeof String
|
||||
| string
|
||||
| number
|
||||
| typeof Number
|
||||
| RegExp
|
||||
| resolveable[]
|
||||
| {[key: string]: resolveable}
|
||||
| TypeChecker
|
||||
| typeof Boolean;
|
||||
@@ -0,0 +1,10 @@
|
||||
import {TypeChecker} from "../basetype";
|
||||
|
||||
export class Any extends TypeChecker {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
check() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import {resolveable, TypeChecker} from "../basetype";
|
||||
|
||||
export class ArrayChecker extends TypeChecker {
|
||||
arr: TypeChecker | TypeChecker[];
|
||||
constructor(arr: resolveable | resolveable[]) {
|
||||
super();
|
||||
if (arr instanceof Array) {
|
||||
this.arr = arr.map((_) => TypeChecker.resolve(_));
|
||||
} else {
|
||||
this.arr = TypeChecker.resolve(arr);
|
||||
}
|
||||
}
|
||||
check(obj: unknown) {
|
||||
if (!(obj instanceof Array)) throw new Error(`${obj} is not of type array`);
|
||||
if (this.arr instanceof Array) {
|
||||
if (this.arr.length !== obj.length) throw new Error(`${obj} does not match array schema`);
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
this.arr[i].check(obj[i]);
|
||||
}
|
||||
} else {
|
||||
for (const elm of obj) {
|
||||
this.arr.check(elm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import {TypeChecker} from "../basetype";
|
||||
|
||||
export class BoolChecker extends TypeChecker {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
check(obj: unknown): void {
|
||||
if (typeof obj !== "boolean") throw new Error(`${obj} is not a bool`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import {TypeChecker} from "../basetype";
|
||||
|
||||
export class Never extends TypeChecker {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
check(): void {
|
||||
throw new Error(`never`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import {TypeChecker} from "../basetype";
|
||||
|
||||
export class NullChecker extends TypeChecker {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
check(obj: unknown): void {
|
||||
if (obj !== null) throw new Error(`${obj} is not null`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import {resolveable, TypeChecker} from "../basetype";
|
||||
|
||||
export class Nullish extends TypeChecker {
|
||||
checker: TypeChecker;
|
||||
constructor(check: resolveable) {
|
||||
super();
|
||||
this.checker = TypeChecker.resolve(check);
|
||||
}
|
||||
check(obj: unknown) {
|
||||
if (obj === null) return;
|
||||
this.checker.check(obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import {TypeChecker} from "../basetype";
|
||||
|
||||
export class NumberChecker extends TypeChecker {
|
||||
num?: number;
|
||||
constructor(num?: number) {
|
||||
super();
|
||||
this.num = num;
|
||||
}
|
||||
check(obj: unknown): void {
|
||||
if (this.num === undefined) {
|
||||
if (typeof obj !== "number") throw new Error(`${obj} is not a number`);
|
||||
} else if (this.num !== obj) throw new Error(`${obj} and ${this.num} do not match`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import {resolveable, TypeChecker} from "../basetype";
|
||||
|
||||
export class ObjectChecker extends TypeChecker {
|
||||
obj: {[key: string]: TypeChecker} | [TypeChecker, TypeChecker] | [TypeChecker];
|
||||
constructor(obj: {[key: string]: resolveable} | [resolveable] | [resolveable, resolveable]) {
|
||||
super();
|
||||
if (obj instanceof Array) {
|
||||
this.obj = obj.map((_) => TypeChecker.resolve(_)) as
|
||||
| [TypeChecker, TypeChecker]
|
||||
| [TypeChecker];
|
||||
} else {
|
||||
this.obj = Object.fromEntries(
|
||||
Object.entries(obj).map(([key, value]) => [key, TypeChecker.resolve(value)]),
|
||||
);
|
||||
}
|
||||
}
|
||||
check(obj: unknown) {
|
||||
if (!(obj instanceof Object)) throw new Error(`${obj} is not of type object`);
|
||||
if (this.obj instanceof Array) {
|
||||
const valueCheck = this.obj[this.obj.length - 1];
|
||||
const keyCheck = this.obj.at(-2);
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
keyCheck?.check(key);
|
||||
valueCheck.check(value);
|
||||
}
|
||||
} else {
|
||||
const thiskeys = Object.keys(this.obj);
|
||||
for (const key of thiskeys) {
|
||||
this.obj[key].check((obj as Record<string, unknown>)[key]);
|
||||
}
|
||||
const objKeys = Object.keys(obj);
|
||||
const extra = new Set(objKeys).difference(new Set(thiskeys));
|
||||
if (extra.size) {
|
||||
throw new Error(`Object has extra keys ${[...extra]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import {resolveable, TypeChecker} from "../basetype";
|
||||
|
||||
export class Optional extends TypeChecker {
|
||||
checker: TypeChecker;
|
||||
constructor(check: resolveable) {
|
||||
super();
|
||||
this.checker = TypeChecker.resolve(check);
|
||||
}
|
||||
check(obj: unknown) {
|
||||
if (obj === undefined) return;
|
||||
this.checker.check(obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import {resolveable, TypeChecker} from "../basetype";
|
||||
|
||||
export class OrChecker extends TypeChecker {
|
||||
arr: TypeChecker[];
|
||||
constructor(arr: resolveable[]) {
|
||||
super();
|
||||
this.arr = arr.map((_) => TypeChecker.resolve(_));
|
||||
}
|
||||
check(obj: unknown) {
|
||||
let err = new Error("or statement is missing things to or");
|
||||
for (const elm of this.arr) {
|
||||
try {
|
||||
elm.check(obj);
|
||||
return;
|
||||
} catch (e) {
|
||||
err = e as Error;
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import {TypeChecker} from "../basetype";
|
||||
|
||||
export class StringChecker extends TypeChecker {
|
||||
str?: string | RegExp;
|
||||
constructor(str?: string | RegExp) {
|
||||
super();
|
||||
this.str = str;
|
||||
}
|
||||
check(obj: unknown): void {
|
||||
if (this.str === undefined) {
|
||||
if (typeof obj !== "string") throw new Error(`${obj} is not a string`);
|
||||
} else if (this.str instanceof RegExp) {
|
||||
if (typeof obj !== "string") throw new Error(`${obj} is not a string`);
|
||||
if (!obj.match(this.str)) throw new Error(`${obj} does not match ${this.str}`);
|
||||
} else if (this.str !== obj) throw new Error(`${obj} does not match ${this.str}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import {TypeChecker} from "./basetype";
|
||||
import {basicEvent} from "./types/basicEvent";
|
||||
import {heartbeat} from "./types/events/hearbeat";
|
||||
import {ready} from "./types/events/ready";
|
||||
import {initHeart} from "./types/events/setInterval";
|
||||
type basicgw = {
|
||||
op: number;
|
||||
d?: unknown;
|
||||
s?: number;
|
||||
t?: string;
|
||||
};
|
||||
export class Check {
|
||||
static apiMatch = [] as [RegExp, TypeChecker][];
|
||||
static checkAPI(path: string, obj: unknown) {
|
||||
for (const [match, check] of this.apiMatch) {
|
||||
if (path.match(match)) {
|
||||
check.check(obj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.warn(`could not check API path: ${path}`);
|
||||
}
|
||||
static checkDispatch(dis: basicgw) {
|
||||
switch (dis.t) {
|
||||
case "READY":
|
||||
return ready.check(dis);
|
||||
default:
|
||||
console.warn(`could not check gateway dispatch: ${dis.t}`);
|
||||
}
|
||||
}
|
||||
static checkEvent(obj: unknown) {
|
||||
basicEvent.check(obj);
|
||||
const o = obj as basicgw;
|
||||
switch (o.op) {
|
||||
case 0:
|
||||
return this.checkDispatch(o);
|
||||
case 10:
|
||||
return initHeart.check(o);
|
||||
case 11:
|
||||
return heartbeat.check(o);
|
||||
default:
|
||||
console.warn(`could not check gateway: ${o.op}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import {Any} from "../checkers/any";
|
||||
import {Nullish} from "../checkers/nullish";
|
||||
import {ObjectChecker} from "../checkers/object";
|
||||
import {Optional} from "../checkers/optional";
|
||||
|
||||
export const basicEvent = new ObjectChecker({
|
||||
op: Number,
|
||||
d: new Nullish(new Optional(new Any())),
|
||||
s: new Nullish(new Optional(Number)),
|
||||
t: new Nullish(new Optional(String)),
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
import {Nullish} from "../../checkers/nullish";
|
||||
import {ObjectChecker} from "../../checkers/object";
|
||||
import {Optional} from "../../checkers/optional";
|
||||
|
||||
export const heartbeat = new ObjectChecker({
|
||||
op: Number,
|
||||
d: {},
|
||||
s: new Nullish(new Optional(Number)),
|
||||
t: new Nullish(new Optional(String)),
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import {ArrayChecker} from "../../checkers/array";
|
||||
import {Nullish} from "../../checkers/nullish";
|
||||
import {ObjectChecker} from "../../checkers/object";
|
||||
import {Optional} from "../../checkers/optional";
|
||||
import {fulluser} from "../objects/user";
|
||||
|
||||
export const ready = new ObjectChecker({
|
||||
op: Number,
|
||||
d: {
|
||||
_trace: new Optional(new ArrayChecker(String)),
|
||||
v: Number,
|
||||
user: fulluser,
|
||||
//TODO user_settings
|
||||
user_settings_proto: String,
|
||||
notification_settings: {
|
||||
flags: Number,
|
||||
},
|
||||
user_guild_settings: {
|
||||
entries: new ArrayChecker({
|
||||
channel_overrides: new ArrayChecker({
|
||||
message_notifications: Number,
|
||||
muted: Boolean,
|
||||
mute_config: new Nullish({
|
||||
selected_time_window: Number,
|
||||
end_time: Number,
|
||||
}),
|
||||
channel_id: String,
|
||||
}),
|
||||
message_notifications: Number,
|
||||
flags: Number,
|
||||
hide_muted_channels: Boolean,
|
||||
mobile_push: Boolean,
|
||||
mute_config: new Nullish({
|
||||
selected_time_window: Number,
|
||||
end_time: Number,
|
||||
}),
|
||||
mute_scheduled_events: Boolean,
|
||||
muted: Boolean,
|
||||
notify_highlights: Number,
|
||||
suppress_everyone: Boolean,
|
||||
suppress_roles: Boolean,
|
||||
version: Number,
|
||||
guild_id: String,
|
||||
}),
|
||||
partial: Boolean,
|
||||
version: Number,
|
||||
},
|
||||
},
|
||||
s: new Nullish(new Optional(Number)),
|
||||
t: new Nullish(new Optional(String)),
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import {Nullish} from "../../checkers/nullish";
|
||||
import {ObjectChecker} from "../../checkers/object";
|
||||
import {Optional} from "../../checkers/optional";
|
||||
|
||||
export const initHeart = new ObjectChecker({
|
||||
op: Number,
|
||||
d: {
|
||||
heartbeat_interval: Number,
|
||||
},
|
||||
s: new Nullish(new Optional(Number)),
|
||||
t: new Nullish(new Optional(String)),
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
import {Nullish} from "../../checkers/nullish";
|
||||
import {ObjectChecker} from "../../checkers/object";
|
||||
import {Optional} from "../../checkers/optional";
|
||||
import {snowflake} from "../snowflake";
|
||||
export const fulluser = new ObjectChecker({
|
||||
id: snowflake,
|
||||
username: String,
|
||||
discriminator: /\d\d\d\d/,
|
||||
global_name: new Nullish(new Optional(String)),
|
||||
avatar: new Nullish(String),
|
||||
avatar_decoration_data: new Nullish(
|
||||
new Optional({
|
||||
asset: String,
|
||||
sku_id: String,
|
||||
}),
|
||||
),
|
||||
//TODO collectibles, display_name_styles, primary_guild, linked_users, premium_state
|
||||
bot: new Optional(Boolean),
|
||||
system: new Optional(Boolean),
|
||||
mfa_enabled: Boolean,
|
||||
nsfw_allowed: new Nullish(new Optional(Boolean)),
|
||||
age_verification_status: new Optional(Number),
|
||||
pronouns: new Optional(String),
|
||||
bio: String,
|
||||
banner: new Optional(new Nullish(String)),
|
||||
accent_color: new Optional(new Nullish(Number)),
|
||||
locale: new Optional(String),
|
||||
verified: new Optional(Boolean),
|
||||
email: new Nullish(String),
|
||||
phone: new Optional(new Nullish(String)),
|
||||
premium: Boolean,
|
||||
premium_type: Number,
|
||||
personal_connection_id: new Optional(snowflake),
|
||||
flags: new Optional(Number),
|
||||
public_flags: Number,
|
||||
purchased_flags: new Optional(Number),
|
||||
premium_usage_flags: new Optional(Number),
|
||||
desktop: new Optional(Number),
|
||||
mobile: new Optional(Number),
|
||||
has_bounced_email: new Optional(Boolean),
|
||||
authenticator_types: new Optional(new Array(Number)),
|
||||
analytics_token: new Optional(String),
|
||||
});
|
||||
export const partialuser = new ObjectChecker({
|
||||
id: snowflake,
|
||||
username: String,
|
||||
discriminator: /\d\d\d\d/,
|
||||
global_name: new Nullish(new Optional(String)),
|
||||
avatar: new Nullish(String),
|
||||
avatar_decoration_data: new Nullish(
|
||||
new Optional({
|
||||
asset: String,
|
||||
sku_id: String,
|
||||
}),
|
||||
),
|
||||
//TODO collectibles, display_name_styles, primary_guild, linked_users
|
||||
bot: new Optional(Boolean),
|
||||
system: new Optional(Boolean),
|
||||
mfa_enabled: Boolean,
|
||||
nsfw_allowed: new Nullish(new Optional(Boolean)),
|
||||
age_verification_status: new Optional(Number),
|
||||
pronouns: new Optional(String),
|
||||
bio: String,
|
||||
banner: new Optional(new Nullish(String)),
|
||||
accent_color: new Optional(new Nullish(Number)),
|
||||
public_flags: new Optional(Number),
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
import {StringChecker} from "../checkers/string";
|
||||
|
||||
export const snowflake = new StringChecker(/\d*/);
|
||||
@@ -224,7 +224,7 @@
|
||||
"gatewayComp": "Disable Gateway compression:",
|
||||
"reportSystem":"Enable experimental reporting system:",
|
||||
"logGateway": "Log received gateway events (log level info):",
|
||||
"name": "Developer Settings",
|
||||
"name": "Developer settings",
|
||||
"traces": "Expose traces:"
|
||||
},
|
||||
"deviceManage": {
|
||||
@@ -554,7 +554,7 @@
|
||||
"themesAndSounds": "Themes & Sounds",
|
||||
"tokenDisplay": "Token: $1",
|
||||
"trace": "Traces",
|
||||
"trusted": "Trusted Domains",
|
||||
"trusted": "Trusted domains",
|
||||
"trustedDesc": "When you click on links sending you to these domain, you will ***not*** be prompted for permission to open like other links, only give this to domains you trust, such as 'https://fermi.chat'",
|
||||
"updateSettings": "Update settings",
|
||||
"updatesYay": "Updates found!",
|
||||
|
||||
Reference in New Issue
Block a user